chore: 合并为单仓库,统一部署流程
- 根目录加入 docker-compose、Caddyfile、文档等原本不在仓库里的文件 - 前后端两份 GitHub Actions 合并成根目录的一个 deploy workflow: 构建前端上传到 build/,再在服务器上 git pull 并重建容器 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
70983a12a5
commit
47d4679984
40 files changed
+13723
-34
No files matched your search
@@ -0,0 +1,113 @@
|
||||
# 挑战分数系统设计
|
||||
|
||||
**日期**: 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)
|
||||
Reference in new issue
Block a user