1. 错题本系统重构: - 新增错题服务层 (wrong_question_service.go) - 实现智能推荐算法(基于掌握度和错误次数) - 添加掌握度追踪机制(连续答对6次标记为已掌握) - 支持错题筛选和排序功能 - 新增错题统计趋势分析 2. UI优化: - 美化错题本界面,采用毛玻璃卡片设计 - 添加四宫格统计卡片(错题总数、已掌握、未掌握、掌握率) - 优化筛选和操作按钮布局 - 使用条状进度条显示掌握度 - 改进响应式设计,优化移动端体验 3. 功能完善: - 修复判断题答案显示问题 - 当掌握率100%时禁用"开始练习"按钮 - 删除测试文件和 nul 文件 - 更新文档 (CLAUDE.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
21 KiB
CLAUDE.md
本文件为 Claude Code (claude.ai/code) 在此代码仓库中工作时提供指导。
重要开发规范
文档同步更新规则
关键规则: 当实现新功能、修改现有功能或更改项目配置时,必须同步更新相关文档。
-
README.md 更新时机:
- 添加新的核心功能或特性
- 修改项目安装、配置或运行方式
- 更改技术栈或主要依赖
- 添加新的 API 端点或修改现有端点
- 更新项目架构或目录结构
-
CLAUDE.md 更新时机:
- 添加新的开发规范或最佳实践
- 修改项目配置(如代理、构建工具等)
- 更改目录结构或文件组织方式
- 引入新的工具或技术
- 更新常用命令或开发流程
-
更新原则:
- 代码修改和文档更新应在同一次提交中完成
- 文档要清晰、准确,反映当前代码状态
- 使用 markdown 链接引用具体文件位置
- 及时移除过时的说明和示例
项目概述
AnKao 是一个使用 Gin 框架构建的 Go Web 应用程序。该项目遵循整洁架构模式,具有清晰的关注点分离,并设计为支持数据库集成。
架构
请求流程
- HTTP 请求到达运行在 8080 端口的 Gin 服务器
- 请求通过 Gin 的默认中间件(Logger、Recovery)和自定义中间件
- Gin 路由器匹配路由并将请求定向到相应的处理器
- 处理器(位于
internal/handlers/)使用*gin.Context处理请求并返回 JSON 响应
中间件模式
Gin 中的中间件使用 gin.HandlerFunc 模式:
func MiddlewareName() gin.HandlerFunc {
return func(c *gin.Context) {
// 预处理
c.Next()
// 后处理
}
}
中间件在 main.go:15 中使用 r.Use() 注册。
模块结构
- main.go - 应用程序入口点,服务器配置和路由设置
- internal/handlers/ - HTTP 请求处理器,全部使用
*gin.Context并返回 JSON 响应- user.go - 用户登录注册相关
- practice_handler.go - 练习题相关
- handlers.go - 通用处理器(健康检查等)
- internal/middleware/ - Gin 中间件链(当前:自定义日志记录器、CORS)
- internal/models/ - 数据模型
- user.go - 用户模型
- practice_question.go - 练习题模型
- internal/services/ - 业务服务
- ai_grading.go - AI评分服务
- internal/database/ - 数据库连接和初始化
- pkg/config/ - 配置管理(数据库配置、AI配置等)
- scripts/ - 工具脚本
- import_questions.go - 题目数据导入脚本
常用命令
开发
# 运行服务器
go run main.go
# 安装/更新依赖
go mod tidy
# 格式化代码
go fmt ./...
# 检查代码常见问题
go vet ./...
# 导入练习题数据(首次运行需要)
go run scripts/import_questions.go
构建
# 构建二进制文件到 bin/server
go build -o bin/server.exe main.go
# 运行构建的二进制文件 (Windows)
.\bin\server.exe
# 运行构建的二进制文件 (Unix)
./bin/server
测试
# 运行所有测试
go test ./...
# 运行带覆盖率的测试
go test -cover ./...
# 运行特定包的测试
go test ./internal/handlers/
# 运行详细输出的测试
go test -v ./...
关键实现细节
- 框架: 使用 Gin v1.11.0
- ORM: 使用 GORM v1.31.1
- 数据库: PostgreSQL (配置在 pkg/config/config.go)
- AI服务: 使用 go-openai SDK v1.41.2,配置在 pkg/config/config.go
- 服务器端口: :8080 (在 main.go:42 中配置)
- 处理器签名: 所有处理器使用
func(c *gin.Context)模式 - JSON 响应: 使用
c.JSON()方法配合gin.H{}或结构体 - 导入路径: 使用模块名
ankao(在 go.mod 中定义) - 路由注册: 路由在 main.go 中使用
r.GET()、r.POST()等注册 - 中间件: 使用
r.Use()全局应用或通过路由分组应用到特定路由 - 密码加密: 使用 bcrypt 加密存储用户密码
- AI评分: 简答题使用AI智能评分,提供分数、评语和改进建议
添加新功能
添加新处理器
- 在
internal/handlers/中创建处理器函数,签名为func(c *gin.Context) - 使用
c.JSON()返回响应 - 在 main.go 中注册路由(例如:
r.GET("/path", handlers.YourHandler))
添加中间件
- 在
internal/middleware/中创建返回gin.HandlerFunc的中间件 - 使用
r.Use(middleware.YourMiddleware())全局应用或应用到路由组
数据库集成
项目已集成 PostgreSQL 数据库:
- 配置: 数据库配置在 pkg/config/config.go
- 初始化: 数据库初始化在 internal/database/database.go
- 模型定义: 在
internal/models/中添加 GORM 模型 - 迁移: 使用
DB.AutoMigrate()自动迁移表结构 - 使用方式: 通过
database.GetDB()获取数据库实例
添加新的数据模型
- 在
internal/models/中创建模型文件 - 定义结构体,使用 GORM 标签
- 在 internal/database/database.go 的
InitDB()中添加AutoMigrate - 在处理器中使用
database.GetDB()进行数据库操作
导入数据到数据库
示例: 练习题数据导入
- 准备JSON数据文件: 如 practice_question_pool.json
- 创建数据模型: 在
internal/models/中定义数据结构 - 创建导入脚本: 在
scripts/目录创建导入脚本,如 import_questions.go - 解析JSON并插入:
// 读取JSON文件 data, _ := os.ReadFile("data.json") // 解析JSON var items []YourStruct json.Unmarshal(data, &items) // 插入数据库 db := database.GetDB() for _, item := range items { db.Create(&item) } - 运行导入脚本:
go run scripts/import_questions.go
注意事项:
- JSON中复杂数据(如数组、对象)需要序列化为字符串存储
- 使用唯一索引防止重复导入
- 大批量导入建议使用事务提高性能
AI评分系统
项目集成了AI智能评分功能,专门用于对简答题进行评分和反馈。
AI评分配置
AI服务配置位于 pkg/config/config.go:
type AIConfig struct {
BaseURL string // AI API地址
APIKey string // API密钥
Model string // 使用的模型名称
}
配置方式:
- 默认配置: 直接在代码中设置默认值
- 环境变量: 通过环境变量覆盖默认配置
export AI_BASE_URL="https://ai.yuchat.top" export AI_API_KEY="你的API密钥" export AI_MODEL="deepseek-v3"
AI评分服务
AI评分服务实现位于 internal/services/ai_grading.go:
主要功能:
NewAIGradingService()- 创建AI评分服务实例GradeShortAnswer(question, standardAnswer, userAnswer)- 对简答题进行AI评分
返回结果:
type AIGradingResult struct {
Score float64 // 得分 (0-100)
IsCorrect bool // 是否正确 (Score >= 60 视为正确)
Feedback string // 评语
Suggestion string // 改进建议
}
集成方式
在 practice_handler.go 的 SubmitPracticeAnswer 函数中:
// 对简答题使用AI评分
if question.Type == "short-answer" {
aiService := services.NewAIGradingService()
aiResult, err := aiService.GradeShortAnswer(
question.Question,
standardAnswerStr,
userAnswerStr,
)
// 使用AI评分结果
correct = aiResult.IsCorrect
aiGrading = &models.AIGrading{
Score: aiResult.Score,
Feedback: aiResult.Feedback,
Suggestion: aiResult.Suggestion,
}
}
API响应格式
简答题提交后,响应中会包含 ai_grading 字段:
{
"success": true,
"data": {
"correct": true,
"user_answer": "用户的答案",
"correct_answer": "标准答案",
"ai_grading": {
"score": 85,
"feedback": "答案基本正确,要点全面",
"suggestion": "可以补充一些具体的例子"
}
}
}
注意事项
- AI评分仅对
short-answer类型的题目生效 - 其他题型(填空题、判断题、选择题)仍使用传统的精确匹配方式
- AI评分失败时不影响主流程,会记录日志并使用传统评分方式
- 评分采用温度参数 0.3,确保评分结果稳定可靠
自定义AI评分提示词
如需调整评分标准,修改 ai_grading.go 中的 prompt 变量:
prompt := fmt.Sprintf(`你是一位专业的阅卷老师,请对以下简答题进行评分。
题目:%s
标准答案:%s
学生答案:%s
请按照以下要求进行评分:
1. 给出一个0-100的分数
2. 判断答案是否正确(60分及以上为正确)
3. 给出简短的评语(不超过50字)
4. 如果答案不完善,给出改进建议(不超过50字)
...
`, question, standardAnswer, userAnswer)
错题本系统
重要更新: 错题本系统已全面重构为 新版本,提供更强大的功能和更好的用户体验。
核心特性
- 多次错误记录历史 - 保存每次答错的完整记录,而非仅保留最后一次
- 智能复习系统 - 基于艾宾浩斯遗忘曲线的间隔重复算法
- 标签管理系统 - 支持自定义标签,灵活分类错题
- 智能推荐引擎 - 优先推荐需要复习的高频错题
- 掌握度追踪 - 自动计算和更新每道题的掌握度(0-100%)
- 详细统计分析 - 错题趋势、掌握度分布、薄弱知识点分析
数据模型设计
错题记录表 (wrong_questions)
type WrongQuestion struct {
ID uint // 主键
UserID uint // 用户ID
QuestionID uint // 题目ID
FirstWrongTime time.Time // 首次错误时间
LastWrongTime time.Time // 最后错误时间
TotalWrongCount int // 总错误次数
MasteryLevel int // 掌握度 (0-100)
NextReviewTime *time.Time // 下次复习时间
ConsecutiveCorrect int // 连续答对次数
IsMastered bool // 是否已掌握
Tags []string // 标签列表 (JSON)
}
错误历史表 (wrong_question_history)
type WrongQuestionHistory struct {
ID uint // 主键
WrongQuestionID uint // 关联错题记录
UserAnswer string // 用户答案 (JSON)
CorrectAnswer string // 正确答案 (JSON)
AnsweredAt time.Time // 答题时间
TimeSpent int // 答题用时(秒)
IsCorrect bool // 本次是否正确
}
错题标签表 (wrong_question_tags)
type WrongQuestionTag struct {
ID uint // 主键
UserID uint // 用户ID
Name string // 标签名
Color string // 标签颜色
Description string // 描述
}
间隔重复算法(艾宾浩斯遗忘曲线)
系统采用科学的间隔重复算法,根据用户答题情况自动安排复习时间:
复习间隔策略: [1天, 3天, 7天, 15天, 30天, 60天]
- 答错时:重置连续答对次数,重新从第一个间隔开始
- 答对时:连续答对次数+1,进入下一个复习间隔
- 完全掌握:连续答对6次后标记为"已掌握"
实现位置: internal/models/wrong_question_v2.go
// 默认复习策略
var DefaultReviewStrategy = ReviewStrategy{
Intervals: []int{1, 3, 7, 15, 30, 60},
}
// 自动计算下次复习时间
func (wq *WrongQuestion) CalculateNextReviewTime() {
strategy := DefaultReviewStrategy
level := wq.ConsecutiveCorrect
if level >= len(strategy.Intervals) {
wq.IsMastered = true // 已完全掌握
wq.MasteryLevel = 100
wq.NextReviewTime = nil
return
}
days := strategy.Intervals[level]
nextTime := time.Now().Add(time.Duration(days) * 24 * time.Hour)
wq.NextReviewTime = &nextTime
wq.MasteryLevel = (level * 100) / len(strategy.Intervals)
}
API 接口
所有 API 都在 /api/v2 路径下,与旧版 API 共存以保持向后兼容。
错题管理 API
| 方法 | 路由 | 功能 | 查询参数 |
|---|---|---|---|
| GET | /api/wrong-questions |
获取错题列表 | is_mastered, tag, type, sort |
| GET | /api/wrong-questions/:id |
获取错题详情 | - |
| GET | /api/wrong-questions/stats |
获取错题统计 | - |
| GET | /api/wrong-questions/recommended |
获取推荐错题 | limit |
| DELETE | /api/wrong-questions/:id |
删除错题 | - |
| DELETE | /api/wrong-questions |
清空错题本 | - |
| PUT | /api/wrong-questions/:id/tags |
更新标签 | - |
排序选项 (sort 参数):
review_time- 按复习时间排序(最需要复习的在前)wrong_count- 按错误次数排序(错误最多的在前)mastery_level- 按掌握度排序(掌握度最低的在前)time- 按最后错误时间排序(默认)
标签管理 API
| 方法 | 路由 | 功能 |
|---|---|---|
| GET | /api/wrong-question-tags |
获取标签列表 |
| POST | /api/wrong-question-tags |
创建标签 |
| PUT | /api/wrong-question-tags/:id |
更新标签 |
| DELETE | /api/wrong-question-tags/:id |
删除标签 |
智能推荐算法
推荐系统采用三级策略,优先推荐最需要复习的题目:
- 优先级 1: 到期需要复习的题目(
next_review_time <= NOW()) - 优先级 2: 高频错题且掌握度低(
wrong_count DESC, mastery_level ASC) - 优先级 3: 最近答错的题目(
last_wrong_time DESC)
实现位置: internal/services/wrong_question_service.go
统计数据
错题统计 提供更丰富的数据:
{
"total_wrong": 50,
"mastered": 10,
"not_mastered": 40,
"need_review": 15,
"type_stats": { "single-choice": 20, "multiple-choice": 15, "fill-in-blank": 15 },
"category_stats": { "数学": 25, "语文": 15, "英语": 10 },
"mastery_level_dist": { "很差": 10, "较差": 15, "一般": 10, "良好": 10, "优秀": 5 },
"tag_stats": { "重点": 20, "难点": 15 },
"trend_data": [
{ "date": "01-01", "count": 5 },
{ "date": "01-02", "count": 3 },
...
]
}
数据迁移
从旧版错题本迁移到 新版本:
# 运行迁移脚本
go run scripts/migrate_wrong_questions.go
迁移说明:
- 旧表
wrong_questions将保留,不会被删除 - 新表
wrong_questions包含迁移后的数据 - 每条旧记录会生成一条新记录和一条历史记录
- 迁移完成后,新答题记录将自动使用 API
前端集成
前端 TypeScript 类型定义位于 web/src/types/question.ts:
// 错题记录
interface WrongQuestion {
id: number
question_id: number
question?: Question
first_wrong_time: string
last_wrong_time: string
total_wrong_count: number
mastery_level: number // 0-100
next_review_time?: string
consecutive_correct: number
is_mastered: boolean
tags: string[]
recent_history?: WrongQuestionHistory[]
}
API 调用方法位于 web/src/api/question.ts:
// 获取错题列表(支持筛选和排序)
getWrongQuestionsV2(filter?: WrongQuestionFilter)
// 获取推荐错题
getRecommendedWrongQuestions(limit: number = 10)
// 获取错题统计
getWrongQuestionStats()
// 标签管理
getWrongQuestionTags()
createWrongQuestionTag(data)
updateWrongQuestionTag(id, data)
deleteWrongQuestionTag(id)
注意事项
- API 与旧版 API 完全兼容,可以同时使用
- 答题时会自动使用 API 记录错题
- 答对错题本中的题目时,会自动更新连续答对次数
- 掌握度达到 100% 时,题目会被标记为"已掌握"
- 标签功能支持自定义,建议按知识点或难度分类
AI评分系统
项目集成了AI智能评分功能,专门用于对简答题进行评分和反馈。
AI评分配置
AI服务配置位于 pkg/config/config.go:
type AIConfig struct {
BaseURL string // AI API地址
APIKey string // API密钥
Model string // 使用的模型名称
}
配置方式:
- 默认配置: 直接在代码中设置默认值
- 环境变量: 通过环境变量覆盖默认配置
export AI_BASE_URL="https://ai.yuchat.top" export AI_API_KEY="你的API密钥" export AI_MODEL="deepseek-v3"
AI评分服务
AI评分服务实现位于 internal/services/ai_grading.go:
主要功能:
NewAIGradingService()- 创建AI评分服务实例GradeShortAnswer(question, standardAnswer, userAnswer)- 对简答题进行AI评分
返回结果:
type AIGradingResult struct {
Score float64 // 得分 (0-100)
IsCorrect bool // 是否正确 (Score >= 60 视为正确)
Feedback string // 评语
Suggestion string // 改进建议
}
集成方式
在 practice_handler.go 的 SubmitPracticeAnswer 函数中:
// 对简答题使用AI评分
if question.Type == "short-answer" {
aiService := services.NewAIGradingService()
aiResult, err := aiService.GradeShortAnswer(
question.Question,
standardAnswerStr,
userAnswerStr,
)
// 使用AI评分结果
correct = aiResult.IsCorrect
aiGrading = &models.AIGrading{
Score: aiResult.Score,
Feedback: aiResult.Feedback,
Suggestion: aiResult.Suggestion,
}
}
API响应格式
简答题提交后,响应中会包含 ai_grading 字段:
{
"success": true,
"data": {
"correct": true,
"user_answer": "用户的答案",
"correct_answer": "标准答案",
"ai_grading": {
"score": 85,
"feedback": "答案基本正确,要点全面",
"suggestion": "可以补充一些具体的例子"
}
}
}
注意事项
- AI评分仅对
short-answer类型的题目生效 - 其他题型(填空题、判断题、选择题)仍使用传统的精确匹配方式
- AI评分失败时不影响主流程,会记录日志并使用传统评分方式
- 评分采用温度参数 0.3,确保评分结果稳定可靠
前端开发规范
包管理和开发
重要: 前端项目使用 yarn 作为包管理工具。
- 包管理器: 使用
yarn而非npm - 常用命令:
# 安装依赖 yarn install # 启动开发服务器 (运行在 http://localhost:3000) yarn dev # 构建生产版本 yarn build # 添加依赖 yarn add <package-name> # 添加开发依赖 yarn add -D <package-name>
API 代理配置
- 开发环境: Vite 已配置代理,前端请求
/api/*会被代理到后端http://localhost:8080 - 配置位置: web/vite.config.ts
- 使用方式: 前端代码中使用相对路径调用API,例如
fetch('/api/login') - 后端服务器: 确保后端服务运行在
http://localhost:8080
UI 组件使用原则
重要: 在开发前端页面时,必须优先使用 UI 框架的组件。
- 优先使用 Ant Design 组件: 项目使用 antd (Ant Design) 作为 UI 框架,开发时应优先查找并使用框架提供的组件
- 常用组件示例:
- 表单输入: 使用
Input组件,而非原生<input> - 按钮: 使用
Button组件,而非原生<button> - 表单: 使用
Form和Form.Item组件 - 提示信息: 使用
message组件,而非自定义提示框 - 对话框: 使用
Modal组件 - 列表: 使用
List组件 - 布局: 使用
Row、Col、Layout等布局组件 - 图标: 使用
@ant-design/icons包中的图标
- 表单输入: 使用
- 仅在必要时自定义: 只有当 antd 没有提供对应组件时,才使用自定义组件
- 样式处理: 使用 CSS Modules (
.module.less) 编写组件样式,避免全局样式污染 - 主题定制: 在 web/vite.config.ts 中通过
modifyVars定制 antd 主题
前端项目结构
- web/src/pages/ - 页面组件
- web/src/components/ - 可复用组件
- web/src/pages/*.module.less - 页面样式文件 (CSS Modules)
- web/vite.config.ts - Vite 配置文件(包含代理配置和 antd 主题定制)
响应式设计
重要: 应用采用响应式设计,同时适配移动端和PC端。
- 响应式断点:
- 移动端:
max-width: 768px - 平板:
769px ~ 1024px - PC端:
min-width: 1025px
- 移动端:
- 布局适配: 使用 antd 的 Grid 系统 (
Row、Col) 实现响应式布局 - 移动端优化:
- 底部导航栏仅在移动端显示
- 触摸区域大小适中(最小 44x44px)
- 禁止双指缩放
- PC端优化:
- 内容居中,最大宽度限制
- 隐藏移动端特有的底部导航栏
- 更大的字体和间距