chore: 初始开源发布
iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。 - Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件) - 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、 库名 xiujie_admin → iotlcm、哨兵名占位 - 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异, 不破坏「校验 MSISDN 格式」等测试断言) - 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像 digest 记录 - Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23), 移除写死的 git 凭据 - README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 已知:3 个测试包在源仓同一提交上同样失败(client/upstream、 service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
package renewchange
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
|
||||
rc "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/renewchange"
|
||||
)
|
||||
|
||||
type subscriptionValidation struct {
|
||||
ICCID string
|
||||
MSISDN string
|
||||
IMSI string
|
||||
TenantID string
|
||||
CustomerDeptID int64
|
||||
PackageCode string
|
||||
SourceBatchNo string
|
||||
}
|
||||
|
||||
// resolveCurrentSubscriptions selects the current package fact per ICCID.
|
||||
// It keeps future pending subscriptions for change-package cleanup, while
|
||||
// allowing renew to append after the farthest pending subscription.
|
||||
func resolveCurrentSubscriptions(rows []*rc.SimCardBindPkg, iccids []string) (map[string]*rc.SimCardBindPkg, map[string][]*rc.SimCardBindPkg, error) {
|
||||
grouped := make(map[string][]*rc.SimCardBindPkg, len(iccids))
|
||||
for _, row := range rows {
|
||||
if row == nil {
|
||||
continue
|
||||
}
|
||||
iccid := strings.TrimSpace(row.Iccid)
|
||||
if iccid == "" {
|
||||
continue
|
||||
}
|
||||
grouped[iccid] = append(grouped[iccid], row)
|
||||
}
|
||||
|
||||
current := make(map[string]*rc.SimCardBindPkg, len(iccids))
|
||||
future := make(map[string][]*rc.SimCardBindPkg, len(iccids))
|
||||
for _, rawICCID := range iccids {
|
||||
iccid := strings.TrimSpace(rawICCID)
|
||||
selected, pending, err := selectCurrentSubscription(grouped[iccid], iccid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
current[iccid] = selected
|
||||
future[iccid] = pending
|
||||
}
|
||||
return current, future, nil
|
||||
}
|
||||
|
||||
func selectCurrentSubscription(rows []*rc.SimCardBindPkg, iccid string) (*rc.SimCardBindPkg, []*rc.SimCardBindPkg, error) {
|
||||
var (
|
||||
effective *rc.SimCardBindPkg
|
||||
pending []*rc.SimCardBindPkg
|
||||
expired []*rc.SimCardBindPkg
|
||||
)
|
||||
for _, row := range rows {
|
||||
if row == nil || strings.TrimSpace(row.Iccid) != strings.TrimSpace(iccid) {
|
||||
continue
|
||||
}
|
||||
if consts.IsAddPackageType(row.PackageType) {
|
||||
continue
|
||||
}
|
||||
switch row.EffectiveStatus {
|
||||
case consts.EffectiveStatusEffective:
|
||||
if effective != nil {
|
||||
return nil, nil, gerror.Newf("同一张卡存在多个生效中的套餐订购记录:%s", iccid)
|
||||
}
|
||||
effective = row
|
||||
case consts.EffectiveStatusPending:
|
||||
pending = append(pending, row)
|
||||
case consts.EffectiveStatusExpired:
|
||||
expired = append(expired, row)
|
||||
}
|
||||
}
|
||||
sortPendingSubscriptions(pending)
|
||||
if effective != nil {
|
||||
return effective, pending, nil
|
||||
}
|
||||
if len(pending) > 0 {
|
||||
return pending[0], pending[1:], nil
|
||||
}
|
||||
if latest := latestExpiredSubscription(expired); latest != nil {
|
||||
return latest, nil, nil
|
||||
}
|
||||
return nil, nil, gerror.Newf("ICCID没有当前套餐订购记录:%s", iccid)
|
||||
}
|
||||
|
||||
func latestExpiredSubscription(rows []*rc.SimCardBindPkg) *rc.SimCardBindPkg {
|
||||
var latest *rc.SimCardBindPkg
|
||||
for _, row := range rows {
|
||||
if row == nil {
|
||||
continue
|
||||
}
|
||||
if latest == nil || subscriptionAfter(row, latest) {
|
||||
latest = row
|
||||
}
|
||||
}
|
||||
return latest
|
||||
}
|
||||
|
||||
func sortPendingSubscriptions(rows []*rc.SimCardBindPkg) {
|
||||
if len(rows) <= 1 {
|
||||
return
|
||||
}
|
||||
for i := 1; i < len(rows); i++ {
|
||||
current := rows[i]
|
||||
j := i - 1
|
||||
for j >= 0 && subscriptionAfter(rows[j], current) {
|
||||
rows[j+1] = rows[j]
|
||||
j--
|
||||
}
|
||||
rows[j+1] = current
|
||||
}
|
||||
}
|
||||
|
||||
func subscriptionAfter(left, right *rc.SimCardBindPkg) bool {
|
||||
if compareTime(expectedExpireTime(left), expectedExpireTime(right)) != 0 {
|
||||
return compareTime(expectedExpireTime(left), expectedExpireTime(right)) > 0
|
||||
}
|
||||
if compareTime(effectiveTime(left), effectiveTime(right)) != 0 {
|
||||
return compareTime(effectiveTime(left), effectiveTime(right)) > 0
|
||||
}
|
||||
return strings.TrimSpace(subscriptionNo(left)) > strings.TrimSpace(subscriptionNo(right))
|
||||
}
|
||||
|
||||
func expectedExpireTime(row *rc.SimCardBindPkg) *gtime.Time {
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
return row.ExpectedExpireTime
|
||||
}
|
||||
|
||||
func effectiveTime(row *rc.SimCardBindPkg) *gtime.Time {
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
return row.EffectiveTime
|
||||
}
|
||||
|
||||
func subscriptionNo(row *rc.SimCardBindPkg) string {
|
||||
if row == nil {
|
||||
return ""
|
||||
}
|
||||
return row.SubscriptionNo
|
||||
}
|
||||
|
||||
func compareTime(left, right *gtime.Time) int {
|
||||
switch {
|
||||
case left == nil && right == nil:
|
||||
return 0
|
||||
case left == nil:
|
||||
return -1
|
||||
case right == nil:
|
||||
return 1
|
||||
case left.Before(right):
|
||||
return -1
|
||||
case left.After(right):
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func validateCurrentSubscription(card *rc.SimCardInfo, bind *rc.SimCardBindPkg, expected subscriptionValidation) error {
|
||||
if card == nil || bind == nil {
|
||||
return gerror.New("卡信息或当前订购记录缺失")
|
||||
}
|
||||
iccid := strings.TrimSpace(expected.ICCID)
|
||||
if iccid == "" || strings.TrimSpace(card.Iccid) != iccid || strings.TrimSpace(bind.Iccid) != iccid {
|
||||
return gerror.New("ICCID不一致")
|
||||
}
|
||||
if strings.TrimSpace(expected.TenantID) != "" &&
|
||||
(strings.TrimSpace(card.TenantId) != expected.TenantID || strings.TrimSpace(bind.TenantId) != expected.TenantID) {
|
||||
return gerror.New("租户不一致")
|
||||
}
|
||||
// t_sim_card_info.dept_id is the current card owner. A card allocation
|
||||
// changes that field, while the historical bind row keeps the department
|
||||
// that originally purchased the subscription.
|
||||
if expected.CustomerDeptID <= 0 || card.DeptId != expected.CustomerDeptID {
|
||||
return gerror.New("客户部门不一致")
|
||||
}
|
||||
packageCode := strings.TrimSpace(expected.PackageCode)
|
||||
if packageCode == "" || strings.TrimSpace(card.CurrentPackageCode) != packageCode || strings.TrimSpace(bind.PackageCode) != packageCode {
|
||||
return gerror.New("当前套餐不一致")
|
||||
}
|
||||
if card.PackageStatus == consts.PackageStatusNone {
|
||||
return gerror.New("卡没有当前套餐")
|
||||
}
|
||||
if msisdn := strings.TrimSpace(expected.MSISDN); msisdn != "" && strings.TrimSpace(card.Msisdn) != msisdn {
|
||||
return gerror.New("MSISDN不一致")
|
||||
}
|
||||
if bindMSISDN := strings.TrimSpace(bind.Msisdn); bindMSISDN != "" && strings.TrimSpace(card.Msisdn) != bindMSISDN {
|
||||
return gerror.New("订购记录MSISDN不一致")
|
||||
}
|
||||
if imsi := strings.TrimSpace(expected.IMSI); imsi != "" && strings.TrimSpace(card.Imsi) != imsi {
|
||||
return gerror.New("IMSI不一致")
|
||||
}
|
||||
if sourceBatchNo := strings.TrimSpace(expected.SourceBatchNo); sourceBatchNo != "" {
|
||||
bindBatchNo := strings.TrimSpace(bind.BatchNo)
|
||||
outboundBatchNo := strings.TrimSpace(card.OutboundBatchNo)
|
||||
if bindBatchNo != sourceBatchNo && outboundBatchNo != sourceBatchNo {
|
||||
return gerror.New("当前订购来源批次不一致")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user