diff --git a/internal/project/domain.go b/internal/project/domain.go index e791166..4bd208a 100644 --- a/internal/project/domain.go +++ b/internal/project/domain.go @@ -144,7 +144,11 @@ type IssueService interface { } // ===== 输入 DTO ===== +// +// 约定(适用于全部 Update*Input):指针字段为 nil 表示"未提供,保持原值"; +// 非 nil 表示"用解引用值替换"。Filter 类型使用零值(""、nil)表示"不过滤"。 +// CreateProjectInput 携带创建 Project 必需的字段。Visibility 留空时由 Service 默认 private。 type CreateProjectInput struct { Slug string Name string @@ -152,29 +156,39 @@ type CreateProjectInput struct { Visibility Visibility } +// UpdateProjectInput 是 Project 的 patch 输入:每个非 nil 字段表示要替换的值; +// nil 字段保持现有值不变。Slug / Owner / ArchivedAt 不在此处修改。 type UpdateProjectInput struct { Name *string Description *string Visibility *Visibility } +// CreateRequirementInput 创建一条需求。OwnerID 为 nil 时由 Service 默认为调用者。 type CreateRequirementInput struct { Title string Description string OwnerID *uuid.UUID } +// UpdateRequirementInput 同 UpdateProjectInput 的 patch 语义。Phase / Status 不在此处修改, +// 走专用方法 ChangePhase / Close / Reopen 以便 audit 区分。 type UpdateRequirementInput struct { Title *string Description *string OwnerID *uuid.UUID } +// RequirementFilter 控制 List 时的过滤维度。Phase / Status 留零值("" / Status(""))表示 +// 不限制;非零值必须落在对应枚举集合内(IsValid 校验在 Service 层做)。 type RequirementFilter struct { Phase Phase Status Status } +// CreateIssueInput 创建 Issue。RequirementNumber == nil 表示游离 Issue(不挂载到任何 +// Requirement);非 nil 时 Service 会校验 1) 该 Requirement 与目标 Project 同属, +// 2) 未关闭。 type CreateIssueInput struct { Title string Description string @@ -182,12 +196,28 @@ type CreateIssueInput struct { AssigneeID *uuid.UUID } +// UpdateIssueInput 是 Issue 的 patch 输入。RequirementNumber 为指针的指针,承载三种状态: +// +// nil —— 未提供(不修改 issue.requirement_id) +// *x == nil —— 已提供,但要求"取消挂载"(issue.requirement_id 置 NULL) +// *x != nil —— 已提供,要求"挂载到本项目下 number=**x 的 Requirement" +// +// HTTP 层用 json.RawMessage 区分 "字段缺省 vs 显式 null vs 数字",再翻译为本三态。 type UpdateIssueInput struct { Title *string Description *string RequirementNumber **int } +// IssueFilter 控制 List 时的过滤。Requirement 字段三态(与 sqlc requirement_filter +// narg 对应): +// +// nil —— 不按 requirement 过滤 +// *Requirement == 0 —— 仅游离(requirement_id IS NULL)—— 0 是 sentinel,因为 +// Issue.Number 自增从 1 起 +// *Requirement > 0 —— 仅匹配 number == *Requirement 的 Requirement +// +// AssigneeID nil 表示不按 assignee 过滤;Status 留零值表示不按状态过滤。 type IssueFilter struct { Status Status Requirement *int