fix: 时间精度精确到秒 + 同步全部文档到最新状态
时间字段修正: - init.sql: 所有 TIMESTAMP 改为 TIMESTAMP(0),精确到秒 - GORM 模型: 所有时间字段添加 type:timestamp(0) tag - 已有数据库表通过 ALTER TABLE 修正列类型 文档同步(架构设计 + 实施步骤): - system-architecture.md: - auth 模块描述更新为"有状态 JWT Token 管理(Redis 存储)" - 开发环境端口 8080 → 8085 - phase1-foundation-and-auth.md: - Go 版本 1.22+ → 1.23+ - 常量命名更新为驼峰风格(UserStatusActive / RoleUser 等) - Task 4 完整重写:增加 token_store.go、TokenValidator 接口、有状态 JWT - Task 5 端口号 8080 → 8085,测试命令增加登出失效验证 - 依赖版本选择原则说明 Made-with: Cursor
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
**架构:** Go 单体模块化服务 + uniapp 前台 + Vue3 管理端 + PostgreSQL + Redis,Docker Compose 编排。
|
||||
|
||||
**技术栈:** Go 1.22+ (Gin + GORM + Wire + zap) / Vue 3.4+ / uniapp 3.0 / Element Plus / PostgreSQL 17 / Redis 7 / Docker Compose
|
||||
**技术栈:** Go 1.23+ (Gin + GORM + Wire + zap) / Vue 3.4+ / uniapp 3.0 / Element Plus / PostgreSQL 17 / Redis 7 / Docker Compose
|
||||
|
||||
**设计文档:** `docs/plans/2026-02-27-echochat-system-design.md`
|
||||
|
||||
@@ -319,7 +319,7 @@ Run: `cd backend/go-service && go mod tidy`
|
||||
**Step 10: 运行验证**
|
||||
|
||||
Run: `cd backend/go-service && go run cmd/server/main.go`
|
||||
Expected: 服务启动在 :8080,访问 /health 返回 200
|
||||
Expected: 服务启动在 :8085,访问 /health 返回 200
|
||||
|
||||
**Step 11: Commit**
|
||||
|
||||
@@ -344,18 +344,18 @@ git commit -m "feat(backend): Go 服务骨架(配置、日志、数据库、
|
||||
**Step 1: 创建用户状态和角色常量**
|
||||
|
||||
```go
|
||||
// app/constants/user_status.go
|
||||
// app/constants/user_status.go — 驼峰命名,符合 Go 社区规范
|
||||
const (
|
||||
USER_STATUS_ACTIVE = 1 // 正常
|
||||
USER_STATUS_DISABLED = 2 // 禁用
|
||||
USER_STATUS_DELETED = 3 // 注销
|
||||
UserStatusActive = 1 // 正常
|
||||
UserStatusDisabled = 2 // 禁用
|
||||
UserStatusDeleted = 3 // 注销
|
||||
)
|
||||
|
||||
// app/constants/role_code.go
|
||||
const (
|
||||
ROLE_USER = "user"
|
||||
ROLE_ADMIN = "admin"
|
||||
ROLE_SUPER_ADMIN = "super_admin"
|
||||
RoleUser = "user"
|
||||
RoleAdmin = "admin"
|
||||
RoleSuperAdmin = "super_admin"
|
||||
)
|
||||
```
|
||||
|
||||
@@ -419,6 +419,7 @@ git commit -m "feat(auth): 用户/角色模型与数据访问层"
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/go-service/app/auth/service/auth_service.go`
|
||||
- Create: `backend/go-service/app/auth/service/token_store.go`
|
||||
- Create: `backend/go-service/app/dto/auth_dto.go`
|
||||
- Create: `backend/go-service/pkg/utils/jwt.go`
|
||||
- Create: `backend/go-service/pkg/utils/password.go`
|
||||
@@ -427,85 +428,87 @@ git commit -m "feat(auth): 用户/角色模型与数据访问层"
|
||||
**Step 1: 创建密码工具**
|
||||
|
||||
创建 `pkg/utils/password.go`:
|
||||
- `HashPassword(password)` — bcrypt 加密
|
||||
- `HashPassword(password)` — bcrypt 加密(`golang.org/x/crypto/bcrypt`,复用 go.mod 已有的兼容版本)
|
||||
- `CheckPassword(password, hash)` — 密码校验
|
||||
|
||||
**Step 2: 创建 JWT 工具**
|
||||
|
||||
创建 `pkg/utils/jwt.go`:
|
||||
- `GenerateToken(userID, username)` — 生成 Access Token
|
||||
- `GenerateRefreshToken(userID)` — 生成 Refresh Token
|
||||
- `ParseToken(tokenStr)` — 解析验证 Token
|
||||
创建 `pkg/utils/jwt.go`(使用 `github.com/golang-jwt/jwt/v5@v5.2.1`,兼容 Go 1.23):
|
||||
- `GenerateToken(cfg, userID, username, roles)` — 生成 Access Token
|
||||
- `GenerateRefreshToken(cfg, userID)` — 生成 Refresh Token
|
||||
- `ParseToken(cfg, tokenStr)` — 解析验证 Token
|
||||
- Claims 结构体包含 UserID, Username, Roles
|
||||
|
||||
> **依赖版本选择原则:** 优先选择兼容当前 Go 版本的包版本,不盲目拉取最新版,避免触发工具链升级。
|
||||
|
||||
**Step 3: 创建认证 DTO**
|
||||
|
||||
创建 `app/dto/auth_dto.go`:
|
||||
|
||||
```go
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=50"`
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6,max=50"`
|
||||
Nickname string `json:"nickname" binding:"max=50"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Account string `json:"account" binding:"required"` // 邮箱或用户名
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
User UserInfo `json:"user"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
Roles []string `json:"roles"`
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
Gender int `json:"gender"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Roles []string `json:"roles"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: 创建认证服务**
|
||||
**Step 4: 创建 Token Redis 存储管理**
|
||||
|
||||
创建 `app/auth/service/token_store.go`:
|
||||
- 遵循设计方案中的 Redis Key 规范:`echo:auth:token:{user_id}` / `echo:auth:refresh:{user_id}`
|
||||
- `SaveTokens(ctx, userID, accessToken, refreshToken)` — 存入 Redis(覆盖旧 Token,实现单设备登录)
|
||||
- `ValidateAccessToken(ctx, userID, token)` — 校验 Access Token 与 Redis 一致
|
||||
- `ValidateRefreshToken(ctx, userID, token)` — 校验 Refresh Token 与 Redis 一致
|
||||
- `RemoveTokens(ctx, userID)` — 登出时删除 Redis 中的 Token
|
||||
|
||||
**Step 5: 创建认证服务**
|
||||
|
||||
创建 `app/auth/service/auth_service.go`:
|
||||
- `Register(ctx, req)` — 注册逻辑(检查重复、加密密码、创建用户、分配默认角色、生成 Token)
|
||||
- `Login(ctx, req)` — 登录逻辑(查找用户、校验密码、检查状态、更新登录信息、生成 Token)
|
||||
- `RefreshToken(ctx, refreshToken)` — 刷新 Token
|
||||
- `Register(ctx, req)` — 注册逻辑(检查重复、加密密码、创建用户、分配默认角色、生成 Token + 存 Redis)
|
||||
- `Login(ctx, req, clientIP)` — 登录逻辑(查找用户、校验密码、检查状态、更新登录信息、生成 Token + 存 Redis)
|
||||
- `AdminLogin(ctx, req, clientIP)` — 管理员登录(额外检查 admin/super_admin 角色)
|
||||
- `RefreshToken(ctx, refreshToken)` — 刷新 Token(校验 Redis + 生成新 Token + 覆盖 Redis)
|
||||
- `Logout(ctx, userID)` — 登出(从 Redis 删除 Token)
|
||||
- `ValidateAccessToken(ctx, userID, token)` — 供中间件调用,校验 Redis
|
||||
- `GetProfile(ctx, userID)` — 获取用户信息
|
||||
- `UpdateProfile(ctx, userID, req)` — 更新用户信息
|
||||
- `ChangePassword(ctx, userID, req)` — 修改密码
|
||||
|
||||
**Step 5: 创建认证中间件**
|
||||
**Step 6: 创建认证中间件**
|
||||
|
||||
创建 `pkg/middleware/auth.go`:
|
||||
- `JWTAuth()` — JWT 验证中间件,从 Authorization Header 提取 Token
|
||||
- `TokenValidator` 接口 — 定义 Token 校验契约,由 AuthService 实现(解耦 pkg 与 app 层依赖)
|
||||
- `JWTAuth(cfg, validator)` — 有状态 JWT 验证中间件(解析 Token + 校验 Redis 有效性)
|
||||
- `RequireRole(roles ...string)` — 角色检查中间件
|
||||
- 将 UserID 和 Roles 注入 Gin Context
|
||||
- 将 UserID、Username 和 Roles 注入 Gin Context
|
||||
|
||||
**Step 6: 更新 Wire Provider**
|
||||
**Step 7: 更新 Wire Provider**
|
||||
|
||||
更新 `app/auth/provider.go`,添加 Service 层 Provider:
|
||||
更新 `app/auth/provider.go`:
|
||||
|
||||
```go
|
||||
var AuthSet = wire.NewSet(
|
||||
dao.NewUserDAO,
|
||||
dao.NewRoleDAO,
|
||||
service.NewTokenStore,
|
||||
service.NewAuthService,
|
||||
controller.NewAuthController,
|
||||
controller.NewAdminAuthController,
|
||||
)
|
||||
```
|
||||
|
||||
**Step 7: Commit**
|
||||
**Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add backend/go-service/
|
||||
git commit -m "feat(auth): 认证服务层(注册、登录、JWT、密码加密、中间件)"
|
||||
git commit -m "feat(auth): 认证服务层(注册、登录、有状态JWT、密码加密、中间件)"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -585,21 +588,27 @@ func RegisterRoutes(r *gin.Engine, ctrl *AuthController, adminCtrl *AdminAuthCon
|
||||
**Step 5: 运行验证**
|
||||
|
||||
Run: `cd backend/go-service && go run cmd/server/main.go`
|
||||
Expected: 服务启动,可以通过 curl 测试注册和登录接口
|
||||
Expected: 服务启动在 :8085,可以通过 curl 测试注册和登录接口
|
||||
|
||||
```bash
|
||||
# 测试注册
|
||||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||||
curl -X POST http://localhost:8085/api/v1/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser","email":"test@example.com","password":"123456","nickname":"测试用户"}'
|
||||
|
||||
# 测试登录
|
||||
curl -X POST http://localhost:8080/api/v1/auth/login \
|
||||
curl -X POST http://localhost:8085/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"account":"testuser","password":"123456"}'
|
||||
|
||||
# 测试登出后 Token 立即失效
|
||||
TOKEN=$(curl -s ... | jq -r '.data.token')
|
||||
curl -X POST http://localhost:8085/api/v1/auth/logout -H "Authorization: Bearer $TOKEN"
|
||||
curl http://localhost:8085/api/v1/auth/profile -H "Authorization: Bearer $TOKEN"
|
||||
# 预期返回 401 "认证已失效,请重新登录"
|
||||
```
|
||||
|
||||
Expected: 注册返回 Token,登录返回 Token + 用户信息
|
||||
Expected: 注册返回 Token(同时存入 Redis),登录返回 Token + 用户信息,登出后 Token 立即失效
|
||||
|
||||
**Step 6: Commit**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user