feat: 实名即生效套餐沉默期、延期办理与资产出库时间
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录 - 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描 - 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理 - 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付 - 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
merchantPaymentApp "github.com/break/junhong_cmp_fiber/internal/application/merchantpayment"
|
||||
notificationApp "github.com/break/junhong_cmp_fiber/internal/application/notification"
|
||||
packageExpiryApp "github.com/break/junhong_cmp_fiber/internal/application/packageexpiry"
|
||||
packagesilenceApp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
|
||||
roleApp "github.com/break/junhong_cmp_fiber/internal/application/role"
|
||||
shopApp "github.com/break/junhong_cmp_fiber/internal/application/shop"
|
||||
systemConfigApp "github.com/break/junhong_cmp_fiber/internal/application/systemconfig"
|
||||
@@ -42,6 +43,7 @@ import (
|
||||
notificationQuery "github.com/break/junhong_cmp_fiber/internal/query/notification"
|
||||
operationsreportquery "github.com/break/junhong_cmp_fiber/internal/query/operationsreport"
|
||||
packageExpiryQuery "github.com/break/junhong_cmp_fiber/internal/query/packageexpiry"
|
||||
packagesilenceQuery "github.com/break/junhong_cmp_fiber/internal/query/packagesilence"
|
||||
packagetrafficalertquery "github.com/break/junhong_cmp_fiber/internal/query/packagetrafficalert"
|
||||
priorityPollingQuery "github.com/break/junhong_cmp_fiber/internal/query/prioritypolling"
|
||||
shopQuery "github.com/break/junhong_cmp_fiber/internal/query/shop"
|
||||
@@ -126,6 +128,7 @@ func initHandlers(svc *services, deps *Dependencies) *Handlers {
|
||||
registerPaymentMethodConfigDefinitions(systemConfigRegistry, deps.Logger)
|
||||
gatewaylimit.RegisterConfigDefinitions(systemConfigRegistry, deps.Logger)
|
||||
carrierqueue.RegisterConfigDefinitions(systemConfigRegistry, deps.Logger)
|
||||
packagesilenceApp.RegisterConfigDefinitions(systemConfigRegistry, deps.Logger)
|
||||
systemConfigAlerts := systemConfigInfra.NewLogAlertSink(deps.Logger)
|
||||
var systemConfigCache systemConfigInfra.Cache
|
||||
if deps.Redis != nil {
|
||||
@@ -313,11 +316,16 @@ func initHandlers(svc *services, deps *Dependencies) *Handlers {
|
||||
ShopSeriesGrant: admin.NewShopSeriesGrantHandler(svc.ShopSeriesGrant),
|
||||
AdminOrder: admin.NewOrderHandler(svc.Order, validate),
|
||||
AdminExchange: admin.NewExchangeHandler(svc.Exchange, exchangeQuery.NewListQuery(deps.DB), validate),
|
||||
PaymentCallback: callback.NewPaymentHandler(
|
||||
svc.Order, svc.Recharge, rechargeOrderService, svc.AgentRecharge,
|
||||
deps.WechatPayment, svc.WechatConfig, paymentStore,
|
||||
svc.AgentRechargePaymentConfirm, integrationlog.NewRepository(deps.DB), deps.Logger,
|
||||
),
|
||||
PaymentCallback: func() *callback.PaymentHandler {
|
||||
handler := callback.NewPaymentHandler(
|
||||
svc.Order, svc.Recharge, rechargeOrderService, svc.AgentRecharge,
|
||||
deps.WechatPayment, svc.WechatConfig, paymentStore,
|
||||
svc.AgentRechargePaymentConfirm, integrationlog.NewRepository(deps.DB), deps.Logger,
|
||||
)
|
||||
// 沉默期延期支付回调:按 order_type = silence_extension 分发
|
||||
handler.SetSilencePaymentConfirmer(svc.PackageSilence.Extensions)
|
||||
return handler
|
||||
}(),
|
||||
CTCCRealnameCallback: callback.NewCTCCRealnameHandler(
|
||||
carriercallback.NewCTCCRealnameTranslator(), carriercallback.NewCTCCCardResolver(deps.DB),
|
||||
integrationlog.NewRepository(deps.DB), svc.CardObservation, svc.CardObservationSeries, systemConfigReader, deps.Logger,
|
||||
@@ -338,6 +346,9 @@ func initHandlers(svc *services, deps *Dependencies) *Handlers {
|
||||
PollingConfig: admin.NewPollingConfigHandler(svc.PollingConfig),
|
||||
GatewayRateLimit: admin.NewGatewayRateLimitHandler(pollingSvcPkg.NewGatewayRateLimitService(deps.DB, auditInfra.NewWriter(auditInfra.NewRegistry(), nil), deps.GatewayLimit), deps.Redis, validate),
|
||||
PollingConcurrency: admin.NewPollingConcurrencyHandler(svc.PollingConcurrency),
|
||||
PackageSilence: admin.NewPackageSilenceHandler(svc.PackageSilence.Switches, svc.PackageSilence.Policies,
|
||||
svc.PackageSilence.Extensions, packagesilenceQuery.NewQuery(deps.DB), validate),
|
||||
ClientPackageSilence: app.NewClientPackageSilenceHandler(svc.PackageSilence.Extensions, packagesilenceQuery.NewQuery(deps.DB), validate),
|
||||
PollingPause: admin.NewPollingPauseHandler(assetPollingSvc,
|
||||
pollingSvcPkg.NewPollingStatusImportService(deps.DB, deps.QueueClient, deps.Logger), validate),
|
||||
CarrierScheduler: admin.NewCarrierSchedulerHandler(pollingSvcPkg.NewCarrierSchedulerService(deps.DB, deps.Redis,
|
||||
|
||||
56
internal/bootstrap/package_silence.go
Normal file
56
internal/bootstrap/package_silence.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
|
||||
approvalApp "github.com/break/junhong_cmp_fiber/internal/application/approval"
|
||||
"github.com/break/junhong_cmp_fiber/internal/application/merchantpayment"
|
||||
packagesilenceApp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
|
||||
refundchannelApp "github.com/break/junhong_cmp_fiber/internal/application/refundchannel"
|
||||
auditInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
||||
packageSvc "github.com/break/junhong_cmp_fiber/internal/service/package"
|
||||
)
|
||||
|
||||
// packageSilenceBundle 是沉默期相关用例的装配结果(API 与 Worker 共用)。
|
||||
type packageSilenceBundle struct {
|
||||
Switch *packagesilenceApp.Switch
|
||||
Resolver *packagesilenceApp.Resolver
|
||||
Setter *packagesilenceApp.Setter
|
||||
Outbound *packagesilenceApp.OutboundRecorder
|
||||
// 以下仅 API 与 Worker 中延期相关入口装配
|
||||
Extensions *packagesilenceApp.ExtensionService
|
||||
Policies *packagesilenceApp.PolicyService
|
||||
Switches *packagesilenceApp.SwitchService
|
||||
}
|
||||
|
||||
// newPackageSilenceBundle 装配沉默期总开关、判定、设置与出库时间用例,并把激活闸门注入激活服务。
|
||||
func newPackageSilenceBundle(db *gorm.DB, redisClient *redis.Client, logger *zap.Logger, auditWriter *auditInfra.Writer, activation *packageSvc.ActivationService) *packageSilenceBundle {
|
||||
sw := packagesilenceApp.NewDefaultSwitch(db, redisClient, logger)
|
||||
resolver := packagesilenceApp.NewResolver(db, sw, auditWriter, logger)
|
||||
if activation != nil {
|
||||
resolver.SetActivator(activation)
|
||||
activation.SetSilenceGate(resolver, sw)
|
||||
}
|
||||
return &packageSilenceBundle{
|
||||
Switch: sw, Resolver: resolver,
|
||||
Setter: packagesilenceApp.NewSetter(db, auditWriter),
|
||||
Outbound: packagesilenceApp.NewOutboundRecorder(auditWriter),
|
||||
Extensions: packagesilenceApp.NewExtensionService(db, resolver, auditWriter, logger),
|
||||
Policies: packagesilenceApp.NewPolicyService(db, auditWriter),
|
||||
Switches: packagesilenceApp.NewSwitchService(db, redisClient, sw, auditWriter),
|
||||
}
|
||||
}
|
||||
|
||||
// wireExtensions 注入延期用例的审批、权限、资产归属、商户池与支付渠道。
|
||||
func (b *packageSilenceBundle) wireExtensions(approval approvalApp.Port, perms packagesilenceApp.PermissionChecker, ownership packagesilenceApp.OwnershipChecker,
|
||||
runtime *merchantpayment.RuntimeLoader, ports packagesilenceApp.PaymentPorts, refunder refundchannelApp.Refunder) {
|
||||
b.Setter.SetApprovalPort(approval)
|
||||
b.Setter.SetPermissionChecker(perms)
|
||||
b.Extensions.SetApprovalPort(approval)
|
||||
b.Extensions.SetPermissionChecker(perms)
|
||||
b.Extensions.SetOwnershipChecker(ownership)
|
||||
b.Extensions.SetPayment(runtime, ports, refunder)
|
||||
b.Policies.SetPermissionChecker(perms)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
packagesilenceApp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
@@ -97,6 +98,7 @@ type services struct {
|
||||
AssetAudit *assetAuditSvc.Service
|
||||
Role *roleSvc.Service
|
||||
Permission *permissionSvc.Service
|
||||
PackageSilence *packageSilenceBundle
|
||||
PersonalCustomer *personalCustomerSvc.Service
|
||||
ClientAuth *clientAuthSvc.Service
|
||||
Shop *shopSvc.Service
|
||||
@@ -267,6 +269,7 @@ func initServices(s *stores, deps *Dependencies) *services {
|
||||
// 激活前流量结算:资产从无生效主套餐变为有生效主套餐前,对已切换日快照计量的卡查一次流量入账。
|
||||
preActivationSettler := cardObservationInfra.NewPreActivationSettler(deps.DB, deps.GatewayClient, cardObservationService, postgres.NewCarrierStore(deps.DB), deps.Logger)
|
||||
packageActivation.SetPreActivationSettler(preActivationSettler)
|
||||
packageSilence := newPackageSilenceBundle(deps.DB, deps.Redis, deps.Logger, auditWriter, packageActivation)
|
||||
|
||||
stopResumeService := iotCardSvc.NewStopResumeService(
|
||||
deps.Redis,
|
||||
@@ -338,6 +341,8 @@ func initServices(s *stores, deps *Dependencies) *services {
|
||||
orderService.SetPaymentIntegrationLog(integrationlog.NewRepository(deps.DB))
|
||||
orderService.SetObservationSeriesEventWriter(observationSeriesEvents)
|
||||
orderService.SetPriorityEventWriter(priorityEvents)
|
||||
orderService.SetSilenceSetter(packageSilence.Setter)
|
||||
orderService.SetOutboundRecorder(packageSilence.Outbound)
|
||||
walletOutbox := outbox.NewRepository()
|
||||
walletDebitEvents := walletinfra.NewDebitEventWriter(walletOutbox, auditWriter)
|
||||
orderService.SetAgentWalletDebitService(walletapp.NewDebitService(walletDebitEvents, nil))
|
||||
@@ -392,6 +397,7 @@ func initServices(s *stores, deps *Dependencies) *services {
|
||||
refundService.SetNotificationOutbox(walletOutbox)
|
||||
refundService.SetPaymentMerchantRuntime(merchantpayment.NewRuntimeLoader(deps.DB, deps.Redis))
|
||||
refundService.SetLifecycleAudit(auditWriter)
|
||||
refundService.SetSilenceSwitch(packageSilence.Switch)
|
||||
// 渠道原路退款的登记与执行共用同一用例:API 侧只登记待执行事实与可靠事件,
|
||||
// 真正的渠道调用由 Worker 消费该事件执行。
|
||||
refundService.SetChannelRefundService(
|
||||
@@ -405,10 +411,12 @@ func initServices(s *stores, deps *Dependencies) *services {
|
||||
exchangeService := exchangeSvc.New(deps.DB, s.ExchangeOrder, s.IotCard, s.Device, s.AssetWallet, s.AssetWalletTransaction, s.PackageUsage, s.PackageUsageDailyRecord, s.ResourceTag, customerBinding, deps.Logger)
|
||||
exchangeService.SetShippingCreatedNotifier(exchangeApp.NewShippingCreatedNotifier(exchangeInfra.NewShippingNotificationWriter(outbox.NewRepository())))
|
||||
exchangeService.SetAccessAudit(auditWriter)
|
||||
exchangeService.SetSilenceHook(packagesilenceApp.NewExchangeHook(packageSilence.Setter, packageSilence.Outbound))
|
||||
exchangeService.SetPhoneAssetAssociationStore(s.PhoneAssetAssociation)
|
||||
assetService := assetSvc.New(deps.DB, s.Device, s.IotCard, s.PackageUsage, s.Package, s.PackageSeries, s.DeviceSimBinding, s.Shop, deps.Redis, iotCard, deps.GatewayClient, s.AssetIdentifier, s.Order, s.OrderItem, s.ExchangeOrder)
|
||||
assetService.SetPhoneAssetAssociationStore(s.PhoneAssetAssociation)
|
||||
assetService.SetAccessAudit(auditWriter)
|
||||
assetService.SetSilenceSwitch(packageSilence.Switch)
|
||||
agentOpenAPI := agentOpenAPISvc.New(assetService, packageService, orderService, shopCommission, stopResumeService, device, s.IotCard, s.PackageUsage, s.Package, s.PackageSeries, s.AgentWallet, s.DeviceSimBinding, s.Device)
|
||||
wecomApplicationRepository := wecomInfra.NewApplicationRepository(deps.DB)
|
||||
wecomSceneRepository := wecomInfra.NewSceneRepository(deps.DB)
|
||||
@@ -453,6 +461,15 @@ func initServices(s *stores, deps *Dependencies) *services {
|
||||
roleService.SetAccessAudit(deps.DB, deps.Redis, auditWriter)
|
||||
permissionService := permissionSvc.New(s.Permission, s.AccountRole, s.RolePermission, account, deps.Redis)
|
||||
permissionService.SetAccessAudit(deps.DB, auditWriter)
|
||||
packageSilence.wireExtensions(approvalCreationService, permissionService, customerBinding,
|
||||
merchantpayment.NewRuntimeLoader(deps.DB, deps.Redis),
|
||||
packagesilenceApp.PaymentPorts{
|
||||
Wechat: paymentInfra.NewWechatWebAdapter(wechat.NewRedisCache(deps.Redis), paymentIntegration, deps.Logger),
|
||||
Alipay: paymentInfra.NewAlipayWapAdapter(paymentIntegration, deps.Logger),
|
||||
Fuiou: paymentInfra.NewFuiouScanAdapter(paymentIntegration, deps.Logger),
|
||||
},
|
||||
paymentInfra.NewRefundAdapter(wechat.NewRedisCache(deps.Redis), deps.Logger),
|
||||
)
|
||||
shopService := shopSvc.New(s.Shop, s.Account, s.ShopRole, s.Role)
|
||||
shopService.SetAccessAudit(deps.DB, deps.Redis, auditWriter)
|
||||
commissionWithdrawal := commissionWithdrawalSvc.New(deps.DB, s.Shop, s.Account, s.AgentWallet, s.AgentWalletTransaction, s.CommissionWithdrawalRequest)
|
||||
@@ -513,6 +530,7 @@ func initServices(s *stores, deps *Dependencies) *services {
|
||||
AssetAudit: assetAudit,
|
||||
Role: roleService,
|
||||
Permission: permissionService,
|
||||
PackageSilence: packageSilence,
|
||||
PersonalCustomer: personalCustomerSvc.NewService(deps.DB, s.PersonalCustomer, s.PersonalCustomerPhone, deps.Logger, auditWriter),
|
||||
ClientAuth: clientAuthSvc.New(
|
||||
deps.DB,
|
||||
|
||||
@@ -67,6 +67,8 @@ type Handlers struct {
|
||||
GatewayRateLimit *admin.GatewayRateLimitHandler
|
||||
CarrierScheduler *admin.CarrierSchedulerHandler
|
||||
PollingPause *admin.PollingPauseHandler
|
||||
PackageSilence *admin.PackageSilenceHandler
|
||||
ClientPackageSilence *app.ClientPackageSilenceHandler
|
||||
PollingConcurrency *admin.PollingConcurrencyHandler
|
||||
PollingMonitoring *admin.PollingMonitoringHandler
|
||||
PollingAlert *admin.PollingAlertHandler
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
cardObservationApp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
|
||||
carrierThresholdApp "github.com/break/junhong_cmp_fiber/internal/application/carrierthreshold"
|
||||
employeecollectionApp "github.com/break/junhong_cmp_fiber/internal/application/employeecollection"
|
||||
"github.com/break/junhong_cmp_fiber/internal/application/merchantpayment"
|
||||
packagesilenceApp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
|
||||
walletapp "github.com/break/junhong_cmp_fiber/internal/application/wallet"
|
||||
assetAutoRenewalInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/assetautorenewal"
|
||||
auditInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
||||
@@ -19,6 +21,7 @@ import (
|
||||
carrierThresholdInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/carrierthreshold"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/messaging/outbox"
|
||||
paymentInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/payment"
|
||||
prioritypollingInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/prioritypolling"
|
||||
walletinfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/wallet"
|
||||
assetAutoRenewalQuery "github.com/break/junhong_cmp_fiber/internal/query/assetautorenewal"
|
||||
@@ -31,6 +34,7 @@ import (
|
||||
pollingSvc "github.com/break/junhong_cmp_fiber/internal/service/polling"
|
||||
purchaseValidationSvc "github.com/break/junhong_cmp_fiber/internal/service/purchase_validation"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/queue"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/wechat"
|
||||
)
|
||||
|
||||
type workerServices struct {
|
||||
@@ -92,6 +96,14 @@ func initWorkerServices(stores *queue.WorkerStores, deps *WorkerDependencies) *q
|
||||
deps.Logger,
|
||||
)
|
||||
activationService.SetLifecycleAudit(auditWriter)
|
||||
packageSilence := newPackageSilenceBundle(deps.DB, deps.Redis, deps.Logger, auditWriter, activationService)
|
||||
// Worker 内延期用例只承担审批通过后的执行、查单恢复与异常原路退款,不创建新申请。
|
||||
silencePaymentLog := integrationlog.NewRepository(deps.DB)
|
||||
packageSilence.Extensions.SetPayment(merchantpayment.NewRuntimeLoader(deps.DB, deps.Redis), packagesilenceApp.PaymentPorts{
|
||||
Wechat: paymentInfra.NewWechatWebAdapter(wechat.NewRedisCache(deps.Redis), silencePaymentLog, deps.Logger),
|
||||
Alipay: paymentInfra.NewAlipayWapAdapter(silencePaymentLog, deps.Logger),
|
||||
Fuiou: paymentInfra.NewFuiouScanAdapter(silencePaymentLog, deps.Logger),
|
||||
}, paymentInfra.NewRefundAdapter(wechat.NewRedisCache(deps.Redis), deps.Logger))
|
||||
usageService.SetLifecycleAudit(auditWriter)
|
||||
resetService.SetLifecycleAudit(auditWriter)
|
||||
|
||||
@@ -186,6 +198,8 @@ func initWorkerServices(stores *queue.WorkerStores, deps *WorkerDependencies) *q
|
||||
orderService.SetAgentWalletDebitService(walletapp.NewDebitService(walletDebitEvents, nil))
|
||||
orderService.SetObservationSeriesEventWriter(observationSeriesEvents)
|
||||
orderService.SetPriorityEventWriter(priorityEvents)
|
||||
orderService.SetSilenceSetter(packageSilence.Setter)
|
||||
orderService.SetOutboundRecorder(packageSilence.Outbound)
|
||||
|
||||
// 创建停复机服务并注入回调:流量耗尽自动停机、套餐激活/重置/支付后自动复机
|
||||
stopResumeService := iotCardSvc.NewStopResumeService(
|
||||
@@ -218,6 +232,7 @@ func initWorkerServices(stores *queue.WorkerStores, deps *WorkerDependencies) *q
|
||||
ObservationEvents: observationSeriesEvents,
|
||||
PriorityEvents: priorityEvents,
|
||||
})
|
||||
assetAutoRenewalService.SetSilenceRecorder(packagesilenceApp.PurchaseRecorder{Setter: packageSilence.Setter, Outbound: packageSilence.Outbound})
|
||||
activationService.SetObservationSeriesEventWriter(observationSeriesEvents)
|
||||
activationService.SetPriorityEventWriter(priorityEvents)
|
||||
usageService.SetStopResumeCallback(stopResumeService)
|
||||
@@ -253,5 +268,10 @@ func initWorkerServices(stores *queue.WorkerStores, deps *WorkerDependencies) *q
|
||||
DeviceBatchAllocator: deviceBatchAllocator,
|
||||
AssetPolling: assetPollingService,
|
||||
IotCardPolling: iotCardAuditService,
|
||||
SilenceSwitch: packageSilence.Switch,
|
||||
SilenceResolver: packageSilence.Resolver,
|
||||
SilenceSetter: packageSilence.Setter,
|
||||
SilenceOutbound: packageSilence.Outbound,
|
||||
SilenceExtensions: packageSilence.Extensions,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user