收口七月卡状态回调与系列授权兼容契约
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:
@@ -2,9 +2,10 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cardObservationApp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
|
||||
@@ -16,7 +17,6 @@ import (
|
||||
customerBinding "github.com/break/junhong_cmp_fiber/internal/service/customer_binding"
|
||||
pollingSvc "github.com/break/junhong_cmp_fiber/internal/service/polling"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/config"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
||||
@@ -36,7 +36,7 @@ type ClientRealnameHandler struct {
|
||||
carrierStore *postgres.CarrierStore
|
||||
gatewayClient *gateway.Client
|
||||
logger *zap.Logger
|
||||
manualTriggerSvc *pollingSvc.ManualTriggerService // 手动触发服务(可为nil,nil时跳过自动触发)
|
||||
observationSeries cardObservationApp.BestEffortSeriesDispatcher
|
||||
}
|
||||
|
||||
// NewClientRealnameHandler 创建 C 端实名认证处理器
|
||||
@@ -48,7 +48,7 @@ func NewClientRealnameHandler(
|
||||
carrierStore *postgres.CarrierStore,
|
||||
gatewayClient *gateway.Client,
|
||||
logger *zap.Logger,
|
||||
manualTriggerSvc *pollingSvc.ManualTriggerService, // 可为nil
|
||||
_ *pollingSvc.ManualTriggerService, // 兼容旧构造签名;获取链接改由 0/3/5 观测序列收敛
|
||||
) *ClientRealnameHandler {
|
||||
return &ClientRealnameHandler{
|
||||
assetService: assetSvc,
|
||||
@@ -58,10 +58,14 @@ func NewClientRealnameHandler(
|
||||
carrierStore: carrierStore,
|
||||
gatewayClient: gatewayClient,
|
||||
logger: logger,
|
||||
manualTriggerSvc: manualTriggerSvc,
|
||||
}
|
||||
}
|
||||
|
||||
// SetObservationSeriesDispatcher 注入获取实名链接后的观测序列端口。
|
||||
func (h *ClientRealnameHandler) SetObservationSeriesDispatcher(dispatcher cardObservationApp.BestEffortSeriesDispatcher) {
|
||||
h.observationSeries = dispatcher
|
||||
}
|
||||
|
||||
// GetRealnameLink E1 获取实名认证链接
|
||||
// GET /api/c/v1/realname/link
|
||||
func (h *ClientRealnameHandler) GetRealnameLink(c *fiber.Ctx) error {
|
||||
@@ -138,15 +142,29 @@ func (h *ClientRealnameHandler) GetRealnameLink(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 异步触发实名检查,提升检测优先级;失败不影响主流程
|
||||
if h.manualTriggerSvc != nil && config.Get().PollingAutoTrigger.EnableAutoTrigger {
|
||||
systemUserID := uint(config.Get().PollingAutoTrigger.AutoTriggerSystemUserID)
|
||||
go h.triggerRealnameCheck(targetCard.ID, customerID, targetCard.ICCID, systemUserID)
|
||||
}
|
||||
h.dispatchRealnameObservation(ctx, targetCard.ID)
|
||||
|
||||
return response.Success(c, resp)
|
||||
}
|
||||
|
||||
func (h *ClientRealnameHandler) dispatchRealnameObservation(ctx context.Context, cardID uint) {
|
||||
if h.observationSeries == nil {
|
||||
return
|
||||
}
|
||||
requestID := ""
|
||||
if value := pkgMiddleware.GetRequestIDFromContext(ctx); value != nil {
|
||||
requestID = *value
|
||||
}
|
||||
h.observationSeries.Dispatch(ctx, cardObservationApp.SeriesRequest{
|
||||
Scene: constants.CardObservationSceneClientRealnameLink,
|
||||
ResourceType: constants.CardObservationResourceTypeCard,
|
||||
ResourceID: strconv.FormatUint(uint64(cardID), 10),
|
||||
SyncType: constants.CardObservationSyncTypeRealname,
|
||||
ExpectedValue: "verified", Source: constants.CardObservationSourceBusinessEvent,
|
||||
RequestID: requestID, CorrelationID: requestID,
|
||||
})
|
||||
}
|
||||
|
||||
// resolveTargetCard 根据资产类型和ICCID定位目标卡
|
||||
// 支持三条路径:直接卡资产、设备+指定ICCID、设备取第一张绑定卡
|
||||
func (h *ClientRealnameHandler) resolveTargetCard(c *fiber.Ctx, asset *dto.AssetResolveResponse, iccid string) (*model.IotCard, error) {
|
||||
@@ -275,33 +293,3 @@ func (h *ClientRealnameHandler) findFirstBoundCard(c *fiber.Ctx, deviceID uint)
|
||||
|
||||
return card, nil
|
||||
}
|
||||
|
||||
// triggerRealnameCheck 异步触发单卡实名检查
|
||||
// 在独立 goroutine 中调用,使用独立 context 避免 Fiber 请求 context 失效问题
|
||||
// 参数全部为值类型,不捕获请求相关指针
|
||||
func (h *ClientRealnameHandler) triggerRealnameCheck(cardID, customerID uint, iccid string, systemUserID uint) {
|
||||
// 必须使用独立 context,禁止复用 Fiber 请求 context(请求返回后即失效)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 使用平台用户身份构建 context,绕过卡归属权限检查
|
||||
// 注意:使用 UserTypePlatform 而非 UserTypeSuperAdmin,SuperAdmin 不受日限制约束
|
||||
sysCtx := pkgMiddleware.SetUserContext(ctx, &pkgMiddleware.UserContextInfo{
|
||||
UserID: systemUserID,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
err := h.manualTriggerSvc.TriggerSingle(sysCtx, cardID, constants.TaskTypePollingRealname, systemUserID)
|
||||
if err != nil {
|
||||
h.logger.Warn("自动触发实名检查失败",
|
||||
zap.Uint("customer_id", customerID),
|
||||
zap.String("iccid", iccid),
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.Error(err))
|
||||
return
|
||||
}
|
||||
h.logger.Info("自动触发实名检查成功",
|
||||
zap.Uint("customer_id", customerID),
|
||||
zap.String("iccid", iccid),
|
||||
zap.Uint("card_id", cardID))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user