package pickup import ( "errors" "net/http" "strings" "hfb_sys/backend/internal/auditlog" "hfb_sys/backend/internal/middleware" "hfb_sys/backend/pkg/response" "github.com/gin-gonic/gin" ) type Handler struct { service *Service } func NewHandler(service *Service) *Handler { return &Handler{service: service} } // Create 创建提号订单(管理员) func (h *Handler) Create(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } var req CreateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数格式错误") return } item, err := h.service.Create(c.Request.Context(), req, adminID, auditMeta(c)) if err != nil { writePickupError(c, err) return } response.OK(c, item) } // Complete 完成提号(管理员) func (h *Handler) Complete(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } id, ok := parseID(c) if !ok { return } var req CompleteRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "结算金额不正确") return } item, err := h.service.Complete(c.Request.Context(), id, req, adminID, auditMeta(c)) if err != nil { writePickupError(c, err) return } response.OK(c, item) } // MarkOfflineSettlement 确认平台代管提号已完成实际线下打款(管理员)。 func (h *Handler) MarkOfflineSettlement(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } id, ok := parseID(c) if !ok { return } var req OfflineSettlementRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "线下结算备注格式不正确") return } item, err := h.service.MarkOfflineSettlement(c.Request.Context(), id, req, adminID, auditMeta(c)) if err != nil { writePickupError(c, err) return } response.OK(c, item) } // UpdateProfit 修改提号中的线下利润(管理员) func (h *Handler) UpdateProfit(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } id, ok := parseID(c) if !ok { return } var req UpdateProfitRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "利润金额不正确") return } item, err := h.service.UpdateProfit(c.Request.Context(), id, req, adminID, auditMeta(c)) if err != nil { writePickupError(c, err) return } response.OK(c, item) } // CreateFinancialAdjustment 为已完成提号创建利润或打款调整。 func (h *Handler) CreateFinancialAdjustment(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } id, ok := parseID(c) if !ok { return } var req FinancialAdjustmentRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "调整金额或原因不正确") return } item, err := h.service.CreateFinancialAdjustment(c.Request.Context(), id, req, adminID, auditMeta(c)) if err != nil { writePickupError(c, err) return } response.OK(c, item) } // ListFinancialAdjustments 返回提号完成后的财务调整历史。 func (h *Handler) ListFinancialAdjustments(c *gin.Context) { id, ok := parseID(c) if !ok { return } items, err := h.service.ListFinancialAdjustments(c.Request.Context(), id) if err != nil { writePickupError(c, err) return } response.OK(c, items) } func (h *Handler) ProcessEvents(c *gin.Context) { id, ok := parseID(c) if !ok { return } items, err := h.service.ProcessEvents(c.Request.Context(), id) if err != nil { writePickupError(c, err) return } response.OK(c, gin.H{"items": items}) } // SettleFinancialAdjustment 确认代管提号的补款或追回调整已线下处理。 func (h *Handler) SettleFinancialAdjustment(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } id, ok := parseID(c) if !ok { return } var req FinancialAdjustmentSettlementRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "确认备注格式不正确") return } if err := h.service.SettleFinancialAdjustment(c.Request.Context(), id, req, adminID, auditMeta(c)); err != nil { writePickupError(c, err) return } response.OK(c, gin.H{"settled": true}) } // Cancel 取消提号(管理员) func (h *Handler) Cancel(c *gin.Context) { adminID, ok := currentAdminID(c) if !ok { response.Unauthorized(c, "缺少管理员上下文") return } id, ok := parseID(c) if !ok { return } var req CancelRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "取消原因不能为空") return } if err := h.service.Cancel(c.Request.Context(), id, req.Reason, adminID, auditMeta(c)); err != nil { writePickupError(c, err) return } response.OK(c, gin.H{"cancelled": true}) } // List 提号订单列表(管理员) func (h *Handler) List(c *gin.Context) { query := parseAdminQuery(c) result, err := h.service.ListAdmin(c.Request.Context(), query) if err != nil { writePickupError(c, err) return } response.OK(c, result) } // ShopOptions 返回后台提号已使用的店铺名称。 func (h *Handler) ShopOptions(c *gin.Context) { items, err := h.service.ListShopNames(c.Request.Context(), c.Query("platform")) if err != nil { writePickupError(c, err) return } response.OK(c, items) } // Detail 提号订单详情(管理员) func (h *Handler) Detail(c *gin.Context) { id, ok := parseID(c) if !ok { return } item, err := h.service.FindByID(c.Request.Context(), id) if err != nil { writePickupError(c, err) return } response.OK(c, item) } // AvailableListings 可提号的 listing 搜索(管理员) func (h *Handler) AvailableListings(c *gin.Context) { page, pageSize := parsePagination(c) result, err := h.service.ListAvailableListings(c.Request.Context(), AvailableListingQuery{ Keyword: strings.TrimSpace(c.Query("keyword")), SourceType: strings.TrimSpace(c.Query("source_type")), Page: page, PageSize: pageSize, }) if err != nil { writePickupError(c, err) return } response.OK(c, result) } // ListForSeller 卖家自己的提号记录 func (h *Handler) ListForSeller(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } query := parseSellerQuery(c) result, err := h.service.ListForSeller(c.Request.Context(), userID, query) if err != nil { writePickupError(c, err) return } response.OK(c, result) } func writePickupError(c *gin.Context, err error) { response.RecordError(c, err) switch { case errors.Is(err, ErrDependencyUnavailable): response.ServiceUnavailable(c, "数据库未连接") case errors.Is(err, ErrListingUnavailable): response.BadRequest(c, "账号不可用或已在交易中") case errors.Is(err, ErrDuplicatePickup): response.BadRequest(c, "该账号已有进行中的提号订单") case errors.Is(err, ErrPickupNotFound): response.Error(c, http.StatusNotFound, "pickup_not_found", "提号订单不存在") case errors.Is(err, ErrPickupNotPickingUp): response.BadRequest(c, "提号订单状态不允许该操作") case errors.Is(err, ErrInvalidAmount): response.BadRequest(c, "结算金额不正确") case errors.Is(err, ErrInvalidProfit): response.BadRequest(c, "利润金额不正确") case errors.Is(err, ErrOfflineSettlementCannotMark): response.BadRequest(c, "当前提号不可确认线下结算") case errors.Is(err, ErrFinancialAdjustmentInvalid): response.BadRequest(c, "调整金额、状态或原因不正确") case errors.Is(err, ErrFinancialAdjustmentNotFound): response.Error(c, http.StatusNotFound, "pickup_financial_adjustment_not_found", "财务调整记录不存在") case errors.Is(err, ErrFinancialAdjustmentCannotSettle): response.BadRequest(c, "当前调整不可确认处理") default: response.Error(c, http.StatusInternalServerError, "pickup_error", err.Error()) } } func auditMeta(c *gin.Context) auditlog.Meta { return auditlog.Meta{ IP: c.ClientIP(), UserAgent: c.GetHeader("User-Agent"), RequestID: middleware.GetRequestID(c), } }