scorpio d6df056687 feat: Part 模型 + 文件版本追踪 + 写手团队工作流 v2
- 数据层:messages 表增加 part_type 字段,新建 file_versions 表支持版本追踪
- 后端:saveWorkspace 版本追踪、saveAgentOutput 源头分离、generateBriefMessage 成员简报
- 后端:applyDocumentEdit 增量编辑、buildWorkflowStep phase-aware 工作流引擎
- API:文件版本查询/回退接口
- 前端:part_type 驱动渲染,产物面板版本历史
- 新增写手团队(主编/搜索员/策划编辑/合规审查员)配置
- store 模块、scheduler 模块、web-search skill

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:44:34 +08:00

117 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package store
import (
"database/sql"
"sync"
_ "modernc.org/sqlite"
)
type Store struct {
db *sql.DB
mu sync.Mutex
}
func New(dbPath string) (*Store, error) {
db, err := sql.Open("sqlite", dbPath+"?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)")
if err != nil {
return nil, err
}
s := &Store{db: db}
if err := s.migrate(); err != nil {
db.Close()
return nil, err
}
return s, nil
}
func (s *Store) Close() error {
return s.db.Close()
}
func (s *Store) migrate() error {
_, err := s.db.Exec(`
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
room_id TEXT NOT NULL,
agent TEXT NOT NULL,
role TEXT NOT NULL,
content TEXT NOT NULL DEFAULT '',
filename TEXT,
title TEXT,
group_id INTEGER,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_msg_room ON messages(room_id, created_at);
CREATE INDEX IF NOT EXISTS idx_msg_group ON messages(group_id);
CREATE TABLE IF NOT EXISTS token_usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
room_id TEXT NOT NULL,
agent TEXT NOT NULL,
prompt_tokens INTEGER NOT NULL DEFAULT 0,
completion_tokens INTEGER NOT NULL DEFAULT 0,
total_tokens INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_tu_room ON token_usage(room_id);
CREATE TABLE IF NOT EXISTS schedules (
id TEXT PRIMARY KEY,
room_id TEXT NOT NULL,
cron TEXT NOT NULL,
message TEXT NOT NULL,
user_name TEXT NOT NULL DEFAULT 'scheduler',
enabled INTEGER NOT NULL DEFAULT 1,
once INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_run_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_sch_room ON schedules(room_id);
CREATE TABLE IF NOT EXISTS file_versions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
room_id TEXT NOT NULL,
filename TEXT NOT NULL,
content TEXT NOT NULL,
agent TEXT NOT NULL DEFAULT '',
version INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_fv_room_file ON file_versions(room_id, filename);
`)
if err != nil {
return err
}
// 安全添加 part_type 列SQLite 不支持 ALTER TABLE ... IF NOT EXISTS
var hasPartType bool
rows, err := s.db.Query(`PRAGMA table_info(messages)`)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var cid int
var name, typ string
var notnull int
var dfltValue *string
var pk int
if err := rows.Scan(&cid, &name, &typ, &notnull, &dfltValue, &pk); err != nil {
return err
}
if name == "part_type" {
hasPartType = true
break
}
}
if !hasPartType {
_, err = s.db.Exec(`ALTER TABLE messages ADD COLUMN part_type TEXT NOT NULL DEFAULT 'text'`)
if err != nil {
return err
}
}
return nil
}