You've already forked agentic-coding-workflow
原型阶段
This commit is contained in:
+119
-8
@@ -38,13 +38,14 @@ type Handler struct {
|
||||
projects ProjectService
|
||||
requirements RequirementService
|
||||
issues IssueService
|
||||
prototypes PrototypeService
|
||||
resolver middleware.SessionResolver
|
||||
users AdminLookup
|
||||
}
|
||||
|
||||
// NewHandler 构造 Handler。
|
||||
func NewHandler(p ProjectService, r RequirementService, i IssueService, resolver middleware.SessionResolver, users AdminLookup) *Handler {
|
||||
return &Handler{projects: p, requirements: r, issues: i, resolver: resolver, users: users}
|
||||
func NewHandler(p ProjectService, r RequirementService, i IssueService, proto PrototypeService, resolver middleware.SessionResolver, users AdminLookup) *Handler {
|
||||
return &Handler{projects: p, requirements: r, issues: i, prototypes: proto, resolver: resolver, users: users}
|
||||
}
|
||||
|
||||
// Mount 把所有 PM 路由挂到 r 上,并装入 Auth 中间件。
|
||||
@@ -66,6 +67,9 @@ func (h *Handler) Mount(r chi.Router) {
|
||||
r.Post("/{slug}/requirements/{number}/phase", h.changeRequirementPhase)
|
||||
r.Post("/{slug}/requirements/{number}/close", h.closeRequirement)
|
||||
r.Post("/{slug}/requirements/{number}/reopen", h.reopenRequirement)
|
||||
r.Post("/{slug}/requirements/{number}/prototypes", h.createPrototype)
|
||||
r.Get("/{slug}/requirements/{number}/prototypes", h.listPrototypes)
|
||||
r.Get("/{slug}/requirements/{number}/prototypes/{version}", h.getPrototype)
|
||||
|
||||
r.Post("/{slug}/issues", h.createIssue)
|
||||
r.Get("/{slug}/issues", h.listIssues)
|
||||
@@ -157,6 +161,16 @@ type issueDTO struct {
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type prototypeDTO struct {
|
||||
ID string `json:"id"`
|
||||
RequirementID string `json:"requirement_id"`
|
||||
Version int `json:"version"`
|
||||
Content string `json:"content"`
|
||||
Note string `json:"note"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
func toProjectDTO(p *Project) projectDTO {
|
||||
d := projectDTO{
|
||||
ID: p.ID.String(), Slug: p.Slug, Name: p.Name, Description: p.Description,
|
||||
@@ -217,6 +231,15 @@ func toIssueDTO(i *Issue) issueDTO {
|
||||
return d
|
||||
}
|
||||
|
||||
func toPrototypeDTO(p *Prototype) prototypeDTO {
|
||||
return prototypeDTO{
|
||||
ID: p.ID.String(), RequirementID: p.RequirementID.String(),
|
||||
Version: p.Version, Content: p.Content, Note: p.Note,
|
||||
CreatedBy: p.CreatedBy.String(),
|
||||
CreatedAt: p.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Project endpoints =====
|
||||
|
||||
type createProjectReq struct {
|
||||
@@ -314,8 +337,10 @@ func (h *Handler) updateProject(w http.ResponseWriter, r *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, toProjectDTO(out))
|
||||
}
|
||||
|
||||
func (h *Handler) archiveProject(w http.ResponseWriter, r *http.Request) { h.toggleArchive(w, r, true) }
|
||||
func (h *Handler) unarchiveProject(w http.ResponseWriter, r *http.Request) { h.toggleArchive(w, r, false) }
|
||||
func (h *Handler) archiveProject(w http.ResponseWriter, r *http.Request) { h.toggleArchive(w, r, true) }
|
||||
func (h *Handler) unarchiveProject(w http.ResponseWriter, r *http.Request) {
|
||||
h.toggleArchive(w, r, false)
|
||||
}
|
||||
|
||||
func (h *Handler) toggleArchive(w http.ResponseWriter, r *http.Request, archive bool) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
@@ -529,8 +554,12 @@ func (h *Handler) changeRequirementPhase(w http.ResponseWriter, r *http.Request)
|
||||
httpx.WriteJSON(w, http.StatusOK, toRequirementDTO(out))
|
||||
}
|
||||
|
||||
func (h *Handler) closeRequirement(w http.ResponseWriter, r *http.Request) { h.toggleReqStatus(w, r, true) }
|
||||
func (h *Handler) reopenRequirement(w http.ResponseWriter, r *http.Request) { h.toggleReqStatus(w, r, false) }
|
||||
func (h *Handler) closeRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
h.toggleReqStatus(w, r, true)
|
||||
}
|
||||
func (h *Handler) reopenRequirement(w http.ResponseWriter, r *http.Request) {
|
||||
h.toggleReqStatus(w, r, false)
|
||||
}
|
||||
|
||||
func (h *Handler) toggleReqStatus(w http.ResponseWriter, r *http.Request, doClose bool) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
@@ -555,6 +584,86 @@ func (h *Handler) toggleReqStatus(w http.ResponseWriter, r *http.Request, doClos
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ===== Prototype endpoints =====
|
||||
|
||||
type createPrototypeReq struct {
|
||||
Content string `json:"content"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
func (h *Handler) createPrototype(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
|
||||
}
|
||||
var req createPrototypeReq
|
||||
if err := decodeBody(r, &req); err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
out, err := h.prototypes.Create(r.Context(), c, chi.URLParam(r, "slug"), num, CreatePrototypeInput{
|
||||
Content: req.Content, Note: req.Note,
|
||||
})
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusCreated, toPrototypeDTO(out))
|
||||
}
|
||||
|
||||
func (h *Handler) listPrototypes(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
|
||||
}
|
||||
out, err := h.prototypes.List(r.Context(), c, chi.URLParam(r, "slug"), num)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
dtos := make([]prototypeDTO, 0, len(out))
|
||||
for _, x := range out {
|
||||
dtos = append(dtos, toPrototypeDTO(x))
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"prototypes": dtos})
|
||||
}
|
||||
|
||||
func (h *Handler) getPrototype(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
|
||||
}
|
||||
out, err := h.prototypes.GetByVersion(r.Context(), c, chi.URLParam(r, "slug"), num, ver)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"prototype": toPrototypeDTO(out)})
|
||||
}
|
||||
|
||||
// ===== Issue endpoints =====
|
||||
|
||||
type createIssueReq struct {
|
||||
@@ -751,8 +860,10 @@ func (h *Handler) assignIssue(w http.ResponseWriter, r *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, toIssueDTO(out))
|
||||
}
|
||||
|
||||
func (h *Handler) closeIssue(w http.ResponseWriter, r *http.Request) { h.toggleIssueStatus(w, r, true) }
|
||||
func (h *Handler) reopenIssue(w http.ResponseWriter, r *http.Request) { h.toggleIssueStatus(w, r, false) }
|
||||
func (h *Handler) closeIssue(w http.ResponseWriter, r *http.Request) { h.toggleIssueStatus(w, r, true) }
|
||||
func (h *Handler) reopenIssue(w http.ResponseWriter, r *http.Request) {
|
||||
h.toggleIssueStatus(w, r, false)
|
||||
}
|
||||
|
||||
func (h *Handler) toggleIssueStatus(w http.ResponseWriter, r *http.Request, doClose bool) {
|
||||
c, err := h.callerFromCtx(r)
|
||||
|
||||
Reference in New Issue
Block a user