收口七月卡状态回调与系列授权兼容契约
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:
2026-07-24 19:59:24 +08:00
parent a18ed8bc8d
commit 5c4d17e9fc
79 changed files with 4341 additions and 478 deletions

View File

@@ -7,6 +7,7 @@ import (
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/cardtrafficlock"
"github.com/break/junhong_cmp_fiber/internal/polling"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
@@ -28,7 +29,6 @@ var acquireConcurrencyScript = redis.NewScript(`
return current
`)
const cardTrafficSyncLockTTL = 10 * time.Minute
const pollingFallbackOperationTimeout = 5 * time.Second
// pollingFallbackContext 创建轮询兜底操作使用的独立短超时上下文。
@@ -47,6 +47,7 @@ type PollingBase struct {
iotCardStore *postgres.IotCardStore
logger *zap.Logger
verboseLog bool
trafficLock *cardtrafficlock.Lock
}
// NewPollingBase 创建轮询共享基类
@@ -65,6 +66,7 @@ func NewPollingBase(
iotCardStore: iotCardStore,
logger: logger,
verboseLog: verboseLog,
trafficLock: cardtrafficlock.New(redisClient),
}
}
@@ -105,26 +107,16 @@ func (b *PollingBase) releaseConcurrency(_ context.Context, taskType string) {
}
// acquireCardTrafficSyncLock 获取卡流量同步锁,避免轮询与手动刷新重复统计同一上游读数。
func (b *PollingBase) acquireCardTrafficSyncLock(ctx context.Context, cardID uint) (bool, error) {
if b.redis == nil {
return true, nil
}
locked, err := b.redis.SetNX(ctx, constants.RedisCardTrafficSyncLockKey(cardID), "1", cardTrafficSyncLockTTL).Result()
if err != nil {
return false, err
}
return locked, nil
func (b *PollingBase) acquireCardTrafficSyncLock(ctx context.Context, cardID uint) (string, bool, error) {
return b.trafficLock.Acquire(ctx, cardID)
}
// releaseCardTrafficSyncLock 释放卡流量同步锁。
func (b *PollingBase) releaseCardTrafficSyncLock(_ context.Context, cardID uint) {
if b.redis == nil {
return
}
func (b *PollingBase) releaseCardTrafficSyncLock(_ context.Context, cardID uint, token string) {
ctx, cancel := pollingFallbackContext()
defer cancel()
if err := b.redis.Del(ctx, constants.RedisCardTrafficSyncLockKey(cardID)).Err(); err != nil {
if err := b.trafficLock.Release(ctx, cardID, token); err != nil {
b.logger.Warn("释放卡流量同步锁失败", zap.Uint("card_id", cardID), zap.Error(err))
}
}