feat(收口): 补齐 8 月迭代缺口并同步 Spec 与证据链
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕 - 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败 - 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影 - 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序 - 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列 - 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名 - 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
This commit is contained in:
@@ -14,26 +14,26 @@ import (
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
// PriorityPollingHandler 处理卡轮询优先队列的人工入队与只读查询请求。
|
||||
// PriorityPollingHandler 处理卡轮询优先队列的人工入口(入队、关闭、重触发)与只读查询请求。
|
||||
type PriorityPollingHandler struct {
|
||||
enqueue *pollingSvc.PriorityEnqueueService
|
||||
manual *pollingSvc.PriorityManualService
|
||||
query *prioritypollingquery.Query
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// NewPriorityPollingHandler 创建卡轮询优先队列处理器。
|
||||
func NewPriorityPollingHandler(
|
||||
enqueue *pollingSvc.PriorityEnqueueService,
|
||||
manual *pollingSvc.PriorityManualService,
|
||||
query *prioritypollingquery.Query,
|
||||
validator *validator.Validate,
|
||||
) *PriorityPollingHandler {
|
||||
return &PriorityPollingHandler{enqueue: enqueue, query: query, validator: validator}
|
||||
return &PriorityPollingHandler{manual: manual, query: query, validator: validator}
|
||||
}
|
||||
|
||||
// Enqueue 人工优先入队。
|
||||
// POST /api/admin/polling-priority-items
|
||||
func (h *PriorityPollingHandler) Enqueue(c *fiber.Ctx) error {
|
||||
if h.enqueue == nil {
|
||||
if h.manual == nil {
|
||||
return errors.New(errors.CodeInternalError, "人工优先入队能力未配置")
|
||||
}
|
||||
var req dto.EnqueuePriorityPollingRequest
|
||||
@@ -43,7 +43,7 @@ func (h *PriorityPollingHandler) Enqueue(c *fiber.Ctx) error {
|
||||
if err := h.validator.Struct(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, validation.Message("人工优先入队参数不合法", &req, err))
|
||||
}
|
||||
result, err := h.enqueue.Enqueue(c.UserContext(), req.CardID, req.Reason, middleware.GetUserIDFromContext(c.UserContext()))
|
||||
result, err := h.manual.Enqueue(c.UserContext(), req.CardID, req.Reason, middleware.GetUserIDFromContext(c.UserContext()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -74,6 +74,84 @@ func (h *PriorityPollingHandler) Enqueue(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// Close 人工关闭未完成的优先轮询项。
|
||||
// 只允许超级管理员与平台账号;原因必填并写入审计,关闭后不再发起上游调用且保留既有执行结果与失败原因。
|
||||
// POST /api/admin/polling-priority-items/:id/close
|
||||
func (h *PriorityPollingHandler) Close(c *fiber.Ctx) error {
|
||||
if h.manual == nil {
|
||||
return errors.New(errors.CodeInternalError, "人工关闭优先轮询项能力未配置")
|
||||
}
|
||||
id, err := pathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req dto.ClosePriorityPollingItemRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数格式不正确")
|
||||
}
|
||||
if err := h.validator.Struct(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, validation.Message("人工关闭优先轮询项参数不合法", &req, err))
|
||||
}
|
||||
result, err := h.manual.Close(c.UserContext(), id, req.Reason, middleware.GetUserIDFromContext(c.UserContext()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, dto.ClosePriorityPollingItemResponse{
|
||||
ItemID: result.ItemID,
|
||||
CardID: result.CardID,
|
||||
TaskType: result.TaskType,
|
||||
TaskTypeName: constants.PollingPriorityTaskTypeName(result.TaskType),
|
||||
Status: result.Status,
|
||||
StatusName: constants.PollingPriorityStatusName(result.Status),
|
||||
DequeuedAt: result.DequeuedAt,
|
||||
Reason: result.Reason,
|
||||
})
|
||||
}
|
||||
|
||||
// Retrigger 人工重触发失败出队或已过期的优先轮询项。
|
||||
// 复用人工入队入口为该卡该任务类型建立新的活动项(尝试次数从零开始),原因必填并写入审计;
|
||||
// 权限与人工入队一致(含数据范围内的代理账号),不绕过活动项唯一键代表的并发上限。
|
||||
// POST /api/admin/polling-priority-items/:id/retrigger
|
||||
func (h *PriorityPollingHandler) Retrigger(c *fiber.Ctx) error {
|
||||
if h.manual == nil {
|
||||
return errors.New(errors.CodeInternalError, "人工重触发优先轮询项能力未配置")
|
||||
}
|
||||
id, err := pathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req dto.RetriggerPriorityPollingItemRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数格式不正确")
|
||||
}
|
||||
if err := h.validator.Struct(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, validation.Message("人工重触发优先轮询项参数不合法", &req, err))
|
||||
}
|
||||
result, err := h.manual.Retrigger(c.UserContext(), id, req.Reason, middleware.GetUserIDFromContext(c.UserContext()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
view := dto.RetriggerPriorityPollingItemResponse{
|
||||
SourceItemID: result.SourceItemID,
|
||||
CardID: result.Enqueue.CardID,
|
||||
Reason: result.Reason,
|
||||
}
|
||||
if len(result.Enqueue.Items) > 0 {
|
||||
// 重触发只针对来源项的任务类型,因此结果里至多一项;防御性取首项。
|
||||
item := result.Enqueue.Items[0]
|
||||
view.TaskType = item.TaskType
|
||||
view.TaskTypeName = constants.PollingPriorityTaskTypeName(item.TaskType)
|
||||
view.ItemID = item.ItemID
|
||||
view.Status = item.Status
|
||||
view.StatusName = constants.PollingPriorityStatusName(item.Status)
|
||||
view.AttemptCount = item.AttemptCount
|
||||
view.TriggerCount = item.TriggerCount
|
||||
view.LastTriggeredAt = item.LastTriggeredAt
|
||||
view.Created = item.Created
|
||||
}
|
||||
return response.Success(c, view)
|
||||
}
|
||||
|
||||
// ListPriorityItems 分页查询优先轮询项。
|
||||
// GET /api/admin/polling-priority-items
|
||||
func (h *PriorityPollingHandler) ListPriorityItems(c *fiber.Ctx) error {
|
||||
|
||||
Reference in New Issue
Block a user