- 做了什么: 1. 数据库新增 013_rock_tokens_remark.sql 迁移,rock_tokens 表补充 remark 字段并在 ListTokens 中支持备注模糊检索; 2. 后端扩展 UpdateTokenRemark、MarkTokenAbnormal 接口,同时优化 ResetMismatchToken 支持 mismatch 与 abnormal 状态的解冻重置; 3. 前端 RockTokenTable 新增「流转时间」列(入库/绑错/发货/绑定/提货)与「备注」列(TokenRemarkCell 浮窗快捷预设选择与自定义输入); 4. 快捷选择「已上报」或点击「上报失效」自动将链接标记为 abnormal 作废,从可用库存池中剔除,防止被新订单领走; 5. 优化操作列与顶部筛选栏:mismatch 增加「上报失效」与警示重置;abnormal 状态明确显示已失效并提供强制恢复;筛选栏新增「已上报失效」Tab。 - 为什么: 解决绑定错误上报 0868 上游后无法记录备注、且原「解冻重置」容易误将已作废卡密放回可用库存池导致后续订单报错的问题。 - 验证方式: 1. 后端单元测试 TestRockService_TokenRemarkAndAbnormal 与 TestRock 测试集通过; 2. make fmt、go vet 通过; 3. 前端 oxlint 检查通过 (0 errors, 0 warnings),npm run build 生产构建通过。
224 lines
5.9 KiB
Go
224 lines
5.9 KiB
Go
package handler
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"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 {
|
|
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)
|
|
}
|
|
|
|
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
|
|
}
|
|
response.OK(c, gin.H{
|
|
"purchased_count": len(tokens),
|
|
"message": "采购成功,已批量入库",
|
|
})
|
|
}
|
|
|
|
// 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": "已释放回可用库存,该提货链接可以再次分配"})
|
|
}
|
|
|
|
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,
|
|
})
|
|
}
|