package handler import ( "encoding/json" "net/http" "strconv" "affiliate_dash/internal/middleware" "affiliate_dash/internal/model" "affiliate_dash/internal/pkg/response" "affiliate_dash/internal/service" "github.com/gin-gonic/gin" ) // OpenV1Handler 提供面向商户系统和履约器的通用开放接口。 type OpenV1Handler struct { merchantSvc *service.MerchantService fulfillmentSvc *service.FulfillmentService } func NewOpenV1Handler(merchantSvc *service.MerchantService, fulfillmentSvc *service.FulfillmentService) *OpenV1Handler { return &OpenV1Handler{merchantSvc: merchantSvc, fulfillmentSvc: fulfillmentSvc} } func (h *OpenV1Handler) ListProducts(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("size", "20")) list, total, err := h.merchantSvc.ListMerchantProducts(middleware.GetMerchantID(c), page, size, true) if err != nil { response.ServerError(c, err.Error()) return } response.Page(c, list, total, page, size) } type openCreateOrderReq struct { ClientOrderNo string `json:"client_order_no" binding:"required"` SKU string `json:"sku" binding:"required"` Quantity int64 `json:"quantity"` BuyerReference string `json:"buyer_reference"` Data json.RawMessage `json:"data"` } func (h *OpenV1Handler) CreateOrder(c *gin.Context) { var req openCreateOrderReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数错误:client_order_no 与 sku 必填") return } if key := c.GetHeader("Idempotency-Key"); key != "" && key != req.ClientOrderNo { response.BadRequest(c, "Idempotency-Key 必须与 client_order_no 一致") return } var data interface{} if len(req.Data) > 0 { var decoded interface{} if err := json.Unmarshal(req.Data, &decoded); err != nil { response.BadRequest(c, "data 必须是有效 JSON") return } data = decoded } client := middleware.GetAPIClient(c) result, err := h.fulfillmentSvc.CreateOrder(service.CreateFulfillmentOrderInput{ MerchantID: middleware.GetMerchantID(c), APIClientID: client.ID, ClientOrderNo: req.ClientOrderNo, SKU: req.SKU, Quantity: req.Quantity, BuyerReference: req.BuyerReference, RequestData: data, }) if err != nil { response.BadRequest(c, err.Error()) return } status := http.StatusOK if !result.Idempotent { status = http.StatusCreated } c.JSON(status, response.Body{ Code: 0, Message: "ok", Data: gin.H{ "order": buildOpenOrderResponse(result.Order), "idempotent": result.Idempotent, }, }) } func (h *OpenV1Handler) QueryOrder(c *gin.Context) { order, err := h.fulfillmentSvc.GetOrder(middleware.GetMerchantID(c), c.Param("order_no")) if err != nil { if err.Error() == "订单不存在" { response.NotFound(c, err.Error()) return } response.ServerError(c, err.Error()) return } response.OK(c, buildOpenOrderResponse(order)) } type openCancelOrderReq struct { Reason string `json:"reason"` } func (h *OpenV1Handler) CancelOrder(c *gin.Context) { var req openCancelOrderReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数错误") return } client := middleware.GetAPIClient(c) order, err := h.fulfillmentSvc.CancelOrder(middleware.GetMerchantID(c), client.ID, c.Param("order_no"), req.Reason) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, buildOpenOrderResponse(order)) } type openShipNotifyReq struct { OrderNo string `json:"order_no"` ShipStatus string `json:"ship_status" binding:"required"` ProviderOrderNo string `json:"provider_order_no"` FailReason string `json:"fail_reason"` Result json.RawMessage `json:"result"` } func (h *OpenV1Handler) ShipNotify(c *gin.Context) { var req openShipNotifyReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数错误:ship_status 必填") return } status := "" switch req.ShipStatus { case "processing": status = model.FulfillmentStatusProcessing case "success": status = model.FulfillmentStatusSucceeded case "failed": status = model.FulfillmentStatusFailed default: response.BadRequest(c, "ship_status 仅支持 processing、success、failed") return } var result interface{} if len(req.Result) > 0 { if err := json.Unmarshal(req.Result, &result); err != nil { response.BadRequest(c, "result 必须是有效 JSON") return } } client := middleware.GetAPIClient(c) orderNo := c.Param("order_no") if orderNo == "" { orderNo = req.OrderNo } if orderNo == "" { response.BadRequest(c, "order_no 必填") return } order, err := h.fulfillmentSvc.UpdateFulfillment(service.FulfillmentUpdateInput{ MerchantID: middleware.GetMerchantID(c), APIClientID: client.ID, OrderNo: orderNo, Status: status, ProviderOrderNo: req.ProviderOrderNo, FailureReason: req.FailReason, ResultData: result, }) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, buildOpenOrderResponse(order)) } func (h *OpenV1Handler) GetWallet(c *gin.Context) { wallet, err := h.fulfillmentSvc.GetWallet(middleware.GetMerchantID(c)) if err != nil { response.ServerError(c, err.Error()) return } response.OK(c, wallet) } func buildOpenOrderResponse(order *model.FulfillmentOrder) gin.H { canFulfill, reason := service.CanFulfill(order) data := gin.H{ "order_no": order.OrderNo, "client_order_no": order.ClientOrderNo, "payment_status": order.PaymentStatus, "fulfillment_status": order.FulfillmentStatus, "can_fulfill": canFulfill, "cannot_fulfill_reason": reason, "product": gin.H{ "sku": order.ProductSKU, "name": order.ProductName, }, "quantity": order.Quantity, "base_amount": order.BaseAmount, "fee_type": order.FeeType, "service_fee_amount": order.ServiceFeeAmount, "amount": order.Amount, "currency": order.Currency, "buyer_reference": order.BuyerReference, "provider_order_no": order.ProviderOrderNo, "failure_reason": order.FailureReason, "created_at": order.CreatedAt, "delivered_at": order.DeliveredAt, "cancelled_at": order.CancelledAt, } if json.Valid([]byte(order.RequestData)) { data["data"] = json.RawMessage(order.RequestData) } if json.Valid([]byte(order.ResultData)) { data["result"] = json.RawMessage(order.ResultData) } return data }