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:
xuyueandClaude Opus 5.5 committed 2026-10-26 16:02:12 +08:00
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)