Files
Agent-System/internal/docduty/owner_backfill.go
T
weichenandCursor 2f0a1b337c fix(docduty): treat assigned duty as authoritative on heal
Align document slots to project document_duty even when the wrong ID is
an active member, and fail generate/list instead of returning stale
placeholder duties. Owner author backfill now picks creator, else lowest ID.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 22:16:36 +00:00

91 lines
2.8 KiB
Go

package docduty
import "strings"
// OwnerAuthorBackfillSQL 与 goose 00031 保持同步:每个项目只回填一行编制人。
// 创建者优先,否则最低 user_id,避免单条 UPDATE 同时改多行撞部分唯一索引。
const OwnerAuthorBackfillSQL = `
WITH candidates AS (
SELECT pm.project_id, pm.user_id,
CASE WHEN p.creator_user_id = pm.user_id THEN 0 ELSE 1 END AS creator_rank,
pm.user_id AS id_rank
FROM project_members pm
JOIN projects p ON p.id = pm.project_id
WHERE pm.status = 'active'
AND pm.document_duty = ''
AND (
pm.project_role = 'owner'
OR p.creator_user_id = pm.user_id
)
AND NOT EXISTS (
SELECT 1 FROM project_members other
WHERE other.project_id = pm.project_id
AND other.status = 'active'
AND other.document_duty = 'author'
)
),
picked AS (
SELECT DISTINCT ON (project_id) project_id, user_id
FROM candidates
ORDER BY project_id, creator_rank ASC, id_rank ASC
)
UPDATE project_members pm
SET document_duty = 'author', updated_at = now()
FROM picked
WHERE pm.project_id = picked.project_id AND pm.user_id = picked.user_id
`
// OwnerAuthorBackfillOneProjectSQL 与上式相同,仅限单个项目,供隔离测试使用。
const OwnerAuthorBackfillOneProjectSQL = `
WITH candidates AS (
SELECT pm.project_id, pm.user_id,
CASE WHEN p.creator_user_id = pm.user_id THEN 0 ELSE 1 END AS creator_rank,
pm.user_id AS id_rank
FROM project_members pm
JOIN projects p ON p.id = pm.project_id
WHERE pm.project_id = $1
AND pm.status = 'active'
AND pm.document_duty = ''
AND (
pm.project_role = 'owner'
OR p.creator_user_id = pm.user_id
)
AND NOT EXISTS (
SELECT 1 FROM project_members other
WHERE other.project_id = pm.project_id
AND other.status = 'active'
AND other.document_duty = 'author'
)
),
picked AS (
SELECT DISTINCT ON (project_id) project_id, user_id
FROM candidates
ORDER BY project_id, creator_rank ASC, id_rank ASC
)
UPDATE project_members pm
SET document_duty = 'author', updated_at = now()
FROM picked
WHERE pm.project_id = picked.project_id AND pm.user_id = picked.user_id
`
// PickDefaultAuthorUserID 多 owner/异常历史时确定性选择编制回填对象。
// 仅考虑在任且 document_duty 为空的 owner 或创建者:创建者优先,否则最低 user_id。
func PickDefaultAuthorUserID(members []Member, creatorID int64) int64 {
var lowestOwner int64
for _, m := range members {
if !isActiveMember(m) || strings.TrimSpace(m.DocumentDuty) != DutyNone {
continue
}
if creatorID > 0 && m.UserID == creatorID {
return creatorID
}
if m.ProjectRole != "owner" {
continue
}
if lowestOwner == 0 || m.UserID < lowestOwner {
lowestOwner = m.UserID
}
}
return lowestOwner
}