package handler import ( "errors" "fmt" "strconv" "strings" "affiliate_dash/internal/middleware" "affiliate_dash/internal/pkg/response" "affiliate_dash/internal/service" "github.com/gin-gonic/gin" ) type RockHandler struct { rockSvc *service.RockService } func NewRockHandler(rockSvc *service.RockService) *RockHandler { return &RockHandler{rockSvc: rockSvc} } // GetConfig 获取 Rock 平台配置 func (h *RockHandler) GetConfig(c *gin.Context) { cfg, err := h.rockSvc.GetConfig(c.Request.Context()) if err != nil { response.ServerError(c, err.Error()) return } response.OK(c, cfg) } // UpdateConfig 更新 Rock 平台配置 func (h *RockHandler) UpdateConfig(c *gin.Context) { var req service.UpdateRockConfigReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数解析失败") return } cfg, err := h.rockSvc.UpdateConfig(c.Request.Context(), req) if err != nil { var validationErr *service.RockConfigValidationError if errors.As(err, &validationErr) { response.BadRequest(c, validationErr.Error()) return } response.ServerError(c, err.Error()) return } response.OK(c, cfg) } type testConnReq struct { BearerToken string `json:"bearer_token"` } // TestConnection 测试上游 Token 连通性 func (h *RockHandler) TestConnection(c *gin.Context) { var req testConnReq _ = c.ShouldBindJSON(&req) me, err := h.rockSvc.TestConnection(c.Request.Context(), req.BearerToken) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, me) } // GetStats 获取概览数据与库存统计 func (h *RockHandler) GetStats(c *gin.Context) { stats, err := h.rockSvc.GetStats(c.Request.Context()) if err != nil { response.ServerError(c, err.Error()) return } response.OK(c, stats) } // RefreshDashboard 拉取上游 dashboard 并同步最新采购单价。 func (h *RockHandler) RefreshDashboard(c *gin.Context) { result, err := h.rockSvc.RefreshDashboard(c.Request.Context()) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, result) } type purchaseReq struct { Count int `json:"count"` } // PurchaseTokens 手动采购并入库 Token func (h *RockHandler) PurchaseTokens(c *gin.Context) { var req purchaseReq if err := c.ShouldBindJSON(&req); err != nil || req.Count <= 0 { req.Count = 50 } tokens, err := h.rockSvc.PurchaseTokens(c.Request.Context(), req.Count, false) if err != nil { response.BadRequest(c, err.Error()) return } summary := h.rockSvc.LastPurchaseSummary() message := "采购成功,已批量入库" planned := len(tokens) limitReason := "" if summary != nil { planned = summary.PlannedCount limitReason = summary.LimitReason switch summary.LimitReason { case "upstream_stock": message = fmt.Sprintf("上游货源不足,已按 %d 个采购", summary.PlannedCount) case "points": message = fmt.Sprintf("积分不足,已按 %d 个采购", summary.PlannedCount) case "points_and_stock": message = fmt.Sprintf("积分与上游库存不足,已按 %d 个采购", summary.PlannedCount) case "points_snapshot": message = fmt.Sprintf("积分不足(按快照估算),已按 %d 个采购", summary.PlannedCount) } } response.OK(c, gin.H{ "purchased_count": len(tokens), "requested_count": req.Count, "planned_count": planned, "limit_reason": limitReason, "log_id": summaryLogID(summary), "message": message, }) } func summaryLogID(summary *service.PurchaseSummary) uint { if summary == nil { return 0 } return summary.LogID } // ListPurchaseLogs 分页查询采购流水 func (h *RockHandler) ListPurchaseLogs(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) triggerType := strings.TrimSpace(c.Query("trigger_type")) status := strings.TrimSpace(c.Query("status")) list, total, err := h.rockSvc.ListPurchaseLogs(c.Request.Context(), page, pageSize, triggerType, status) if err != nil { response.ServerError(c, err.Error()) return } response.OK(c, gin.H{"list": list, "total": total}) } // GetPurchaseLog 查询单次采购详情(含各阶段原始报文) func (h *RockHandler) GetPurchaseLog(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil || id == 0 { response.BadRequest(c, "无效的日志 ID") return } entry, stages, err := h.rockSvc.GetPurchaseLog(c.Request.Context(), uint(id)) if err != nil { response.NotFound(c, "采购日志不存在") return } response.OK(c, gin.H{"log": entry, "stages": stages}) } type importAllPackagesReq struct { MaxPackages int `json:"max_packages"` } // ImportAllUpstreamPackages 全量导入上游包:已导入的跳过,只导入未绑定的 Token func (h *RockHandler) ImportAllUpstreamPackages(c *gin.Context) { var req importAllPackagesReq _ = c.ShouldBindJSON(&req) result, err := h.rockSvc.ImportAllUpstreamPackages(c.Request.Context(), req.MaxPackages, middleware.GetUserID(c)) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, result) } // ListTokens 分页获取 Token 库存列表 func (h *RockHandler) ListTokens(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) status := strings.TrimSpace(c.Query("status")) search := strings.TrimSpace(c.Query("search")) tokens, total, err := h.rockSvc.ListTokens(c.Request.Context(), page, pageSize, status, search) if err != nil { response.ServerError(c, err.Error()) return } response.OK(c, gin.H{ "list": tokens, "total": total, "page": page, "page_size": pageSize, }) } type correctUIDReq struct { ExpectedUID string `json:"expected_uid"` } // CorrectTokenUID 修改绑定错误订单上买家填写的 UID,不释放提货号。 func (h *RockHandler) CorrectTokenUID(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } var req correctUIDReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数解析失败") return } token, err := h.rockSvc.CorrectMismatchExpectedUID(c.Request.Context(), uint(idUint), req.ExpectedUID) if err != nil { response.BadRequest(c, err.Error()) return } message := "已更新买家 UID,提货号仍属于原订单" if token.Status == "bound" { message = "已改成扫码 UID,绑定阻断已解除,提货号仍属于原订单" } response.OK(c, gin.H{ "message": message, "status": token.Status, "token": token, }) } // ResetToken 客服人工重置 mismatch 状态的 Token func (h *RockHandler) ResetToken(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } if err := h.rockSvc.ResetMismatchToken(c.Request.Context(), uint(idUint)); err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, gin.H{"message": "重置成功,Token 已重新释放至可用库存"}) } // ReleaseToken 把尚未扫码绑定的使用中链接释放回可用库存 func (h *RockHandler) ReleaseToken(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } if err := h.rockSvc.ReleaseUnboundUsingToken(c.Request.Context(), uint(idUint)); err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, gin.H{"message": "已释放回可用库存,该提货链接可以再次分配"}) } // RefreshBinding 人工刷新指定 Token 的上游绑定状态 func (h *RockHandler) RefreshBinding(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } result, err := h.rockSvc.RefreshTokenBinding(c.Request.Context(), uint(idUint)) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, result) } // TokenPackageLink 返回 Token 所属上游包页面的定位链接 func (h *RockHandler) TokenPackageLink(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } result, err := h.rockSvc.TokenPackageLink(c.Request.Context(), uint(idUint)) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, result) } type updateRemarkReq struct { Remark string `json:"remark"` Status string `json:"status"` // 可选:比如 abnormal } // UpdateRemark 更新 Token 备注并可联动更新状态 func (h *RockHandler) UpdateRemark(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } var req updateRemarkReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "参数解析失败") return } token, err := h.rockSvc.UpdateTokenRemark(c.Request.Context(), uint(idUint), req.Remark, req.Status) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, gin.H{ "message": "备注更新成功", "token": token, }) } type markAbnormalReq struct { Remark string `json:"remark"` } // MarkAbnormal 快捷标记为上报失效(作废,不可再用) func (h *RockHandler) MarkAbnormal(c *gin.Context) { idUint, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "无效的 Token ID") return } var req markAbnormalReq _ = c.ShouldBindJSON(&req) token, err := h.rockSvc.MarkTokenAbnormal(c.Request.Context(), uint(idUint), req.Remark) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, gin.H{ "message": "已标记为上报失效,该提货链接已作废", "token": token, }) }