2 changed files with 128 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
[submodule ".claude/skills/zero-skills"] |
||||
|
path = .claude/skills/zero-skills |
||||
|
url = https://github.com/zeromicro/zero-skills.git |
||||
@ -0,0 +1,125 @@ |
|||||
|
# 后端热重载 + 登录方式改造 设计文档 |
||||
|
|
||||
|
**日期**: 2026-02-14 |
||||
|
**状态**: 已批准 |
||||
|
|
||||
|
## 概述 |
||||
|
|
||||
|
两项改造: |
||||
|
1. 后端引入 Air 热重载,保存文件自动重启 |
||||
|
2. 登录从邮箱改为手机号/用户名,注册移除邮箱必填,JWT Claims 移除 email |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 设计一:Air 热重载 |
||||
|
|
||||
|
### 方案 |
||||
|
|
||||
|
使用 [Air](https://github.com/air-verse/air) 监听 `.go` 和 `.yaml` 文件变化,自动编译重启。 |
||||
|
|
||||
|
### 新增文件 |
||||
|
|
||||
|
`backend/.air.toml`: |
||||
|
- 监听 `.go`, `.yaml` 文件 |
||||
|
- 排除 `tmp/`, `vendor/`, `tests/` |
||||
|
- 编译到 `tmp/base.exe` |
||||
|
- 延迟 1000ms 防抖 |
||||
|
|
||||
|
### 启动方式 |
||||
|
|
||||
|
```bash |
||||
|
# 安装(一次性) |
||||
|
go install github.com/air-verse/air@latest |
||||
|
|
||||
|
# 开发启动 |
||||
|
cd backend && air |
||||
|
``` |
||||
|
|
||||
|
### 影响范围 |
||||
|
|
||||
|
- 新增 `backend/.air.toml` |
||||
|
- `backend/.gitignore` 加 `tmp/` |
||||
|
- 零侵入现有代码 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 设计二:登录方式改造 |
||||
|
|
||||
|
### API 变更 |
||||
|
|
||||
|
**LoginRequest**: |
||||
|
``` |
||||
|
Account string `json:"account" validate:"required"` // 手机号或用户名 |
||||
|
Password string `json:"password" validate:"required,min=6,max=32"` |
||||
|
``` |
||||
|
|
||||
|
**RegisterRequest**: |
||||
|
``` |
||||
|
Username string `json:"username" validate:"required,min=3,max=32"` |
||||
|
Password string `json:"password" validate:"required,min=6,max=32"` |
||||
|
Phone string `json:"phone" validate:"required"` // 必填 |
||||
|
Email string `json:"email,optional"` // 可选 |
||||
|
``` |
||||
|
|
||||
|
### 后端 Login 逻辑 |
||||
|
|
||||
|
单一输入框,后端正则自动识别: |
||||
|
- `^\d{11}$` → 手机号 → `FindOneByPhone` |
||||
|
- 否则 → 用户名 → `FindOneByUsername` |
||||
|
|
||||
|
### Model 层 |
||||
|
|
||||
|
新增: |
||||
|
- `FindOneByPhone(ctx, db, phone) (*User, error)` |
||||
|
- `FindOneByUsername(ctx, db, username) (*User, error)` |
||||
|
|
||||
|
### JWT Claims 变更 |
||||
|
|
||||
|
移除 `Email` 字段: |
||||
|
```go |
||||
|
type Claims struct { |
||||
|
UserID int64 `json:"userId"` |
||||
|
Username string `json:"username"` |
||||
|
Role string `json:"role"` |
||||
|
jwt.RegisteredClaims |
||||
|
} |
||||
|
|
||||
|
func GenerateToken(userId int64, username, role string) (string, error) |
||||
|
``` |
||||
|
|
||||
|
影响调用点:loginlogic, registerlogic, refreshtokenlogic, ssologic, authmiddleware |
||||
|
|
||||
|
### Register 逻辑 |
||||
|
|
||||
|
- 检查 username 唯一性(FindOneByUsername) |
||||
|
- 检查 phone 唯一性(FindOneByPhone) |
||||
|
- email 可选,不做唯一性检查 |
||||
|
|
||||
|
### 种子超级管理员 |
||||
|
|
||||
|
```go |
||||
|
admin := &model.User{ |
||||
|
Username: "admin", |
||||
|
Phone: "13800000000", |
||||
|
Email: "", |
||||
|
Password: md5("admin123"), |
||||
|
Role: "super_admin", |
||||
|
Source: "system", |
||||
|
} |
||||
|
// 查找改为 FindOneByUsername(ctx, db, "admin") |
||||
|
``` |
||||
|
|
||||
|
### 前端变更 |
||||
|
|
||||
|
| 文件 | 变更 | |
||||
|
|------|------| |
||||
|
| `types/index.ts` | `LoginRequest.email` → `.account`; `RegisterRequest.email` 可选, `.phone` 必填 | |
||||
|
| `services/api.ts` | `login()` 传 `account` | |
||||
|
| `contexts/AuthContext.tsx` | `login(email, password)` → `login(account, password)`; JWT 解析移除 email | |
||||
|
| `pages/LoginPage.tsx` | 输入框: Mail 图标 → User 图标, placeholder "手机号 / 用户名", `type="text"` | |
||||
|
|
||||
|
### 不变的部分 |
||||
|
|
||||
|
- SSO 登录流程(Casdoor 自有账号体系) |
||||
|
- 用户管理 CRUD、Profile 接口 |
||||
|
- `user_entity.go` 的 email 字段保留,仅改为非必填 |
||||
Loading…
Reference in new issue