**统计功能**: - 新增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>
58 lines
1.1 KiB
Go
58 lines
1.1 KiB
Go
package middleware
|
|
|
|
import (
|
|
"ankao/internal/database"
|
|
"ankao/internal/models"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Auth 认证中间件
|
|
func Auth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// 从请求头获取token
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"success": false,
|
|
"message": "未登录",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 解析Bearer token
|
|
parts := strings.SplitN(authHeader, " ", 2)
|
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"success": false,
|
|
"message": "token格式错误",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
token := parts[1]
|
|
|
|
// 从数据库查找token对应的用户
|
|
db := database.GetDB()
|
|
var user models.User
|
|
if err := db.Where("token = ?", token).First(&user).Error; err != nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
"success": false,
|
|
"message": "token无效或已过期",
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 将用户ID设置到上下文
|
|
c.Set("user_id", user.ID)
|
|
c.Set("username", user.Username)
|
|
|
|
c.Next()
|
|
}
|
|
}
|