Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕 - 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败 - 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影 - 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序 - 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列 - 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名 - 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
78 lines
3.2 KiB
Go
78 lines
3.2 KiB
Go
package task
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog"
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
)
|
|
|
|
// newGatewayAttempt 只在内存中准备稳定关联标识;成功无变化的轮询不落库。
|
|
func newGatewayAttempt(cardID uint, operation, scene string) integrationlog.Attempt {
|
|
resourceID := strconv.FormatUint(uint64(cardID), 10)
|
|
integrationID := uuid.NewString()
|
|
triggerSource := constants.CardObservationSourcePolling
|
|
triggerScene := scene
|
|
return integrationlog.Attempt{IntegrationID: integrationID,
|
|
Provider: constants.IntegrationProviderGateway, Direction: constants.IntegrationDirectionOutbound,
|
|
Operation: operation, ResourceType: constants.AssetTypeIotCard, ResourceID: &resourceID,
|
|
TriggerSource: &triggerSource, TriggerScene: &triggerScene, TriggerSeries: &integrationID,
|
|
CorrelationID: &integrationID, RequestSummary: map[string]any{"card_id": cardID},
|
|
Metadata: map[string]any{"scene": scene}}
|
|
}
|
|
|
|
// recordGatewayAttempt 在结果明确后才写入外部交互日志。
|
|
func recordGatewayAttempt(ctx context.Context, repository *integrationlog.Repository, attempt integrationlog.Attempt, result string, changed bool, summary string) error {
|
|
if repository == nil {
|
|
return errors.New(errors.CodeInternalError, "Gateway Integration Log 未配置")
|
|
}
|
|
attempt.InitialResult = result
|
|
attempt.StateChanged = changed
|
|
attempt.Metadata = map[string]any{"scene": attempt.TriggerScene, "summary": summary}
|
|
_, err := repository.Start(ctx, attempt)
|
|
return err
|
|
}
|
|
|
|
// recordPriorityGatewayAttempt 记录外部交互日志,并把真正落库的日志标识回写优先项。
|
|
// 成功且无变化的轮询按既有语义不落库,因此只有落库成功才回写标识,避免悬挂引用;
|
|
// 其余调用方(无优先项在途)继续使用 recordGatewayAttempt。
|
|
func recordPriorityGatewayAttempt(
|
|
ctx context.Context,
|
|
repository *integrationlog.Repository,
|
|
attempt integrationlog.Attempt,
|
|
result string,
|
|
changed bool,
|
|
summary string,
|
|
round *pollingPriorityRound,
|
|
) error {
|
|
if err := recordGatewayAttempt(ctx, repository, attempt, result, changed, summary); err != nil {
|
|
return err
|
|
}
|
|
round.RecordIntegrationLog(attempt.IntegrationID)
|
|
return nil
|
|
}
|
|
|
|
// completeGatewayAttempt 记录 Gateway 查询成功或明确失败,不把响应正文写入日志。
|
|
func completeGatewayAttempt(ctx context.Context, repository *integrationlog.Repository, attempt *model.IntegrationLog, success bool, startedAt time.Time) error {
|
|
if repository == nil || attempt == nil {
|
|
return errors.New(errors.CodeInternalError, "Gateway Integration Log 尝试不存在")
|
|
}
|
|
completionCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
|
|
defer cancel()
|
|
result := constants.IntegrationResultFailed
|
|
if success {
|
|
result = constants.IntegrationResultSuccess
|
|
}
|
|
_, err := repository.Complete(completionCtx, attempt.IntegrationID, integrationlog.Completion{
|
|
Result: result, DurationMS: time.Since(startedAt).Milliseconds(), StateChanged: false,
|
|
ResponseSummary: map[string]any{"success": success},
|
|
})
|
|
return err
|
|
}
|