feat(review): 支持多候选稿验收轮次
- 支持每轮提交 1–5 个候选稿并按指定稿验收 - 保留历史轮次只读并兼容单候选稿版本接口 - 同步 SQLite/PostgreSQL schema、迁移验证、测试与项目文档
This commit is contained in:
45
api/db.ts
45
api/db.ts
@@ -174,6 +174,19 @@ db.exec(`
|
||||
FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS review_rounds (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
note_id INTEGER NOT NULL,
|
||||
round_number INTEGER NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'reviewing',
|
||||
selected_version_number INTEGER,
|
||||
created_by INTEGER,
|
||||
completed_at TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE(note_id, round_number),
|
||||
FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS review_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
note_id INTEGER NOT NULL,
|
||||
@@ -202,6 +215,8 @@ addColumn('notes', 'external_id TEXT');
|
||||
addColumn('notes', "tags TEXT NOT NULL DEFAULT '[]'");
|
||||
addColumn('notes', "review_status TEXT NOT NULL DEFAULT 'pending'");
|
||||
addColumn('notes', 'version_number INTEGER NOT NULL DEFAULT 1');
|
||||
addColumn('notes', 'active_round_id INTEGER');
|
||||
addColumn('notes', 'approved_version_number INTEGER');
|
||||
addColumn('annotations', "author_name TEXT NOT NULL DEFAULT '客户'");
|
||||
addColumn('annotations', "status TEXT NOT NULL DEFAULT 'open'");
|
||||
addColumn('projects', 'group_id INTEGER');
|
||||
@@ -213,6 +228,9 @@ addColumn('images', "storage_key TEXT NOT NULL DEFAULT ''");
|
||||
addColumn('images', 'version_number INTEGER NOT NULL DEFAULT 1');
|
||||
addColumn('users', 'last_login_at TEXT');
|
||||
addColumn('collections', 'completed_at TEXT');
|
||||
addColumn('work_versions', 'review_round_id INTEGER');
|
||||
addColumn('work_versions', "candidate_name TEXT NOT NULL DEFAULT '方案 A'");
|
||||
addColumn('work_versions', "candidate_status TEXT NOT NULL DEFAULT 'pending'");
|
||||
|
||||
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_notes_collection_external_id ON notes(collection_id, external_id) WHERE external_id IS NOT NULL AND external_id != ''");
|
||||
|
||||
@@ -256,6 +274,32 @@ db.prepare(`UPDATE collections SET name = '2026 年 7 月任务', client_descrip
|
||||
WHERE project_id = (SELECT id FROM projects WHERE slug = 'light-notes') AND (name LIKE '%?%' OR client_description LIKE '%?%')`).run();
|
||||
db.prepare(`INSERT OR IGNORE INTO work_versions (note_id, version_number, title, description, tags, review_status)
|
||||
SELECT id, version_number, title, description, tags, review_status FROM notes`).run();
|
||||
db.exec(`
|
||||
INSERT OR IGNORE INTO review_rounds (note_id, round_number, status, selected_version_number, completed_at, created_at)
|
||||
SELECT v.note_id, v.version_number,
|
||||
CASE WHEN v.version_number = n.version_number AND n.review_status != 'approved' THEN 'reviewing' ELSE 'completed' END,
|
||||
CASE WHEN v.review_status = 'approved' THEN v.version_number ELSE NULL END,
|
||||
CASE WHEN v.version_number = n.version_number AND n.review_status != 'approved' THEN NULL ELSE v.created_at END,
|
||||
v.created_at
|
||||
FROM work_versions v JOIN notes n ON n.id = v.note_id;
|
||||
|
||||
UPDATE work_versions
|
||||
SET review_round_id = (SELECT r.id FROM review_rounds r WHERE r.note_id = work_versions.note_id AND r.round_number = work_versions.version_number),
|
||||
candidate_name = COALESCE(NULLIF(candidate_name, ''), '方案 A'),
|
||||
candidate_status = CASE
|
||||
WHEN review_status = 'approved' THEN 'selected'
|
||||
WHEN version_number = (SELECT version_number FROM notes WHERE id = work_versions.note_id) AND review_status = 'changes_requested' THEN 'changes_requested'
|
||||
WHEN version_number = (SELECT version_number FROM notes WHERE id = work_versions.note_id) AND review_status = 'pending' THEN 'pending'
|
||||
WHEN version_number = (SELECT version_number FROM notes WHERE id = work_versions.note_id) AND review_status = 'draft' THEN 'draft'
|
||||
ELSE 'not_selected'
|
||||
END
|
||||
WHERE review_round_id IS NULL;
|
||||
|
||||
UPDATE notes
|
||||
SET active_round_id = (SELECT r.id FROM review_rounds r WHERE r.note_id = notes.id AND r.round_number = notes.version_number),
|
||||
approved_version_number = (SELECT MAX(v.version_number) FROM work_versions v WHERE v.note_id = notes.id AND v.review_status = 'approved')
|
||||
WHERE active_round_id IS NULL;
|
||||
`);
|
||||
db.exec(`
|
||||
UPDATE collections
|
||||
SET status = CASE
|
||||
@@ -288,6 +332,7 @@ db.exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_storage_configs_status ON storage_configs(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_work_versions_note_id ON work_versions(note_id, version_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_review_rounds_note_id ON review_rounds(note_id, round_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_review_events_note_id ON review_events(note_id, version_number);
|
||||
`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user