Files
Agent-System/internal/docduty/store.go
T
WeiCHEN 9c7a5be209 feat(arch): clear G1 U3/U4 sql_write and path exceptions
Move document_records duty-slot writes into docgov, project ledger
heads through docgov/freedoc ports, and workspace deliverable I/O
through DeliverableStore so arch-check baseline drops to sql_write:1
path:13.
2026-09-22 21:40:12 +08:00

94 lines
3.0 KiB
Go

package docduty
import (
"context"
"fmt"
"strings"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
)
// Compile-time check: pool satisfies DB.
var _ DB = (*pgxpool.Pool)(nil)
// DB covers pool and tx for shared member/resolve reads.
type DB interface {
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error)
}
// LoadProjectMembers 读取项目在任/历史成员与创建者,供职责解析。
func LoadProjectMembers(ctx context.Context, pool *pgxpool.Pool, projectID int64) ([]Member, Holder, error) {
if pool == nil || projectID <= 0 {
return nil, Holder{}, nil
}
return LoadProjectMembersDB(ctx, pool, projectID)
}
// LoadProjectMembersDB reads members via pool or an open transaction.
func LoadProjectMembersDB(ctx context.Context, db DB, projectID int64) ([]Member, Holder, error) {
if db == nil || projectID <= 0 {
return nil, Holder{}, nil
}
return loadProjectMembers(ctx, db, projectID)
}
func loadProjectMembers(ctx context.Context, db DB, projectID int64) ([]Member, Holder, error) {
var creator Holder
err := db.QueryRow(ctx, `
SELECT COALESCE(p.creator_user_id, 0), COALESCE(u.name, '')
FROM projects p
LEFT JOIN users u ON u.id = p.creator_user_id
WHERE p.id = $1
`, projectID).Scan(&creator.UserID, &creator.Name)
if err != nil && err != pgx.ErrNoRows {
return nil, Holder{}, fmt.Errorf("docduty: load creator: %w", err)
}
creator.Name = strings.TrimSpace(creator.Name)
rows, err := db.Query(ctx, `
SELECT pm.user_id, COALESCE(u.name, ''), COALESCE(pm.status, ''),
COALESCE(pm.project_role, ''), COALESCE(pm.document_duty, '')
FROM project_members pm
JOIN users u ON u.id = pm.user_id
WHERE pm.project_id = $1
ORDER BY pm.user_id ASC
`, projectID)
if err != nil {
return nil, creator, fmt.Errorf("docduty: load members: %w", err)
}
defer rows.Close()
members := make([]Member, 0)
for rows.Next() {
var m Member
if err := rows.Scan(&m.UserID, &m.Name, &m.Status, &m.ProjectRole, &m.DocumentDuty); err != nil {
return nil, creator, err
}
m.Name = strings.TrimSpace(m.Name)
members = append(members, m)
}
return members, creator, rows.Err()
}
// ResolveProject 从库表解析项目默认文档职责。
func ResolveProject(ctx context.Context, pool *pgxpool.Pool, projectID int64, req RequestDuties) (Binding, error) {
members, creator, err := LoadProjectMembers(ctx, pool, projectID)
if err != nil {
return Binding{}, err
}
return Resolve(members, creator, req), nil
}
// ResolveProjectTx resolves project document duties inside the caller's transaction.
func ResolveProjectTx(ctx context.Context, tx pgx.Tx, projectID int64, req RequestDuties) (Binding, error) {
members, creator, err := loadProjectMembers(ctx, tx, projectID)
if err != nil {
return Binding{}, err
}
return Resolve(members, creator, req), nil
}