 xuyueandClaude Opus 5.5
|
02e2fa7d9b
|
feat(后台): 手机上也能用了——菜单收进左边抽屉,列表一行一张卡,不用再横着滑
- 侧栏手机上不再占一列图标,顶栏左边 ☰ 拉出整张菜单,按返回键先关菜单
- 题目、比赛、用户、公告、题单列表手机上一项一张卡:名字一行、小字一行、按钮一行,
「学生看得到」开关单独一列靠右
- 卡点分析、AI 报告手机上点开一项整屏看,返回键关掉;年度趋势、自学情况、首页改成上下排
- 出题页、教程编辑、题单编辑、标签、设置手机上排成一列;测试数据「删掉」手机上常驻
- 弹框一律收进屏幕宽度
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-10-16 11:21:58 +08:00 |
|
 xuyueandClaude Opus 5.5
|
cea2c7f102
|
fix(手机): 比赛排名表看不出能往右滑;班级 PK 每周图日期字太小;后台题目列表「藏着的」被切掉
Deploy / deploy (push) Canceled after 0s
- 比赛排名:手机上名字、做对、用时就占满一屏,每题的格子全在屏幕外。列收窄,前一两道题的格子露出来
- 班级 PK:每周人均横轴在手机上是 10px 的「10月12日」,改成 12px 的「10/12」
- 后台题目列表:手机上「全部 / 学生看得到 / 藏着的」换小号按钮,放不下就横着滑
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-10-15 12:44:57 +08:00 |
|
 xuyueandClaude Opus 5.5
|
30bff2acc4
|
style(后台): 后台各张列表都改成斑马底纹(题目、用户、比赛、公告、练一练记录、标签、AI 报告、成就)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-10-13 14:45:22 +08:00 |
|
 xuyueandClaude Opus 5.5
|
6470f56f87
|
fix(后台): 用户、题目、比赛、公告、标签几张列表的操作不再收进 ⋯,直接摆在行尾;名字和题目那列不再吃掉整行的空,创建日期不再折成两行
1366 宽的屏上题目列表、比赛列表的按钮都放得下;题目的知识点多于两个时只摆一个加「+N」。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-10-13 14:29:56 +08:00 |
|
 xuyueandClaude Opus 5.5
|
97049d562b
|
feat(后台题目): 题目列表重排——1366 屏上不用横滚就看得到「学生看得到」开关;一行只剩「编辑」和 ⋯,有人交过的题删除直接灰掉;勾几道一次放出、藏起、加减知识点
去掉内部 ID、难度、看不懂的「功能」图标和反馈表情几列;题目名点了就是编辑。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-10-13 13:27:39 +08:00 |
|
xuyue
|
2224478294
|
fix
Deploy / deploy (push) Canceled after 0s
|
2026-10-09 21:35:34 +08:00 |
|
 xuyueandClaude Opus 5.5
|
2cb39fd96b
|
feat(后台): SQL 题有了自己的出题页——左边写,右边就是学生看到的样子,改答案、改数据马上重跑
- 「新建题目」先选编程题还是 SQL 题,选了就不能改;编辑时按题目本身进对应的页
- SQL 出题页(设计稿「SQL 出题页」方案 B):测试数据一组一行,改完自动重跑,
跑不通的用中文说原因;保存时才打包上传测试点,不用再手动「预览」「上传」
- 新建时存草稿,误关页面不丢
- 编程题出题页去掉 SQL 那一堆分支(以前勾上 SQL 会静默清掉语法要求和流程图)
- 题目列表能按题型筛,SQL 题标题后面挂标签
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-10-09 18:59:15 +08:00 |
|
 xuyueandClaude Opus 5.5
|
d3823430f9
|
feat(后台): 侧栏分组带图标、可收起,加顶栏;所有后台页面统一页头
Deploy / deploy (push) Canceled after 0s
- 侧栏按 概览 / 教学内容 / 学情分析 / 站点管理 分组,去掉超管菜单里的重复项;
菜单项的角色与路由守卫一一对应,没有题目权限的教师不再看到点了就被踢回首页的「题目」
- 顶栏:面包屑、回到前台、明暗切换、用户菜单(退出登录);窄屏侧栏自动收起
- 新增 PageHeader:标题 + 右侧主操作 + 下方筛选栏,编辑页带返回按钮
- 卡点分析 / 年度趋势 / 标签管理从题目列表的按钮挪进侧栏
- 首页改成统计卡片 + 班级排名卡片;设置页按钮移到卡片右上角
- 题目、比赛、题单、用户列表的「选项」列固定在右侧,不再被挤出屏幕
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-09-27 19:54:42 +08:00 |
|
xuyue
|
d25b5dbe6d
|
update
Deploy / deploy (push) Canceled after 0s
|
2026-09-27 19:36:28 +08:00 |
|
 xuyueandClaude Opus 5.5
|
5ae830a7f9
|
refactor: 一批名不副实的命名改掉(只动一端,不碰契约)
后端:
- getUserByToken → authenticateToken:它会删会话、续 TTL、记在线,不是纯读
- todayStart → dayStart(instant):本来就接受任意时刻,不只是「今天」
- objectValue → asRecord,删掉 4 份一模一样的私有拷贝和 `as toObject` 别名
- 报错文案 "does not exists" / "Already be a public problem" 改通顺
前端:
- utils/types 里 Contest / User / Problem 三个别名删掉:Contest 原来指后台
比赛、契约里同名的却是学生侧。现在 Contest = 学生侧、AdminContest = 后台,
后台用户表单叫 AdminUserForm,题目详情直接用 ProblemDetail
- SubmissionInfo → ContestSubmissionInfo,免得和 submission.info 撞名
- utoa / atou → compressToBase64 / decompressFromBase64(实际是 zlib + base64),
encode / decode → base64EncodeUtf8 / base64DecodeUtf8
- filterResult → toProblemRow、toProblemListItem → toAdminProblemRow,
行字段 submission / rate → submissionCount / acRate
- 组件:Modal → AddFromLibraryModal、Form → EditorToolbar、
ContestEditor → BasicEditor(题单也在用它)、rank 的 Index → RankMedal、
Name → UserNameCell、Password → PasswordCell、ContestType → ContestAccessTag
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-09-27 16:08:21 +08:00 |
|
 xuyueandClaude Opus 5
|
498fc1ceca
|
fix(后台): 会话吊销、导入查重、练习题校验,以及一批后台页面的小毛病
Deploy / deploy (push) Has been cancelled
后台代码审查后的一批修复。
后端:
- 改密码 / 重置密码 / 禁用账号现在真的把该用户所有设备的会话删掉。原来只
publishSessionRevoked 广播断 WebSocket,HTTP 拿着旧 cookie 照样能用到会话
自然过期 —— 给被盗用的账号改密码等于没改。为此在 Redis 里补了反向索引
user-sessions:<id>(createSession 写入、登出和失效路径清理、跟着会话续期)。
- 导入用户补齐校验:邮箱走 z.email()、批内查重、库内查重,用户名和邮箱各报各的;
用户名和邮箱都归一成小写,和登录的 lower(username) 比较口径对齐。以前导入这条
路什么都不查,而前端占位邮箱按「班级+批内序号」拼,同一个班导第二批必然重号,
那两个账号从此在后台保存一次就撞 409、再也改不动。前端生成的占位邮箱同步加了
每批随机后缀。
- PUT /users/:id 的邮箱查重改比 lower(email),存量大小写混着的数据也能拦住。
- 删用户的裸 catch 收窄成只认外键冲突 23503(顺 cause 链找,drizzle 0.45 把驱动
错误包了一层),别的错照常抛 500,不再把连接故障说成「该用户还有历史数据」。
- 练习题 data 补语义校验(services/exercise.ts):没有 {{空位}} 的填空题、空选项的
选择题、越界的下标等一律拒收。以前后端零校验,坏数据只有学生端会撞到。
- PUT /judge-servers/:id 改用 queryInteger,非数字 id 回 404 而不是 500。
- 比赛克隆不加归属校验是**有意的**(快速再开一场以前的比赛;保密边界在师生之间不在
教师之间),把这条政策和它的副作用写进注释,免得反复被当成漏洞。
前端:
- 编辑用户弹窗的「班级」输入框改成只读 —— 它一直是个改了没用的控件,班级由后端从
用户名推导。
- 新建用户预填唯一占位邮箱、角色默认改成实际会建出来的 Regular User、密码留空直接拦。
- 比赛题目列表的列过滤写的是 top_reaction,实际 key 是 topReaction,空列一直没被滤掉。
- AI 生成流程图加 try/finally,接口失败不再把按钮卡在 loading。
- 单个判题机删除后刷新表格;后台首页显示在线判题机数量(后端一直在下发)。
- 下载测试点失败时读 Blob 里的错误信封弹提示,不再毫无反应。
- 练习题编辑器补上和后端一致的前置校验。
验证:tsc / vue-tsc / vite build / check:routes 全过;后端每条改动都在本机起服务
实跑确认(会话吊销、导入各种重复、练习题七种题型、外键 409、非数字 id)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RC5uL72UY9aZFuTvUKe2jv
|
2026-09-06 08:41:54 -06:00 |
|
 xuyueandClaude Opus 5
|
25c6e99c54
|
feat(后台): 补回「最高票评价」列,重写时漏掉了
管理端题目列表的「反馈」列一直是空的:前端渲染逻辑还在,后端两处
硬编码 topReaction: null —— 旧后端的 reaction/services.py:get_top_reactions
没有跟着迁过来,阶段 0 的清单也没抓到。
新增 services/reaction.ts,口径逐条对齐旧实现:
- 按 (problem_id, type) 分组计数,取票数最高的那个;
- 并列时按 reactionKeySchema 的定义顺序取靠前的,与前端 REACTIONS 的顺序
一致(旧后端是 TYPE_ORDER,已核对两边七个 key 的顺序与内容完全相同);
- 只返回有评价的题目,没有的题调用方兜 null。
比库里可能残留、但前端已经下掉的类型多了一道跳过:不跳的话一个不再展示的
类型会以并列最优的身份把真正的最高票挤掉。
只有公开题列表下发,比赛题列表不下发 —— 与旧后端一致(旧的 top_reaction
只出现在 ProblemAPI,ContestProblemAPI 没有),前端那一列的注释也是这么写的。
实测:造 3 票 learned + 1 票 too_easy 取到 learned/3;interesting 与
too_hard 各 1 票时取到 too_hard(定义顺序在前);插一条 obsolete_kind
不影响结果;无评价的题返回 null。探针数据已清理。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-25 14:08:09 -06:00 |
|
 xuyueandClaude Opus 5
|
c2a81209d7
|
refactor(前端): 拆掉 camelCase→snake_case 转换层,契约成为唯一真相
utils/legacy.ts 是迁移期的临时层:新后端一律 camelCase,而组件读的还是
旧 Django 的 snake_case,于是在 api 层做一次递归键名重写。它自己的注释就
写了「迁移完成后这一层应当整体拆掉」。现在拆了。
代价不只是那 96 处包装:每个响应都要递归遍历整个对象重写一遍键名,而且
utils/types.ts 和 packages/contract 是两份真相 —— 手抄的那份还抄歪了好几处。
做法是按域推进,每域都用 vue-tsc 相对基线做差,确认零新增错误后再往下走。
前端的类型现在一律以契约为准,只在必要处窄化(比如 languages/template 的键
窄化成 LANGUAGE),删掉的重复定义包括 WebsiteConfig、LoginSummary、
AchievementSummary、ProblemSet、Contest、User、Profile、AdminTag、
StuckProblem 等等,其中 ClassComparison 有两个组件各手抄了一份。
## 顺带修掉的真 bug
- 管理端公告列表的「可见」开关每次都 400:列表响应被契约 omit 掉了 content,
而更新接口要求 content 必填,toggleVisible 把列表行原样回传。而且是乐观
翻转、不 await 不 catch,管理员看到开关动了、实际没存也没有提示。
改成先 GET 整条再 PUT,加失败提示。
- 删有提交的题时只显示笼统的「删除失败」:前端还在 match 旧 Django 的英文
文案,而后端返回的是 problem-has-submissions + 中文。连同另外 8 处同类
匹配一起改成判错误码 —— 文案是后端随时能改的,match 文案改一个字就静默失效。
- SubmissionStatus.time_limit_exceeded 写成 `1 | 2`,TS 按位或算成 3,和
memory_limit_exceeded 撞了同一个值。后端 judge/status.ts 里这是分开的
两个码,按后端拆成 cpu_/real_ 两项。当前没有代码读这两个成员,但
CLAUDE.md 明确要求判题状态码三处同步。
- 流程图历史翻到没有提交的那一页会直接抛:契约里 submission 是 nullable,
被 any 掩盖成看起来非空。补了 null 分支。
## 契约里被逼出来的三处不诚实
- grade 写成 z.string(),但 averageGrade() 在没有可用数据时返回空串,
前端三张图表拿它查 Record<Grade,...> 会查出 undefined。按实际收紧成
z.enum([...,""]),四个查表点都补了「无评级」分支。
- difficulty 写成 z.string()。核对过生产库 dump:956 道题只有
Low/Mid/High 三个值(761/149/46)。收紧成枚举。
- topReaction 写成 z.string(),既对不上前端渲染的 {type,count},也对不上
旧后端 get_top_reactions 下发的形状。改成正确形状并注明当前恒传 null。
## 明确保留 snake_case 的 54 处
判题沙箱原始输出(cpu_time/exit_code/output_md5/compile_output)、
statistic_info 内容(err_info/time_cost/ast_results)、submission_info
JSONB(is_ac/ac_time/error_number,回滚时旧后端还要读)、SQL 判题引擎的
total_rows/order_sensitive/changed_tables、WebSocket 的 submission_id、
以及数据库选项键 enable_maxkb。每一处都在类型定义旁写了为什么不能改。
language 没有跟着收紧契约 —— 它是配置项、随时可能加语言,收紧会让新语言
在后端 parse 时直接抛。改在 api 边界一处窄化。
## 另外
- utils/http.ts 整个模块已是死代码(四处引用全是 import type),删除。
- profile 的 blog/github/school/major/language 五个字段全链路空转,没有
任何组件读,从契约到类型一并摘除(数据库列不动)。
- admin/account.ts 往 user_profile 塞的 totalScore 是 OI 模式遗留,表里
没这一列。Drizzle 按表定义拼列名会把它静默丢弃,所以没出过错,是死代码。
验证:vue-tsc 143 → 54 条且无新增,apps/api tsc、check:routes、web build
全通过;各域响应形状逐条打接口核对过。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-25 13:44:24 -06:00 |
|
xuyue
|
ae1fb329b5
|
feat(阶段1): 搬入 ojnext 为 apps/web,未改业务代码
|
2026-08-06 21:18:16 -06:00 |
|