收口七月卡状态回调与系列授权兼容契约
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:
@@ -9,6 +9,7 @@ import (
|
||||
cardapp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
|
||||
carddomain "github.com/break/junhong_cmp_fiber/internal/domain/cardobservation"
|
||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/cardtrafficlock"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
packageexpiry "github.com/break/junhong_cmp_fiber/internal/query/packageexpiry"
|
||||
@@ -24,8 +25,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const cardTrafficSyncLockTTL = 10 * time.Minute
|
||||
|
||||
// PollingCallback 轮询回调接口
|
||||
// 用于在卡生命周期事件发生时通知轮询调度器
|
||||
type PollingCallback interface {
|
||||
@@ -71,6 +70,13 @@ type Service struct {
|
||||
enterpriseStore *postgres.EnterpriseStore
|
||||
packageExpiryQuery *packageexpiry.Query
|
||||
cardObservation *cardapp.Service
|
||||
observationSeries cardapp.BestEffortSeriesDispatcher
|
||||
trafficLock *cardtrafficlock.Lock
|
||||
}
|
||||
|
||||
// SetObservationSeriesDispatcher 注入获取实名链接后的后台观测端口。
|
||||
func (s *Service) SetObservationSeriesDispatcher(dispatcher cardapp.BestEffortSeriesDispatcher) {
|
||||
s.observationSeries = dispatcher
|
||||
}
|
||||
|
||||
// SetCardObservationService 注入统一卡实名观测写入用例。
|
||||
@@ -152,22 +158,17 @@ func (s *Service) SetDeviceSimBindingStore(deviceSimBindingStore *postgres.Devic
|
||||
// 用于清理实名逆转计数器,避免历史计数干扰手动实名后的判定
|
||||
func (s *Service) SetRedisClient(redisClient *redis.Client) {
|
||||
s.redis = redisClient
|
||||
s.trafficLock = cardtrafficlock.New(redisClient)
|
||||
}
|
||||
|
||||
// acquireCardTrafficSyncLock 获取卡流量同步锁,避免主动刷新与轮询重复统计同一上游读数。
|
||||
func (s *Service) acquireCardTrafficSyncLock(ctx context.Context, cardID uint) (bool, error) {
|
||||
if s.redis == nil {
|
||||
return true, nil
|
||||
}
|
||||
return s.redis.SetNX(ctx, constants.RedisCardTrafficSyncLockKey(cardID), "1", cardTrafficSyncLockTTL).Result()
|
||||
func (s *Service) acquireCardTrafficSyncLock(ctx context.Context, cardID uint) (string, bool, error) {
|
||||
return s.trafficLock.Acquire(ctx, cardID)
|
||||
}
|
||||
|
||||
// releaseCardTrafficSyncLock 释放卡流量同步锁。
|
||||
func (s *Service) releaseCardTrafficSyncLock(ctx context.Context, cardID uint) {
|
||||
if s.redis == nil {
|
||||
return
|
||||
}
|
||||
if err := s.redis.Del(ctx, constants.RedisCardTrafficSyncLockKey(cardID)).Err(); err != nil {
|
||||
func (s *Service) releaseCardTrafficSyncLock(ctx context.Context, cardID uint, token string) {
|
||||
if err := s.trafficLock.Release(ctx, cardID, token); err != nil {
|
||||
s.logger.Warn("释放卡流量同步锁失败", zap.Uint("card_id", cardID), zap.Error(err))
|
||||
}
|
||||
}
|
||||
@@ -1531,13 +1532,13 @@ func (s *Service) RefreshCardDataFromGateway(ctx context.Context, iccid string)
|
||||
return err
|
||||
}
|
||||
|
||||
locked, lockErr := s.acquireCardTrafficSyncLock(ctx, card.ID)
|
||||
lockToken, locked, lockErr := s.acquireCardTrafficSyncLock(ctx, card.ID)
|
||||
if lockErr != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, lockErr, "获取卡流量同步锁失败")
|
||||
} else if !locked {
|
||||
return errors.New(errors.CodeTooManyRequests, "卡流量正在同步,请稍后重试")
|
||||
} else {
|
||||
defer s.releaseCardTrafficSyncLock(ctx, card.ID)
|
||||
defer s.releaseCardTrafficSyncLock(ctx, card.ID, lockToken)
|
||||
latestCard, loadErr := s.iotCardStore.GetByID(ctx, card.ID)
|
||||
if loadErr != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, loadErr, "刷新卡数据失败")
|
||||
|
||||
Reference in New Issue
Block a user