105 lines
3.5 KiB
Go
105 lines
3.5 KiB
Go
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)
|
|
}
|