yanlongqi 2bcf6bdacc 实现完整的用户统计功能和认证系统
**统计功能**:
- 新增UserAnswerRecord模型记录用户答题历史
- 实现GetStatistics接口,统计题库总数、已答题数、正确率
- 在提交答案时自动记录答题历史
- 前端连接真实统计接口,显示实时数据

**认证系统优化**:
- 新增Auth中间件,实现基于Token的身份验证
- 登录和注册时自动生成并保存Token到数据库
- 所有需要登录的接口都通过Auth中间件保护
- 统一处理未授权请求,返回401状态码

**错题练习功能**:
- 新增GetRandomWrongQuestion接口,随机获取错题
- 支持错题练习模式(/question?mode=wrong)
- 优化错题本页面UI,移除已掌握功能
- 新增"开始错题练习"按钮,直接进入练习模式

**数据库迁移**:
- 新增user_answer_records表,记录用户答题历史
- User表新增token字段,存储用户登录凭证

**技术改进**:
- 统一错误处理,区分401未授权和404未找到
- 优化答题流程,记录历史和错题分离处理
- 移除异步记录错题,改为同步处理保证数据一致性

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:26:27 +08:00

67 lines
1.9 KiB
Go

package models
import (
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// User 用户结构
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"uniqueIndex;not null;size:50" json:"username"`
Password string `gorm:"not null;size:255" json:"-"` // json:"-" 表示在JSON响应中不返回密码
Token string `gorm:"size:255;index" json:"-"` // 用户登录token
Avatar string `gorm:"size:255" json:"avatar"`
Nickname string `gorm:"size:50" json:"nickname"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// HashPassword 加密密码
func (u *User) HashPassword(password string) error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
u.Password = string(hashedPassword)
return nil
}
// CheckPassword 验证密码
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
}
// UserData 存储所有用户的结构(用于兼容旧的文件存储方式)
type UserData map[string]User
// LoginRequest 登录请求
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// LoginResponse 登录响应
type LoginResponse struct {
Token string `json:"token"`
User UserInfoResponse `json:"user"`
}
// UserInfoResponse 用户信息响应(不包含密码)
type UserInfoResponse struct {
Username string `json:"username"`
Avatar string `json:"avatar"`
Nickname string `json:"nickname"`
}
// RegisterRequest 注册请求
type RegisterRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required,min=6"`
Nickname string `json:"nickname"`
}