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>
91 lines
2.8 KiB
Go
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
|
|
}
|