收口七月卡状态回调与系列授权兼容契约
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
完成运营商实名回调、业务事件观测序列与受控配置装配,同时恢复 UR43 已交付的 packages[].remove 字段及旧响应兼容,统一更新 OpenSpec、OpenAPI 和交付文档。 Constraint: 七月测试环境里程碑不新增或运行自动化测试 Rejected: 以必填 operation_type 替换 packages[].remove | 会破坏已交付前端契约 Confidence: high Scope-risk: broad Directive: 后续修改系列套餐管理接口必须保持 packages[].remove 和 ShopSeriesGrantResponse 兼容 Tested: go run ./cmd/gendocs;go build -buildvcs=false ./...;openspec validate complete-july-iteration-test-release --strict;git diff --check Not-tested: 按本 Change 约定未运行 go test,真实运营商与 Gateway 联调延期
This commit is contained in:
@@ -30,7 +30,7 @@ const (
|
||||
CardObservationSeriesAttemptCount = 3 // 每个序列固定尝试次数
|
||||
CardObservationSeriesTTL = 7 * time.Minute // 活跃序列覆盖最后一次任务及缓冲
|
||||
CardObservationSeriesResultTTL = 24 * time.Hour // 尝试幂等与提前完成结果保留时间
|
||||
CardObservationGatewayLockTTL = 70 * time.Second // Gateway 请求互斥超时加安全余量
|
||||
CardObservationGatewayLockTTL = 16 * time.Minute // 覆盖 300 秒超时、两次网络重试及安全余量
|
||||
CardObservationGatewayMinInterval = 10 * time.Second // 未配置时的运营商最小请求间隔
|
||||
)
|
||||
|
||||
@@ -64,27 +64,46 @@ const (
|
||||
|
||||
// Gateway 轮询 Integration Log 操作编码。
|
||||
const (
|
||||
IntegrationProviderGateway = "gateway" // Gateway 外部系统
|
||||
IntegrationOperationGatewayRealname = "query_realname_status" // 查询实名状态
|
||||
IntegrationOperationGatewayTraffic = "query_flow" // 查询流量
|
||||
IntegrationOperationGatewayNetwork = "query_card_status" // 查询网络状态
|
||||
IntegrationProviderGateway = "gateway" // Gateway 外部系统
|
||||
IntegrationOperationGatewayRealname = "query_realname_status" // 查询实名状态
|
||||
IntegrationOperationGatewayTraffic = "query_flow" // 查询流量
|
||||
IntegrationOperationGatewayNetwork = "query_card_status" // 查询网络状态
|
||||
IntegrationOperationGatewayDeviceInfo = "query_device_info" // 查询设备信息
|
||||
)
|
||||
|
||||
// 卡实名观测场景
|
||||
const (
|
||||
CardObservationSceneRealnamePolling = "realname_polling" // 实名轮询
|
||||
CardObservationSceneTrafficPolling = "traffic_polling" // 流量轮询
|
||||
CardObservationSceneNetworkPolling = "network_polling" // 网络状态轮询
|
||||
CardObservationSceneManualRefresh = "manual_refresh" // 手动刷新
|
||||
CardObservationSceneCarrierCallback = "carrier_realname_callback" // 运营商实名回调
|
||||
CardObservationSceneRealnamePolling = "realname_polling" // 实名轮询
|
||||
CardObservationSceneTrafficPolling = "traffic_polling" // 流量轮询
|
||||
CardObservationSceneNetworkPolling = "network_polling" // 网络状态轮询
|
||||
CardObservationSceneManualRefresh = "manual_refresh" // 手动刷新
|
||||
CardObservationSceneCarrierCallback = "carrier_realname_callback" // 运营商实名回调
|
||||
CardObservationSceneClientAssetRead = "client_asset_read" // C 端资产详情读取
|
||||
CardObservationSceneAdminAssetRead = "admin_asset_realtime" // 后台资产实时状态读取
|
||||
CardObservationSceneOpenCardTraffic = "openapi_card_traffic" // OpenAPI 卡流量读取
|
||||
CardObservationSceneOpenCardNetwork = "openapi_card_network" // OpenAPI 卡网络读取
|
||||
CardObservationSceneOpenCardRealname = "openapi_card_realname" // OpenAPI 卡实名读取
|
||||
CardObservationSceneOpenDeviceTraffic = "openapi_device_traffic" // OpenAPI 设备流量读取
|
||||
CardObservationSceneClientRealnameLink = "client_realname_link" // C 端获取实名链接
|
||||
CardObservationSceneAdminRealnameLink = "admin_realname_link" // 后台获取实名链接
|
||||
CardObservationSceneBusinessStop = "business_stop" // 业务停机成功
|
||||
CardObservationSceneBusinessResume = "business_resume" // 业务复机成功
|
||||
CardObservationScenePackageChanged = "package_changed" // 购包或套餐激活成功
|
||||
CardObservationSceneDeviceSwitchCard = "device_switch_card" // 设备切卡成功
|
||||
CardObservationSceneDeviceSwitchMode = "device_switch_mode" // 设备切卡模式成功
|
||||
CardObservationSceneDeviceReboot = "device_reboot" // 设备重启成功
|
||||
CardObservationSceneDeviceReset = "device_reset" // 设备恢复出厂成功
|
||||
CardObservationSceneDeviceSetWiFi = "device_set_wifi" // 设备 WiFi 设置成功
|
||||
)
|
||||
|
||||
// 卡实名状态变更 Outbox 事件
|
||||
const (
|
||||
OutboxEventTypeCardRealnameChanged = "card.realname.changed" // 卡实名状态变化
|
||||
CardRealnameChangedPayloadVersionV1 = 1 // 卡实名状态变化载荷版本
|
||||
OutboxEventTypeCardTrafficIncremented = "card.traffic.incremented" // 卡流量正增量
|
||||
CardTrafficIncrementedPayloadVersionV1 = 1 // 卡流量正增量载荷版本
|
||||
OutboxEventTypeCardNetworkChanged = "card.network.changed" // 卡网络状态变化
|
||||
CardNetworkChangedPayloadVersionV1 = 1 // 卡网络状态变化载荷版本
|
||||
OutboxEventTypeCardRealnameChanged = "card.realname.changed" // 卡实名状态变化
|
||||
CardRealnameChangedPayloadVersionV1 = 1 // 卡实名状态变化载荷版本
|
||||
OutboxEventTypeCardTrafficIncremented = "card.traffic.incremented" // 卡流量正增量
|
||||
CardTrafficIncrementedPayloadVersionV1 = 1 // 卡流量正增量载荷版本
|
||||
OutboxEventTypeCardNetworkChanged = "card.network.changed" // 卡网络状态变化
|
||||
CardNetworkChangedPayloadVersionV1 = 1 // 卡网络状态变化载荷版本
|
||||
OutboxEventTypeCardSeriesRequested = "card.observation.series.requested" // 业务成功请求卡观测序列
|
||||
CardSeriesRequestedPayloadVersionV1 = 1 // 业务成功观测序列载荷版本
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package constants
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// IntegrationDirectionInbound 表示外部系统调用本系统。
|
||||
IntegrationDirectionInbound = "inbound"
|
||||
@@ -7,6 +9,25 @@ const (
|
||||
IntegrationDirectionOutbound = "outbound"
|
||||
)
|
||||
|
||||
const (
|
||||
// IntegrationProviderCTCC 表示中国电信回调提供方。
|
||||
IntegrationProviderCTCC = "ctcc"
|
||||
// IntegrationOperationCTCCRealnameCallback 表示中国电信实名结果回调。
|
||||
IntegrationOperationCTCCRealnameCallback = "realname_callback"
|
||||
// IntegrationProviderCMCC 表示中国移动回调提供方。
|
||||
IntegrationProviderCMCC = "cmcc"
|
||||
// IntegrationOperationCMCCRealnameCallback 表示中国移动实名结果回调。
|
||||
IntegrationOperationCMCCRealnameCallback = "realname_callback"
|
||||
// IntegrationProviderCUCC 表示中国联通回调提供方。
|
||||
IntegrationProviderCUCC = "cucc"
|
||||
// IntegrationOperationCUCCRealnameRemovalCallback 表示中国联通解除实名回调。
|
||||
IntegrationOperationCUCCRealnameRemovalCallback = "realname_removal_callback"
|
||||
// IntegrationOperationCUCCRealnameCallback 表示中国联通实名成功回调。
|
||||
IntegrationOperationCUCCRealnameCallback = "realname_callback"
|
||||
// IntegrationInboundProcessingLease 表示入站回调 pending 记录允许恢复前的处理租约。
|
||||
IntegrationInboundProcessingLease = time.Minute
|
||||
)
|
||||
|
||||
const (
|
||||
// IntegrationResultPending 表示外部尝试已建立但尚未终结。
|
||||
IntegrationResultPending = "pending"
|
||||
@@ -20,6 +41,8 @@ const (
|
||||
IntegrationResultNotFound = "not_found"
|
||||
// IntegrationResultInvalidPayload 表示入站载荷无效。
|
||||
IntegrationResultInvalidPayload = "invalid_payload"
|
||||
// IntegrationResultConflict 表示入站事实存在唯一性或幂等冲突。
|
||||
IntegrationResultConflict = "conflict"
|
||||
// IntegrationResultIgnored 表示外部尝试被安全忽略。
|
||||
IntegrationResultIgnored = "ignored"
|
||||
// IntegrationResultMerged 表示尝试被合并且未发送请求。
|
||||
@@ -41,6 +64,7 @@ func IntegrationResultName(result string) string {
|
||||
IntegrationResultUnknown: "结果未知",
|
||||
IntegrationResultNotFound: "未找到",
|
||||
IntegrationResultInvalidPayload: "无效载荷",
|
||||
IntegrationResultConflict: "冲突",
|
||||
IntegrationResultIgnored: "已忽略",
|
||||
IntegrationResultMerged: "已合并",
|
||||
IntegrationResultRateLimited: "已限频",
|
||||
|
||||
@@ -494,6 +494,11 @@ func RedisCardObservationAttemptKey(seriesID string, attempt int) string {
|
||||
return fmt.Sprintf("cardsync:attempt:%s:%d", seriesID, attempt)
|
||||
}
|
||||
|
||||
// RedisCardObservationScheduleKey 返回固定阶梯任务的入队幂等键。
|
||||
func RedisCardObservationScheduleKey(seriesID string, attempt int) string {
|
||||
return fmt.Sprintf("cardsync:schedule:%s:%d", seriesID, attempt)
|
||||
}
|
||||
|
||||
// RedisCardObservationInflightKey 返回一次实际 Gateway 请求的互斥键。
|
||||
func RedisCardObservationInflightKey(provider, syncType, resourceID string) string {
|
||||
return fmt.Sprintf("cardsync:inflight:%s:%s:%s", provider, syncType, resourceID)
|
||||
|
||||
6
pkg/constants/shop_series_grant.go
Normal file
6
pkg/constants/shop_series_grant.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
// ShopSeriesGrantMaxPackages 系列授权单次最多处理的套餐数量。
|
||||
ShopSeriesGrantMaxPackages = 100
|
||||
)
|
||||
@@ -16,6 +16,16 @@ const (
|
||||
SystemConfigTypeJSON = "json"
|
||||
// SystemConfigCacheTTL 是系统配置默认缓存时长。
|
||||
SystemConfigCacheTTL = 5 * time.Minute
|
||||
// SystemConfigModuleCarrierCallback 是运营商回调配置模块。
|
||||
SystemConfigModuleCarrierCallback = "carrier_callback"
|
||||
// SystemConfigCarrierCallbackCTCCRealnameEnabled 控制电信实名回调是否执行业务处理。
|
||||
SystemConfigCarrierCallbackCTCCRealnameEnabled = "carrier_callback.ctcc_realname.enabled"
|
||||
// SystemConfigCarrierCallbackCMCCRealnameEnabled 控制移动实名回调是否执行业务处理。
|
||||
SystemConfigCarrierCallbackCMCCRealnameEnabled = "carrier_callback.cmcc_realname.enabled"
|
||||
// SystemConfigCarrierCallbackCUCCRealnameEnabled 控制联通实名成功回调是否执行业务处理。
|
||||
SystemConfigCarrierCallbackCUCCRealnameEnabled = "carrier_callback.cucc_realname.enabled"
|
||||
// SystemConfigCarrierCallbackCUCCRealnameRemovalEnabled 控制联通解除实名回调是否执行业务处理。
|
||||
SystemConfigCarrierCallbackCUCCRealnameRemovalEnabled = "carrier_callback.cucc_realname_removal.enabled"
|
||||
)
|
||||
|
||||
// RedisSystemConfigKey 生成单个系统配置的 Redis 缓存键。
|
||||
|
||||
@@ -34,6 +34,10 @@ var (
|
||||
Name: "wecom_callback", Sensitive: true,
|
||||
SafeFields: fieldSet("event_type", "resource_type", "resource_id", "result_code", "status"),
|
||||
}
|
||||
carrierCallbackPolicy = AccessPolicy{
|
||||
Name: "carrier_callback", Sensitive: true, PresenceOnly: true,
|
||||
ResultFields: fieldSet("code", "msg", "timestamp"),
|
||||
}
|
||||
configPolicy = AccessPolicy{
|
||||
Name: "sensitive_config", Sensitive: true, PresenceOnly: true,
|
||||
SafeFields: fieldSet("config_key", "value_type", "module", "readonly", "sensitive", "configured", "status", "result_code"),
|
||||
@@ -64,6 +68,8 @@ func policyForPath(path string) AccessPolicy {
|
||||
return configPolicy
|
||||
case strings.Contains(normalized, "/callback") && (strings.Contains(normalized, "wecom") || strings.Contains(normalized, "wework")):
|
||||
return wecomPolicy
|
||||
case strings.Contains(normalized, "/callback/carriers/"):
|
||||
return carrierCallbackPolicy
|
||||
case strings.Contains(normalized, "payment"), strings.Contains(normalized, "wechat-pay"),
|
||||
strings.Contains(normalized, "alipay"), strings.Contains(normalized, "fuiou-pay"):
|
||||
return paymentPolicy
|
||||
|
||||
@@ -54,6 +54,10 @@ func BuildDocHandlers() *bootstrap.Handlers {
|
||||
AdminOrder: admin.NewOrderHandler(nil, nil),
|
||||
AdminExchange: admin.NewExchangeHandler(nil, nil, nil),
|
||||
PaymentCallback: callback.NewPaymentHandler(nil, nil, nil, nil, nil, nil, nil, nil),
|
||||
CTCCRealnameCallback: callback.NewCTCCRealnameHandler(nil, nil, nil, nil, nil, nil, nil),
|
||||
CMCCRealnameCallback: callback.NewCMCCRealnameHandler(nil, nil, nil, nil, nil, nil, nil),
|
||||
CUCCRealnameCallback: callback.NewCUCCRealnameHandler(nil, nil, nil, nil, nil, nil, nil),
|
||||
CUCCRealnameRemovalCallback: callback.NewCUCCRealnameRemovalHandler(nil, nil, nil, nil, nil),
|
||||
PollingConfig: admin.NewPollingConfigHandler(nil),
|
||||
PollingConcurrency: admin.NewPollingConcurrencyHandler(nil),
|
||||
PollingMonitoring: admin.NewPollingMonitoringHandler(nil),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
cardapp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
pkgerrors "github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
)
|
||||
|
||||
// CardObservationSeriesScheduler 将固定阶梯任务提交到独立队列。
|
||||
@@ -23,6 +24,9 @@ func NewCardObservationSeriesScheduler(client *Client) *CardObservationSeriesSch
|
||||
|
||||
// Enqueue 以稳定 series_id + attempt 任务 ID 零重试入队。
|
||||
func (s *CardObservationSeriesScheduler) Enqueue(ctx context.Context, payload cardapp.SeriesTaskPayload) error {
|
||||
if s == nil || s.client == nil {
|
||||
return pkgerrors.New(pkgerrors.CodeInternalError, "卡观测序列队列客户端未配置")
|
||||
}
|
||||
taskID := "card-series-" + payload.SeriesID + "-" + strconv.Itoa(payload.Attempt)
|
||||
err := s.client.EnqueueTask(
|
||||
ctx,
|
||||
|
||||
@@ -228,6 +228,7 @@ func (h *Handler) registerPollingHandlers() {
|
||||
h.pollingBase, h.workerResult.Stores.IotCard,
|
||||
h.pollingStopResumeSvc)
|
||||
protectHandler := task.NewPollingProtectHandler(
|
||||
h.db, h.workerResult.Services.ObservationSeriesEvents,
|
||||
h.pollingBase, h.gatewayClient, h.workerResult.Stores.IotCard,
|
||||
h.workerResult.Stores.DeviceSimBinding, h.pollingStopResumeSvc)
|
||||
cardStatusHandler := task.NewPollingCardStatusHandler(
|
||||
@@ -295,6 +296,7 @@ func (h *Handler) registerAutoPurchaseHandler() {
|
||||
h.redis,
|
||||
h.asynqClient,
|
||||
h.logger,
|
||||
h.workerResult.Services.ObservationSeriesEvents,
|
||||
)
|
||||
h.mux.HandleFunc(constants.TaskTypeAutoPurchaseAfterRecharge, autoPurchaseHandler.ProcessTask)
|
||||
h.logger.Info("注册自动购包任务处理器", zap.String("task_type", constants.TaskTypeAutoPurchaseAfterRecharge))
|
||||
|
||||
@@ -54,17 +54,18 @@ type WorkerStores struct {
|
||||
|
||||
// WorkerServices Worker 侧所有 Service 的集合
|
||||
type WorkerServices struct {
|
||||
CardObservation *cardObservationApp.Service
|
||||
CardObservationSeries *cardObservationApp.SeriesAttemptService
|
||||
CommissionCalculation *commission_calculation.Service
|
||||
CommissionStats *commission_stats.Service
|
||||
UsageService *packagepkg.UsageService
|
||||
ActivationService *packagepkg.ActivationService
|
||||
ResetService *packagepkg.ResetService
|
||||
AlertService *pollingSvc.AlertService
|
||||
CleanupService *pollingSvc.CleanupService
|
||||
StopResumeService packagepkg.StopResumeCallback // 停复机服务,用于注入 Scheduler
|
||||
OrderExpirer OrderExpirer // 订单超时取消服务(接口类型,避免循环依赖)
|
||||
CardObservation *cardObservationApp.Service
|
||||
CardObservationSeries *cardObservationApp.SeriesAttemptService
|
||||
ObservationSeriesEvents cardObservationApp.SeriesEventWriter
|
||||
CommissionCalculation *commission_calculation.Service
|
||||
CommissionStats *commission_stats.Service
|
||||
UsageService *packagepkg.UsageService
|
||||
ActivationService *packagepkg.ActivationService
|
||||
ResetService *packagepkg.ResetService
|
||||
AlertService *pollingSvc.AlertService
|
||||
CleanupService *pollingSvc.CleanupService
|
||||
StopResumeService packagepkg.StopResumeCallback // 停复机服务,用于注入 Scheduler
|
||||
OrderExpirer OrderExpirer // 订单超时取消服务(接口类型,避免循环依赖)
|
||||
}
|
||||
|
||||
// WorkerBootstrapResult Worker Bootstrap 结果
|
||||
|
||||
Reference in New Issue
Block a user