You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -60,6 +60,12 @@ func (h *Handler) Mount(r chi.Router) {
|
||||
r.Post("/{slug}/archive", h.archiveProject)
|
||||
r.Post("/{slug}/unarchive", h.unarchiveProject)
|
||||
|
||||
// 成员/角色 ACL(autonomy roadmap §11):list 需可读;add/remove 仅
|
||||
// project-admin 或 global-admin(service 层 assertCanManageMembers 兜底)。
|
||||
r.Get("/{slug}/members", h.listMembers)
|
||||
r.Post("/{slug}/members", h.addMember)
|
||||
r.Delete("/{slug}/members/{userID}", h.removeMember)
|
||||
|
||||
r.Post("/{slug}/requirements", h.createRequirement)
|
||||
r.Get("/{slug}/requirements", h.listRequirements)
|
||||
r.Get("/{slug}/requirements/{number}", h.getRequirement)
|
||||
@@ -70,6 +76,7 @@ func (h *Handler) Mount(r chi.Router) {
|
||||
r.Post("/{slug}/requirements/{number}/artifacts", h.createArtifact)
|
||||
r.Get("/{slug}/requirements/{number}/artifacts", h.listArtifacts)
|
||||
r.Get("/{slug}/requirements/{number}/artifacts/{phase}/{version}", h.getArtifact)
|
||||
r.Post("/{slug}/requirements/{number}/artifacts/{phase}/{version}/approve", h.approveArtifact)
|
||||
|
||||
r.Post("/{slug}/issues", h.createIssue)
|
||||
r.Get("/{slug}/issues", h.listIssues)
|
||||
@@ -171,6 +178,29 @@ type artifactDTO struct {
|
||||
SourceMessageID *int64 `json:"source_message_id,omitempty"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Verdict string `json:"verdict"`
|
||||
}
|
||||
|
||||
type memberDTO struct {
|
||||
ProjectID string `json:"project_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Role string `json:"role"`
|
||||
AddedBy *string `json:"added_by,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
func toMemberDTO(m *Member) memberDTO {
|
||||
d := memberDTO{
|
||||
ProjectID: m.ProjectID.String(),
|
||||
UserID: m.UserID.String(),
|
||||
Role: string(m.Role),
|
||||
CreatedAt: m.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
}
|
||||
if m.AddedBy != nil {
|
||||
s := m.AddedBy.String()
|
||||
d.AddedBy = &s
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func toProjectDTO(p *Project) projectDTO {
|
||||
@@ -240,6 +270,7 @@ func toArtifactDTO(a *Artifact) artifactDTO {
|
||||
SourceMessageID: a.SourceMessageID,
|
||||
CreatedBy: a.CreatedBy.String(),
|
||||
CreatedAt: a.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
Verdict: string(a.Verdict),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +395,75 @@ func (h *Handler) toggleArchive(w http.ResponseWriter, r *http.Request, archive
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ===== Member endpoints(autonomy roadmap §11)=====
|
||||
|
||||
type addMemberReq struct {
|
||||
UserID string `json:"user_id"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
func (h *Handler) listMembers(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
out, err := h.projects.ListMembers(r.Context(), c, chi.URLParam(r, "slug"))
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
dtos := make([]memberDTO, 0, len(out))
|
||||
for _, m := range out {
|
||||
dtos = append(dtos, toMemberDTO(m))
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"items": dtos})
|
||||
}
|
||||
|
||||
func (h *Handler) addMember(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
var req addMemberReq
|
||||
if err := decodeBody(r, &req); err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
uid, err := uuid.Parse(req.UserID)
|
||||
if err != nil {
|
||||
writeErr(w, r, errs.New(errs.CodeInvalidInput, "user_id 非法"))
|
||||
return
|
||||
}
|
||||
out, err := h.projects.AddMember(r.Context(), c, chi.URLParam(r, "slug"), AddMemberInput{
|
||||
UserID: uid, Role: Role(req.Role),
|
||||
})
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, toMemberDTO(out))
|
||||
}
|
||||
|
||||
func (h *Handler) removeMember(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
uid, err := uuid.Parse(chi.URLParam(r, "userID"))
|
||||
if err != nil {
|
||||
writeErr(w, r, errs.New(errs.CodeInvalidInput, "user_id 非法"))
|
||||
return
|
||||
}
|
||||
if err := h.projects.RemoveMember(r.Context(), c, chi.URLParam(r, "slug"), uid); err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ===== Requirement endpoints =====
|
||||
|
||||
type createRequirementReq struct {
|
||||
@@ -672,6 +772,40 @@ func (h *Handler) getArtifact(w http.ResponseWriter, r *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, toArtifactDTO(out))
|
||||
}
|
||||
|
||||
type approveArtifactReq struct {
|
||||
Verdict string `json:"verdict"`
|
||||
}
|
||||
|
||||
func (h *Handler) approveArtifact(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
num, err := numberParam(r)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
ver, err := strconv.Atoi(chi.URLParam(r, "version"))
|
||||
if err != nil || ver <= 0 {
|
||||
writeErr(w, r, errs.New(errs.CodeInvalidInput, "version 非法"))
|
||||
return
|
||||
}
|
||||
var req approveArtifactReq
|
||||
if err := decodeBody(r, &req); err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
phase := Phase(chi.URLParam(r, "phase"))
|
||||
out, err := h.artifacts.Approve(r.Context(), c, chi.URLParam(r, "slug"), num, phase, ver, Verdict(req.Verdict))
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, toArtifactDTO(out))
|
||||
}
|
||||
|
||||
// ===== Issue endpoints =====
|
||||
|
||||
type createIssueReq struct {
|
||||
|
||||
Reference in New Issue
Block a user