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 }