# Day 15 - 手机号登录迁移 + 账户设置功能

**日期**：2026-02-02

---

## 🔐 认证系统迁移：邮箱 → 手机号

### 背景

根据业务需求，将用户认证从邮箱登录迁移到手机号登录（11位中国手机号）。

### 变更范围

| 组件 | 变更内容 |
|------|----------|
| 数据库 Schema | `email` 字段替换为 `phone` |
| 后端 API | 注册/登录/获取用户信息接口使用 `phone` |
| 前端页面 | 登录/注册页面改为手机号输入框 |
| 管理员配置 | `ADMIN_EMAIL` 改为 `ADMIN_PHONE` |

---

## 📦 后端修改

### 1. 数据库 Schema (`schema.sql`)

**文件**: `backend/database/schema.sql`

```sql
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    phone TEXT UNIQUE NOT NULL,  -- 原 email 改为 phone
    password_hash TEXT NOT NULL,
    username TEXT,
    role TEXT DEFAULT 'pending' CHECK (role IN ('pending', 'user', 'admin')),
    is_active BOOLEAN DEFAULT FALSE,
    expires_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_users_phone ON users(phone);
```

### 2. 认证 API (`auth.py`)

**文件**: `backend/app/api/auth.py`

#### 请求模型更新

```python
class RegisterRequest(BaseModel):
    phone: str
    password: str
    username: Optional[str] = None

    @field_validator('phone')
    @classmethod
    def validate_phone(cls, v):
        if not re.match(r'^\d{11}$', v):
            raise ValueError('手机号必须是11位数字')
        return v
```

#### 新增修改密码接口

```python
class ChangePasswordRequest(BaseModel):
    old_password: str
    new_password: str

    @field_validator('new_password')
    @classmethod
    def validate_new_password(cls, v):
        if len(v) < 6:
            raise ValueError('新密码长度至少6位')
        return v

@router.post("/change-password")
async def change_password(request: ChangePasswordRequest, req: Request, response: Response):
    """修改密码，验证当前密码后更新"""
    # 1. 验证当前密码
    # 2. 更新密码 hash
    # 3. 重新生成 session token
    # 4. 返回新的 JWT Cookie
```

### 3. 配置更新

**文件**: `backend/app/core/config.py`

```python
# 管理员配置
ADMIN_PHONE: str = ""  # 原 ADMIN_EMAIL
ADMIN_PASSWORD: str = ""
```

**文件**: `backend/.env`

```bash
ADMIN_PHONE=15549380526
ADMIN_PASSWORD=lam1988324
```

### 4. 管理员初始化 (`main.py`)

**文件**: `backend/app/main.py`

```python
@app.on_event("startup")
async def init_admin():
    admin_phone = settings.ADMIN_PHONE  # 原 ADMIN_EMAIL
    # ... 使用 phone 字段创建管理员
```

### 5. 管理员 API (`admin.py`)

**文件**: `backend/app/api/admin.py`

```python
class UserListItem(BaseModel):
    id: str
    phone: str  # 原 email
    username: Optional[str]
    role: str
    is_active: bool
    expires_at: Optional[str]
    created_at: str
```

---

## 🖥️ 前端修改

### 1. 登录页面 (`login/page.tsx`)

**文件**: `frontend/src/app/login/page.tsx`

```tsx
const [phone, setPhone] = useState('');

// 验证手机号格式
if (!/^\d{11}$/.test(phone)) {
    setError('请输入正确的11位手机号');
    return;
}

<input
    type="tel"
    value={phone}
    onChange={(e) => setPhone(e.target.value.replace(/\D/g, '').slice(0, 11))}
    maxLength={11}
    placeholder="请输入11位手机号"
/>
```

### 2. 注册页面 (`register/page.tsx`)

同样使用手机号输入，增加 11 位数字验证。

### 3. Auth 工具函数 (`auth.ts`)

**文件**: `frontend/src/shared/lib/auth.ts`

```typescript
export interface User {
    id: string;
    phone: string;  // 原 email
    username: string | null;
    role: string;
    is_active: boolean;
}

export async function login(phone: string, password: string): Promise<AuthResponse> { ... }
export async function register(phone: string, password: string, username?: string): Promise<AuthResponse> { ... }
export async function changePassword(oldPassword: string, newPassword: string): Promise<AuthResponse> { ... }
```

### 4. 首页账户设置下拉菜单 (`page.tsx`)

**文件**: `frontend/src/app/page.tsx`

将原来的"退出"按钮改为账户设置下拉菜单：

```tsx
function AccountSettingsDropdown() {
  const [isOpen, setIsOpen] = useState(false);
  const [showPasswordModal, setShowPasswordModal] = useState(false);
  // ...

  return (
    <div className="relative">
      <button onClick={() => setIsOpen(!isOpen)}>
        ⚙️ 账户
      </button>

      {/* 下拉菜单 */}
      {isOpen && (
        <div className="absolute right-0 mt-2 w-40 bg-gray-800 ...">
          <button onClick={() => setShowPasswordModal(true)}>
            🔐 修改密码
          </button>
          <button onClick={handleLogout} className="text-red-300">
            🚪 退出登录
          </button>
        </div>
      )}

      {/* 修改密码弹窗 */}
      {showPasswordModal && (
        <div className="fixed inset-0 z-50 ...">
          <form onSubmit={handleChangePassword}>
            <input placeholder="当前密码" />
            <input placeholder="新密码" />
            <input placeholder="确认新密码" />
          </form>
        </div>
      )}
    </div>
  );
}
```

### 5. 管理员页面 (`admin/page.tsx`)

**文件**: `frontend/src/app/admin/page.tsx`

```tsx
interface UserListItem {
    id: string;
    phone: string;  // 原 email
    // ...
}

// 显示手机号而非邮箱
<div className="text-gray-400 text-sm">{user.phone}</div>
```

---

## 🗄️ 数据库迁移

### 迁移脚本

**文件**: `backend/database/migrate_to_phone.sql`

```sql
-- 删除旧表 (CASCADE 处理外键依赖)
DROP TABLE IF EXISTS user_sessions CASCADE;
DROP TABLE IF EXISTS social_accounts CASCADE;
DROP TABLE IF EXISTS users CASCADE;

-- 重新创建使用 phone 字段的表
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    phone TEXT UNIQUE NOT NULL,
    -- ...
);

-- 重新创建依赖表和索引
CREATE TABLE user_sessions (...);
CREATE TABLE social_accounts (...);
CREATE INDEX idx_users_phone ON users(phone);
```

### 执行方式

```bash
# 方式一：Docker 命令
docker exec -i supabase-db psql -U postgres < backend/database/migrate_to_phone.sql

# 方式二：Supabase Studio SQL Editor
# 打开 https://supabase.hbyrkj.top -> SQL Editor -> 粘贴执行
```

---

## ✅ 部署步骤

```bash
# 1. 执行数据库迁移
docker exec -i supabase-db psql -U postgres < backend/database/migrate_to_phone.sql

# 2. 重新构建前端
cd frontend && npm run build

# 3. 重启服务
pm2 restart vigent2-backend vigent2-frontend
```

---

## 📁 今日修改文件清单

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `backend/database/schema.sql` | 修改 | email → phone |
| `backend/database/migrate_to_phone.sql` | 新增 | 数据库迁移脚本 |
| `backend/app/api/auth.py` | 修改 | 手机号验证 + 修改密码 API |
| `backend/app/api/admin.py` | 修改 | UserListItem.email → phone |
| `backend/app/core/config.py` | 修改 | ADMIN_EMAIL → ADMIN_PHONE |
| `backend/app/main.py` | 修改 | 管理员初始化使用 phone |
| `backend/.env` | 修改 | ADMIN_PHONE=15549380526 |
| `frontend/src/app/login/page.tsx` | 修改 | 手机号登录 + 11位验证 |
| `frontend/src/app/register/page.tsx` | 修改 | 手机号注册 + 11位验证 |
| `frontend/src/shared/lib/auth.ts` | 修改 | phone 参数 + changePassword 函数 |
| `frontend/src/app/page.tsx` | 修改 | AccountSettingsDropdown 组件 |
| `frontend/src/app/admin/page.tsx` | 修改 | 用户列表显示手机号 |
| `frontend/src/contexts/AuthContext.tsx` | 修改 | 存储完整用户信息含 expires_at |

---

## 🆕 后续完善 (Day 15 下午)

### 账户有效期显示

在账户下拉菜单中显示用户的有效期：

| 显示情况 | 格式 |
|----------|------|
| 有设置 expires_at | `2026-03-15` |
| NULL | `永久有效` |

**相关修改**：
- `backend/app/api/auth.py`: UserResponse 新增 `expires_at` 字段
- `frontend/src/contexts/AuthContext.tsx`: 存储完整用户对象
- `frontend/src/app/page.tsx`: 格式化并显示有效期

### 点击外部关闭下拉菜单

使用 `useRef` + `useEffect` 监听全局点击事件，点击菜单外部自动关闭。

### 修改密码后强制重新登录

密码修改成功后：
1. 显示"密码修改成功，正在跳转登录页..."
2. 1.5秒后调用登出 API
3. 跳转到登录页面

---

## 🔗 相关文档

- [TASK_COMPLETE.md](../TASK_COMPLETE.md) - 任务总览
- [Day14.md](./Day14.md) - 模型升级 + AI 标题标签
- [AUTH_DEPLOY.md](../AUTH_DEPLOY.md) - 认证系统部署指南

---

## 🤖 模型与功能增强 (Day 15 晚)

### 1. GLM-4.7-Flash 升级

**文件**: `backend/app/services/glm_service.py`

将文案洗稿模型从 `glm-4-flash` 升级为 `glm-4.7-flash`：

```python
response = client.chat.completions.create(
    model="glm-4.7-flash",  # Upgrade from glm-4-flash
    messages=[...],
    # ...
)
```

**改进**:
- 响应速度提升
- 洗稿文案的流畅度和逻辑性增强

### 2. 独立文案提取助手

实现了独立的文案提取工具，支持从视频/音频文件或 URL 提取文字。

#### 后端实现 (`backend/app/api/tools.py`)

- **多源支持**: 文件上传 (MP4/MP3/WAV) 或 URL 下载
- **智能下载**:
  - `yt-dlp`: 通用下载 (Douyin/TikTok/Bilibili)
  - `Playwright`: 智能回退机制 (Bilibili Dashboard API, Douyin Cookie Bypass)
- **URL 自动清洗**: 正则提取分享文本中的 HTTP 链接
- **流程**: 下载 -> FFmpeg 转 WAV (16k) -> Whisper 识别 -> GLM-4.7 洗稿

#### 前端实现 (`frontend/src/components/ScriptExtractionModal.tsx`)

- **独立模态框**: 通过顶部导航栏打开
- **功能**:
  - 链接粘贴 / 文件拖拽
  - 实时进度显示 (下载 -> 识别 -> 洗稿)
  - **一键填入**: 将提取结果直接填充到主输入框
  - **自动识别**: 自动区分平台与链接
- **交互优化**:
  - 防止误触背景关闭
  - 复制功能兼容 HTTP 环境 (Fallback textArea)

### 3. 上传视频预览功能

在素材列表 (`frontend/src/app/page.tsx`) 中为上传的视频添加预览功能：
- 点击缩略图弹出视频播放模态框
- 支持下载与发布快捷跳转

---

## 📝 任务清单更新

- [x] 认证系统迁移 (手机号)
- [x] 账户管理 (密码修改/有效期)
- [x] GLM-4.7 模型升级
- [x] 独立文案提取助手 (B站/抖音支持)
- [x] 视频预览功能
