- 根目录加入 docker-compose、Caddyfile、文档等原本不在仓库里的文件 - 前后端两份 GitHub Actions 合并成根目录的一个 deploy workflow: 构建前端上传到 build/,再在服务器上 git pull 并重建容器 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
114 lines
2.8 KiB
Markdown
114 lines
2.8 KiB
Markdown
# 挑战分数系统设计
|
||
|
||
**日期**: 2026-03-09
|
||
**状态**: 已审批
|
||
|
||
## 背景
|
||
|
||
`Submission.score` 已有加权平均评分逻辑,`Profile.total_score` 字段存在但从未激活。分数计算出来后无任何展示或用途,需要设计让分数真正发挥作用的功能。
|
||
|
||
## 目标
|
||
|
||
- 学生:看到自己各题得分和在全班的排名
|
||
- 老师:快速了解哪些学生表现好,可按条件导出成绩
|
||
- 为未来解锁机制预留接口
|
||
|
||
## 方案选择
|
||
|
||
采用**方案 A(积分展示中心)**,在 `Challenge` 模型预留 `pass_score` 字段供日后解锁功能使用。
|
||
|
||
---
|
||
|
||
## 数据层
|
||
|
||
### Profile.recalculate_total_score()
|
||
|
||
替换现有 `update_total_score()` 的累加逻辑,改为从数据库重算:
|
||
|
||
```python
|
||
def recalculate_total_score(self):
|
||
from submission.models import Submission
|
||
from django.db.models import Max, Sum
|
||
# 每道挑战取最高分,再求和
|
||
total = (
|
||
Submission.objects
|
||
.filter(user=self.user, task__task_type='challenge')
|
||
.values('task_id')
|
||
.annotate(best=Max('score'))
|
||
.aggregate(total=Sum('best'))['total']
|
||
) or 0.0
|
||
self.total_score = total
|
||
self.save(update_fields=['total_score'])
|
||
```
|
||
|
||
### Rating post_save 信号
|
||
|
||
在现有 `update_submission_score_on_save` 之后,追加调用:
|
||
|
||
```python
|
||
instance.submission.user.profile.recalculate_total_score()
|
||
```
|
||
|
||
### Challenge.pass_score 字段
|
||
|
||
```python
|
||
pass_score = models.FloatField(null=True, blank=True, verbose_name="通过分数线")
|
||
```
|
||
|
||
默认 null,不影响现有功能,为解锁机制预留。
|
||
|
||
### 用户最佳分数定义
|
||
|
||
同一用户同一挑战取 `score` 最高的提交作为该题成绩。
|
||
|
||
---
|
||
|
||
## API 层
|
||
|
||
### 1. 排行榜
|
||
|
||
```
|
||
GET /api/account/leaderboard/
|
||
```
|
||
|
||
- 无需登录
|
||
- 返回所有用户按 `total_score` 降序,含排名、用户名、总分
|
||
|
||
### 2. 我的各题得分
|
||
|
||
```
|
||
GET /api/submission/my-scores/
|
||
```
|
||
|
||
- 需登录
|
||
- 返回当前用户每道挑战的最高分提交(challenge display、title、score、提交时间)
|
||
|
||
### 3. 成绩导出
|
||
|
||
```
|
||
GET /api/submission/export/?task_id=&username=
|
||
```
|
||
|
||
- 仅限 admin/super 角色
|
||
- 复用现有 `SubmissionFilter` 参数
|
||
- 返回 CSV 文件流,每行:用户名、挑战题号、挑战标题、最高分、提交时间
|
||
|
||
---
|
||
|
||
## 前端
|
||
|
||
| 改动点 | 说明 |
|
||
|--------|------|
|
||
| 挑战列表卡片 | 显示该用户最高得分(灰色小标,未评分不显示) |
|
||
| 新页面 `/leaderboard` | 排名/用户名/总分表格,所有人可访问,放主页导航 |
|
||
| 新页面 `/my-scores` | 我的各题得分列表,登录后可见,放主页用户区域 |
|
||
| Submissions 页面 | 现有筛选栏旁加"导出 CSV"按钮,触发浏览器下载 |
|
||
|
||
---
|
||
|
||
## 不在本期范围内
|
||
|
||
- 解锁机制(`pass_score` 字段已预留,后续单独设计)
|
||
- 班级/分组维度的统计
|
||
- 实时分数推送(WebSocket)
|