feat(阶段4): 标签管理 / 批量打标签 / 题目可见性 / 卡点与 AC 趋势 / 流程图 AI
GET/PUT/DELETE admin/problem-tags[/:id] POST admin/problems/batch-tag PUT admin/problems/:id/visibility GET admin/problems/stuck GET admin/problems/ac-trend POST admin/problems/flowchart 要点: - 后台标签列表用 leftJoin 且不加 having,能看到 problemCount=0 的标签 —— 那正是要清理的那些。oj 侧的 /problem-tags 才过滤 >0。 - 标签改名撞上已有标签视为合并:只给「还没挂目标标签」的题目补关系, 否则会撞 (problem_id, problemtag_id) 唯一约束。 - 批量打标签:add 时按需新建标签、remove 时只认已有标签 —— 否则「移除」会顺手造出一堆空标签。名字去重且大小写不敏感。 - **旧 ProblemVisibleAPI 的 `self.error(...)` 少写了 return**,题目不存在时会继续 往下跑并抛 AttributeError(500)。这里正常返回 404。 顺带记下一条阶段 5 的必做项(见 phase3-coverage.md 文末):本地库按显式 id 从生产 导入,序列没跟着走,第一次新建标签就撞 problem_tag_pkey。只要切换流程里有 「导出→导入到新库」这一步,就必须重置全部序列,否则读全正常、第一次写才炸。 实测:学生 403;大小写重复与空白名去重后 tagCount=1、重复 add 幂等、 remove 不存在的标签 404 且不新建;纯改名 merged=false、合并 merged=true affectedCount=1 且只剩一个标签;可见性取反两次复原、不存在的题 404。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,3 +147,34 @@
|
||||
|
||||
1. 本报告的对照关系带人工判断成分,若某条对应有异议,以实际业务行为为准。
|
||||
2. `utils/download.ts` 仍指向旧后端的 `/api/admin`(blob 下载),只被 admin 侧两个页面用,随阶段 4 一起切。
|
||||
|
||||
---
|
||||
|
||||
## 阶段 5 切换必做项(阶段 4 施工时发现,记在这里以免忘)
|
||||
|
||||
**导入数据后必须重置全部序列。** 本地库是按显式 id 从生产导入的,
|
||||
`problem_tag_id_seq` 停在 6 而表里 max(id)=87,于是第一次新建标签就撞
|
||||
`duplicate key value violates unique constraint "problem_tag_pkey"`(500)。
|
||||
|
||||
生产切换若沿用同一个库则不受影响(序列本来就是对的);但只要有任何一步是
|
||||
「导出 → 导入到新库」,就必须补这一句:
|
||||
|
||||
```sql
|
||||
do $$
|
||||
declare r record; mx bigint;
|
||||
begin
|
||||
for r in
|
||||
select split_part(pg_get_serial_sequence(quote_ident(t.table_name), c.column_name), '.', 2) as seqname,
|
||||
t.table_name, c.column_name
|
||||
from information_schema.tables t
|
||||
join information_schema.columns c on c.table_name = t.table_name
|
||||
join pg_sequences s on s.sequencename = split_part(pg_get_serial_sequence(quote_ident(t.table_name), c.column_name), '.', 2)
|
||||
where t.table_schema = 'public'
|
||||
loop
|
||||
execute format('select coalesce(max(%I),0) from %I', r.column_name, r.table_name) into mx;
|
||||
execute format('select setval(%L, greatest(%s, 1))', r.seqname, mx);
|
||||
end loop;
|
||||
end $$;
|
||||
```
|
||||
|
||||
症状很隐蔽:读全部正常,只有**写**才炸,而且是导入后第一次写才炸。
|
||||
|
||||
Reference in New Issue
Block a user