package api import ( "errors" "net/http" "strconv" "github.com/gin-gonic/gin" "ws_usernode/internal/config" "ws_usernode/internal/model" "ws_usernode/internal/service" ) // ApprovalHandler 新账号申请与审批接口(PLAN F4): // 公开提交(POST /approvals)、管理员列表(GET /approvals)、审批(POST /approvals/:id/review)。 type ApprovalHandler struct { svc *service.ApprovalService cfg *config.Config h *Handler // 访问审计 helper } // ApprovalSubmitRequest 提交申请(用户名不含 ext_ 前缀)。 type ApprovalSubmitRequest struct { Username string `json:"username" binding:"required"` Email string `json:"email" binding:"required"` Supervisor string `json:"supervisor"` Purpose string `json:"purpose"` } // Submit POST /approvals —— 公开提交新账号申请。 func (h *ApprovalHandler) Submit(c *gin.Context) { var req ApprovalSubmitRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, "请求参数不合法: "+err.Error()) return } a, err := h.svc.Submit(c.Request.Context(), req.Username, req.Email, req.Supervisor, req.Purpose) if err != nil { h.h.audit(c, "approval.submit", "approval", "", map[string]any{"username": req.Username, "err": err.Error()}, model.ResultFailed) switch { case errors.Is(err, service.ErrUsernameTaken): fail(c, http.StatusConflict, err.Error()) default: fail(c, http.StatusBadRequest, err.Error()) } return } h.h.audit(c, "approval.submit", "approval", strconv.FormatUint(uint64(a.ID), 10), map[string]any{"username": a.UsernameRequested}, model.ResultSuccess) ok(c, a) } // List GET /approvals —— 申请单列表(admin),按状态筛选。 func (h *ApprovalHandler) List(c *gin.Context) { rows, err := h.svc.List(c.Request.Context(), c.Query("status")) if err != nil { fail(c, http.StatusInternalServerError, err.Error()) return } ok(c, gin.H{"items": rows}) } // ApprovalReviewRequest 审批请求:通过 → approve=true;拒绝 → approve=false + 理由。 type ApprovalReviewRequest struct { Approve bool `json:"approve"` Reason string `json:"reason"` } // Review POST /approvals/:id/review —— 审批(通过 → 自动建号 / 拒绝 + 理由)。 func (h *ApprovalHandler) Review(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { fail(c, http.StatusBadRequest, "无效的申请单 ID") return } var req ApprovalReviewRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, "请求参数不合法: "+err.Error()) return } reviewerID := uint(0) if sess := sessionFrom(c); sess != nil { reviewerID = sess.RefID } a, err := h.svc.Review(c.Request.Context(), uint(id), req.Approve, reviewerID, req.Reason) if err != nil { h.h.audit(c, "approval.review", "approval", c.Param("id"), map[string]any{"approve": req.Approve, "err": err.Error()}, model.ResultFailed) switch { case errors.Is(err, service.ErrApprovalNotFound): fail(c, http.StatusNotFound, err.Error()) case errors.Is(err, service.ErrApprovalReviewed): fail(c, http.StatusConflict, err.Error()) case errors.Is(err, service.ErrReasonRequired): fail(c, http.StatusBadRequest, err.Error()) case errors.Is(err, service.ErrUsernameTaken): fail(c, http.StatusConflict, err.Error()) default: fail(c, http.StatusInternalServerError, err.Error()) } return } h.h.audit(c, "approval.review", "approval", c.Param("id"), map[string]any{"approve": req.Approve, "reason": req.Reason, "status": a.Status}, model.ResultSuccess) ok(c, a) }