feat: 完善 Rock 发货异常闭环与绑定判定

This commit is contained in:
yml
2026-11-14 23:58:57 +08:00
parent 9353b439a7
commit 6a87fe5228
45 changed files with 3497 additions and 389 deletions
+123 -4
View File
@@ -8,6 +8,7 @@ import (
"strings"
"affiliate_dash/internal/middleware"
"affiliate_dash/internal/model"
"affiliate_dash/internal/pkg/response"
"affiliate_dash/internal/service"
@@ -15,11 +16,12 @@ import (
)
type RockHandler struct {
rockSvc *service.RockService
rockSvc *service.RockService
deliverySvc *service.DeliveryService
}
func NewRockHandler(rockSvc *service.RockService) *RockHandler {
return &RockHandler{rockSvc: rockSvc}
func NewRockHandler(rockSvc *service.RockService, deliverySvc *service.DeliveryService) *RockHandler {
return &RockHandler{rockSvc: rockSvc, deliverySvc: deliverySvc}
}
// actorPtr 把 0 值用户 ID 转成 nil,便于审计字段留空。
@@ -493,6 +495,123 @@ func (h *RockHandler) ListDeliveryLogs(c *gin.Context) {
response.OK(c, gin.H{"list": logs})
}
func (h *RockHandler) ListTokenIncidents(c *gin.Context) {
idUint, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "无效的 Token ID")
return
}
list, err := h.rockSvc.ListRockTokenIncidents(c.Request.Context(), uint(idUint))
if err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"list": list})
}
func (h *RockHandler) ListIncidents(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
result, err := h.rockSvc.ListRockDeliveryIncidents(
c.Request.Context(),
strings.TrimSpace(c.Query("status")),
strings.TrimSpace(c.Query("category")),
page,
pageSize,
)
if err != nil {
response.ServerError(c, err.Error())
return
}
response.OK(c, gin.H{
"list": result.List,
"total": result.Total,
"page": page,
"page_size": pageSize,
})
}
type resolveIncidentReq struct {
Note string `json:"note"`
}
func (h *RockHandler) ResolveIncident(c *gin.Context) {
idUint, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || idUint == 0 {
response.BadRequest(c, "无效的异常记录 ID")
return
}
var req resolveIncidentReq
_ = c.ShouldBindJSON(&req)
if err := h.rockSvc.ResolveRockDeliveryIncident(
c.Request.Context(),
uint(idUint),
req.Note,
actorPtr(middleware.GetUserID(c)),
); err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"message": "异常记录已标记处理"})
}
type retryIncidentReq struct {
Note string `json:"note"`
}
func (h *RockHandler) RetryIncident(c *gin.Context) {
idUint, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || idUint == 0 {
response.BadRequest(c, "无效的异常记录 ID")
return
}
if h.deliverySvc == nil {
response.ServerError(c, "发货服务未初始化")
return
}
var req retryIncidentReq
_ = c.ShouldBindJSON(&req)
actor := actorPtr(middleware.GetUserID(c))
plan, err := h.rockSvc.ApproveRockIncidentRetry(c.Request.Context(), uint(idUint), req.Note, actor)
if err != nil {
response.BadRequest(c, err.Error())
return
}
result, retryErr := h.deliverySvc.SubmitForMerchant(
plan.Order.MerchantID,
0,
plan.Order.OrderNo,
plan.Token.ExpectedUID,
strconv.FormatUint(uint64(plan.Token.ID), 10),
)
if retryErr != nil {
if blocking, findErr := h.rockSvc.HasBlockingRockIncident(c.Request.Context(), plan.Order.OrderNo); findErr == nil && blocking == nil {
_, _ = h.rockSvc.RecordRockIncident(
c.Request.Context(),
&plan.Token,
plan.Order.OrderNo,
model.RockIncidentCategoryOther,
"人工重试失败: "+retryErr.Error(),
0,
"",
)
}
response.BadRequest(c, "人工重试失败: "+retryErr.Error())
return
}
if err := h.rockSvc.CompleteRockIncidentRetry(
c.Request.Context(),
plan.Token.ID,
plan.Order.OrderNo,
req.Note,
actor,
); err != nil {
response.ServerError(c, "发货已提交,但关闭异常记录失败: "+err.Error())
return
}
response.OK(c, result)
}
// ReleaseToken 把使用中的提货链接释放回可用库存;上游异常时需人工强制确认。
func (h *RockHandler) ReleaseToken(c *gin.Context) {
idUint, err := strconv.ParseUint(c.Param("id"), 10, 64)
@@ -522,7 +641,7 @@ func (h *RockHandler) ReleaseToken(c *gin.Context) {
response.OK(c, gin.H{"message": message})
}
// RefreshStaleTokens 一键刷新异常 Token(绑定错误 + 使用超时 + 绑定超时,同步执行,单次最多 20 条)。
// RefreshStaleTokens 一键刷新异常 Token(绑定错误 + 待绑定超时 + 绑定后未发货,同步执行,单次最多 20 条)。
func (h *RockHandler) RefreshStaleTokens(c *gin.Context) {
result, err := h.rockSvc.RefreshAllStaleTokens(c.Request.Context())
if err != nil {