chore: 工作台产品化进行中的改动
把工作区里其余在制品一并入库,主要是工作台产品化的推进:
后端:新增 capability_definition / project / my_app_center / office_skill
接口与 action_definition / skill_definition / project / user_app_center
模型,config 加路由健康上报。
前端:新增 frontend/src/skills(Office 技能与 workbuddy 复刻)、
项目管理、应用中心、能力目录页,以及配套 api / store / config;
聊天侧新增 SpecialistChip / SpecialistPanel / SkillStrip / AppChatRail
等组件。
清理:移除旧 views/tools 下的单页工具(已并入工作台)、_frozen 冻结组件、
cmd/inspect_oa_debug 调试入口,以及两份调试笔记。
其它:文档与启动脚本同步。
(这批改动与上一提交的 SY23 工作并行进行,此前已在同一工作区内交织。)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# BE02 — 考试模块设计
|
||||
|
||||
> **版本:V1.1 | 参考:pj006-zhilianyuan2 exam/routes.py + exam/service.py**
|
||||
> **版本:V2.0 | 框架:Go + Gin + GORM + SQLite**
|
||||
> **参考:`backend-go/internal/api/exam.go`**
|
||||
|
||||
---
|
||||
|
||||
@@ -33,72 +34,65 @@ question(题库)← exam_paper(考试配置,通过 domain+question_count
|
||||
|
||||
## 4. 题库 API
|
||||
|
||||
```python
|
||||
# 管理员 — 题目 CRUD
|
||||
GET /api/exam/questions?domain=company&status=active # 题目列表
|
||||
POST /api/exam/questions # 新增题目
|
||||
PUT /api/exam/questions/{id} # 编辑题目
|
||||
DELETE /api/exam/questions/{id} # 停用题目
|
||||
```go
|
||||
// backend-go/internal/api/exam.go
|
||||
|
||||
# 管理员 — 考试配置
|
||||
GET /api/exam/papers # 考试配置列表
|
||||
POST /api/exam/papers # 创建考试
|
||||
PUT /api/exam/papers/{id} # 编辑考试
|
||||
DELETE /api/exam/papers/{id} # 停用考试
|
||||
// GET /api/exam/questions?domain=company&status=active # 题目列表
|
||||
func ListQuestions(c *gin.Context)
|
||||
// POST /api/exam/questions # 新增题目
|
||||
func CreateQuestion(c *gin.Context)
|
||||
// PUT /api/exam/questions/{id} # 编辑题目
|
||||
func UpdateQuestion(c *gin.Context)
|
||||
// DELETE /api/exam/questions/{id} # 停用题目
|
||||
func DeleteQuestion(c *gin.Context)
|
||||
|
||||
// GET /api/exam/papers # 考试配置列表
|
||||
func ListPapers(c *gin.Context)
|
||||
// POST /api/exam/papers # 创建考试
|
||||
func CreatePaper(c *gin.Context)
|
||||
// PUT /api/exam/papers/{id} # 编辑考试
|
||||
func UpdatePaper(c *gin.Context)
|
||||
// DELETE /api/exam/papers/{id} # 停用考试
|
||||
func DeletePaper(c *gin.Context)
|
||||
```
|
||||
|
||||
## 5. 学员端考试 API
|
||||
|
||||
```python
|
||||
GET /api/exam/list # 我的考试列表
|
||||
GET /api/exam/cover?id={paperId} # 考试封面/说明
|
||||
POST /api/exam/start # 开始考试 → 下发题目
|
||||
POST /api/exam/submit # 交卷判分
|
||||
GET /api/exam/record # 我的考试记录
|
||||
GET /api/exam/record/{recordId} # 考试记录详情
|
||||
```go
|
||||
// GET /api/exam/list # 我的考试列表
|
||||
func ExamList(c *gin.Context)
|
||||
// GET /api/exam/cover?id={paperId} # 考试封面/说明
|
||||
func ExamCover(c *gin.Context)
|
||||
// POST /api/exam/start # 开始考试 → 下发题目
|
||||
func ExamStart(c *gin.Context)
|
||||
// POST /api/exam/submit # 交卷判分
|
||||
func ExamSubmit(c *gin.Context)
|
||||
// GET /api/exam/record # 我的考试记录
|
||||
func ExamRecordList(c *gin.Context)
|
||||
// GET /api/exam/record/{recordId} # 考试记录详情
|
||||
func ExamRecordDetail(c *gin.Context)
|
||||
```
|
||||
|
||||
## 6. 判分逻辑(参考 zhilianyuan2 _is_correct)
|
||||
## 6. 判分逻辑(`exam.go` 内 `isCorrect` 函数)
|
||||
|
||||
```python
|
||||
# services/exam_service.py
|
||||
```go
|
||||
// backend-go/internal/api/exam.go
|
||||
|
||||
def _is_correct(qtype: str, correct: list, user: any) -> bool:
|
||||
"""确定性判分"""
|
||||
if qtype == "multiple": # 多选题:集合相等
|
||||
return sorted(correct) == sorted(user) if user else False
|
||||
elif qtype == "judge": # 判断题:值相等
|
||||
return str(correct).lower() == str(user).lower()
|
||||
else: # 单选题:值相等
|
||||
return correct == user
|
||||
|
||||
def grade_paper(questions: list, answers: dict) -> dict:
|
||||
"""批卷:逐题比对 → 统计得分/正确数"""
|
||||
correct_count = 0
|
||||
total = len(questions)
|
||||
score_per_question = 100 / total if total else 0
|
||||
details = []
|
||||
|
||||
for q in questions:
|
||||
user_ans = answers.get(str(q["id"]))
|
||||
is_correct = _is_correct(q["type"], q["answer"], user_ans)
|
||||
if is_correct:
|
||||
correct_count += 1
|
||||
details.append({
|
||||
"question_id": q["id"],
|
||||
"is_correct": is_correct,
|
||||
"user_answer": user_ans,
|
||||
"correct_answer": q["answer"],
|
||||
})
|
||||
|
||||
score = round(score_per_question * correct_count)
|
||||
return {
|
||||
"score": score,
|
||||
"correct_count": correct_count,
|
||||
"wrong_count": total - correct_count,
|
||||
"passed": score >= paper.pass_score,
|
||||
"details": details,
|
||||
func isCorrect(qtype string, correct []string, user any) bool {
|
||||
switch qtype {
|
||||
case "multiple": // 多选题:集合相等
|
||||
return sortedEqual(correct, us)
|
||||
case "judge": // 判断题:值相等
|
||||
return strings.EqualFold(correct[0], us)
|
||||
default: // 单选题:值相等
|
||||
return correct[0] == us
|
||||
}
|
||||
}
|
||||
|
||||
func ExamSubmit(c *gin.Context) {
|
||||
// 逐题比对 → 统计得分/正确数
|
||||
// 简答题(essay)走 LLM 评分,其余题型确定性判分
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 自测 vs 正式考区别
|
||||
@@ -113,7 +107,7 @@ def grade_paper(questions: list, answers: dict) -> dict:
|
||||
## 8. 考试记录设计
|
||||
|
||||
```json
|
||||
// exam_record.detail_json 示例
|
||||
// exam_record.detail_json 示例(SQLite TEXT 列)
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
@@ -133,4 +127,4 @@ def grade_paper(questions: list, answers: dict) -> dict:
|
||||
## 9. 权限
|
||||
|
||||
- **员工:** 仅查看自己的考试记录
|
||||
- **管理员:** 查看全部考试记录(`/api/system/exam-records`)
|
||||
- **管理员:** 查看全部考试记录(`/api/system/exam-records`)
|
||||
|
||||
Reference in New Issue
Block a user