You've already forked agentic-coding-workflow
feat(project): add workspace_id to issue/requirement with tri-state update
This commit is contained in:
@@ -129,6 +129,7 @@ type projectDTO struct {
|
||||
type requirementDTO struct {
|
||||
ID string `json:"id"`
|
||||
ProjectID string `json:"project_id"`
|
||||
WorkspaceID *string `json:"workspace_id"`
|
||||
Number int `json:"number"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
@@ -144,6 +145,7 @@ type issueDTO struct {
|
||||
ID string `json:"id"`
|
||||
ProjectID string `json:"project_id"`
|
||||
RequirementID *string `json:"requirement_id"`
|
||||
WorkspaceID *string `json:"workspace_id"`
|
||||
Number int `json:"number"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
@@ -177,6 +179,10 @@ func toRequirementDTO(r *Requirement) requirementDTO {
|
||||
CreatedAt: r.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
UpdatedAt: r.UpdatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
}
|
||||
if r.WorkspaceID != nil {
|
||||
s := r.WorkspaceID.String()
|
||||
d.WorkspaceID = &s
|
||||
}
|
||||
if r.ClosedAt != nil {
|
||||
s := r.ClosedAt.UTC().Format("2006-01-02T15:04:05Z")
|
||||
d.ClosedAt = &s
|
||||
@@ -196,6 +202,10 @@ func toIssueDTO(i *Issue) issueDTO {
|
||||
s := i.RequirementID.String()
|
||||
d.RequirementID = &s
|
||||
}
|
||||
if i.WorkspaceID != nil {
|
||||
s := i.WorkspaceID.String()
|
||||
d.WorkspaceID = &s
|
||||
}
|
||||
if i.AssigneeID != nil {
|
||||
s := i.AssigneeID.String()
|
||||
d.AssigneeID = &s
|
||||
@@ -332,6 +342,7 @@ type createRequirementReq struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
OwnerID *string `json:"owner_id,omitempty"`
|
||||
WorkspaceID *string `json:"workspace_id,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Handler) createRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -354,6 +365,14 @@ func (h *Handler) createRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
in.OwnerID = &uid
|
||||
}
|
||||
if req.WorkspaceID != nil {
|
||||
wid, perr := uuid.Parse(*req.WorkspaceID)
|
||||
if perr != nil {
|
||||
writeErr(w, r, errs.New(errs.CodeInvalidInput, "workspace_id 非法"))
|
||||
return
|
||||
}
|
||||
in.WorkspaceID = &wid
|
||||
}
|
||||
out, err := h.requirements.Create(r.Context(), c, chi.URLParam(r, "slug"), in)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
@@ -411,10 +430,12 @@ func (h *Handler) getRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// updateRequirementReq 用 json.RawMessage 区分 workspace_id 的 "未传"/"显式 null"/"传值"。
|
||||
type updateRequirementReq struct {
|
||||
Title *string `json:"title,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
OwnerID *string `json:"owner_id,omitempty"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
OwnerID *string `json:"owner_id,omitempty"`
|
||||
WorkspaceID json.RawMessage `json:"workspace_id,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Handler) updateRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -442,6 +463,10 @@ func (h *Handler) updateRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
in.OwnerID = &uid
|
||||
}
|
||||
if err := parseWorkspaceIDPatch(req.WorkspaceID, &in.WorkspaceID); err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
out, err := h.requirements.Update(r.Context(), c, chi.URLParam(r, "slug"), num, in)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
@@ -450,6 +475,32 @@ func (h *Handler) updateRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, toRequirementDTO(out))
|
||||
}
|
||||
|
||||
// parseWorkspaceIDPatch 把 PATCH 请求里的 json.RawMessage workspace_id 字段
|
||||
// 翻译为 *uuid.UUID 三态:
|
||||
// - 字段缺省(len == 0):保留 *dst = nil
|
||||
// - 显式 null:*dst = &uuid.Nil(解绑 sentinel)
|
||||
// - UUID 字符串:解析并赋值;解析失败返回 invalid_input
|
||||
func parseWorkspaceIDPatch(raw json.RawMessage, dst **uuid.UUID) error {
|
||||
if len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
if string(raw) == "null" {
|
||||
nilUUID := uuid.Nil
|
||||
*dst = &nilUUID
|
||||
return nil
|
||||
}
|
||||
var s string
|
||||
if err := json.Unmarshal(raw, &s); err != nil {
|
||||
return errs.New(errs.CodeInvalidInput, "workspace_id 非法")
|
||||
}
|
||||
wid, perr := uuid.Parse(s)
|
||||
if perr != nil {
|
||||
return errs.New(errs.CodeInvalidInput, "workspace_id 非法")
|
||||
}
|
||||
*dst = &wid
|
||||
return nil
|
||||
}
|
||||
|
||||
type phaseReq struct {
|
||||
Phase string `json:"phase"`
|
||||
}
|
||||
@@ -511,6 +562,7 @@ type createIssueReq struct {
|
||||
Description string `json:"description"`
|
||||
RequirementNumber *int `json:"requirement_number,omitempty"`
|
||||
AssigneeID *string `json:"assignee_id,omitempty"`
|
||||
WorkspaceID *string `json:"workspace_id,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Handler) createIssue(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -533,6 +585,14 @@ func (h *Handler) createIssue(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
in.AssigneeID = &uid
|
||||
}
|
||||
if req.WorkspaceID != nil {
|
||||
wid, perr := uuid.Parse(*req.WorkspaceID)
|
||||
if perr != nil {
|
||||
writeErr(w, r, errs.New(errs.CodeInvalidInput, "workspace_id 非法"))
|
||||
return
|
||||
}
|
||||
in.WorkspaceID = &wid
|
||||
}
|
||||
out, err := h.issues.Create(r.Context(), c, chi.URLParam(r, "slug"), in)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
@@ -607,6 +667,7 @@ type updateIssueReq struct {
|
||||
Title *string `json:"title,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
RequirementNumber json.RawMessage `json:"requirement_number,omitempty"`
|
||||
WorkspaceID json.RawMessage `json:"workspace_id,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Handler) updateIssue(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -641,6 +702,10 @@ func (h *Handler) updateIssue(w http.ResponseWriter, r *http.Request) {
|
||||
in.RequirementNumber = &pn
|
||||
}
|
||||
}
|
||||
if err := parseWorkspaceIDPatch(req.WorkspaceID, &in.WorkspaceID); err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
out, err := h.issues.Update(r.Context(), c, chi.URLParam(r, "slug"), num, in)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
|
||||
Reference in New Issue
Block a user