package addpackage import ( "context" "fmt" "strings" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" ) type cardTarget struct { Iccid string `orm:"iccid"` CurrentPackageCode string `orm:"current_package_code"` PackageStatus int `orm:"package_status"` } type cardLatestBindTarget struct { Iccid string `orm:"iccid"` PackageCode string `orm:"package_code"` EffectiveStatus int `orm:"effective_status"` ExpectedExpireTime *gtime.Time `orm:"expected_expire_time"` } type poolTarget struct { FlowPoolCode string `orm:"flow_pool_code"` PoolPackageCode int64 `orm:"pool_package_code"` PoolStatus int `orm:"pool_status"` } func (p *Processor) PrepareRows(ctx context.Context, rows []batchimport.Row) error { if p.config.Operation == OperationPool { return p.preparePoolRows(ctx, rows) } return p.prepareCardRows(ctx, rows) } func (p *Processor) prepareCardRows(ctx context.Context, rows []batchimport.Row) error { typedRows, targetCodes, err := collectTypedRows(rows) if err != nil { return err } if len(targetCodes) == 0 { return nil } var cards []*cardTarget cardColumns := dao.TSimCardInfo.Columns() if err = handler.Model(dao.TSimCardInfo.Ctx(ctx), &handler.Option{FilterTenant: true, FilterAuth: true}). Fields(cardColumns.Iccid, cardColumns.CurrentPackageCode, cardColumns.PackageStatus, cardColumns.TenantId, cardColumns.DeptId, cardColumns.CreatedDept, cardColumns.CreatedBy). WhereIn(cardColumns.Iccid, targetCodes). Scan(&cards); err != nil { return gerror.Wrap(err, "查询加油包卡信息失败") } cardMap := make(map[string]*cardTarget, len(cards)) for _, card := range cards { if card == nil { continue } iccid := strings.TrimSpace(card.Iccid) if iccid == "" { continue } cardMap[iccid] = card } latestBinds, err := p.loadLatestCardBaseBinds(ctx, targetCodes) if err != nil { return err } latestBindMap := make(map[string]*cardLatestBindTarget, len(latestBinds)) for _, bind := range latestBinds { if bind == nil { continue } iccid := strings.TrimSpace(bind.Iccid) if iccid == "" || latestBindMap[iccid] != nil { continue } latestBindMap[iccid] = bind } basePackageCodes := make([]string, 0, len(latestBindMap)) for _, bind := range latestBindMap { if bind == nil { continue } basePackageCodes = append(basePackageCodes, bind.PackageCode) } packages, err := p.loadPackageMap(ctx, append(basePackageCodes, rowPackageCodes(typedRows)...)) if err != nil { return err } now := gtime.Now() for _, row := range typedRows { card := cardMap[row.TargetCode] if card == nil { row.AddErrorReason("ICCID不存在或无权限") continue } latestBind := latestBindMap[row.TargetCode] if latestBind == nil || strings.TrimSpace(latestBind.PackageCode) == "" { row.AddErrorReason("当前卡订购记录不存在") continue } row.Authorized = true if card.PackageStatus != consts.PackageStatusEffective { row.AddErrorReason("当前卡套餐未生效,不能订购加油包") continue } if bindExpired(latestBind, now) { row.AddErrorReason("当前卡基础套餐已到期,不能订购加油包") continue } if err := p.validateTargetPackage(row, packages, 103); err != nil { row.AddErrorReason(err.Error()) continue } basePackage := packages[strings.TrimSpace(latestBind.PackageCode)] if basePackage == nil { row.AddErrorReason("当前卡基础套餐不存在") continue } if !containsString(packageCodesFromJSON(basePackage.TopupPackageCodes), row.PackageCode) { row.AddErrorReason("当前卡基础套餐不允许订购该加油包") continue } } return rowsValidationError(rows, "加油包卡信息校验失败") } func (p *Processor) preparePoolRows(ctx context.Context, rows []batchimport.Row) error { typedRows, targetCodes, err := collectTypedRows(rows) if err != nil { return err } if len(targetCodes) == 0 { return nil } var pools []*poolTarget poolColumns := dao.TSimFlowPool.Columns() if err = handler.Model(dao.TSimFlowPool.Ctx(ctx), &handler.Option{FilterTenant: true, FilterAuth: true}). Fields(poolColumns.FlowPoolCode, poolColumns.PoolPackageCode, poolColumns.PoolStatus, poolColumns.TenantId, poolColumns.DeptId, poolColumns.CreatedDept, poolColumns.CreatedBy). WhereIn(poolColumns.FlowPoolCode, targetCodes). Scan(&pools); err != nil { return gerror.Wrap(err, "查询流量池信息失败") } poolMap := make(map[string]*poolTarget, len(pools)) for _, pool := range pools { if pool == nil { continue } code := strings.TrimSpace(pool.FlowPoolCode) poolMap[code] = pool } packages, err := p.loadPackageMap(ctx, rowPackageCodes(typedRows)) if err != nil { return err } for _, row := range typedRows { pool := poolMap[row.TargetCode] if pool == nil { row.AddErrorReason("流量池编号不存在或无权限") continue } row.Authorized = true if pool.PoolStatus != 0 { row.AddErrorReason("流量池状态异常,不能订购加油包") continue } if err := p.validateTargetPackage(row, packages, 104); err != nil { row.AddErrorReason(err.Error()) continue } targetPackage := packages[row.PackageCode] if targetPackage == nil { row.AddErrorReason("加油包不存在") continue } if targetPackage.PoolPackageCode != pool.PoolPackageCode { row.AddErrorReason("加油包不属于当前流量池套餐") continue } } return rowsValidationError(rows, "流量池加油包校验失败") } func (p *Processor) loadLatestCardBaseBinds(ctx context.Context, iccids []string) ([]*cardLatestBindTarget, error) { iccids = uniqueStrings(iccids) if len(iccids) == 0 { return nil, nil } var binds []*cardLatestBindTarget if err := handler.Model(dao.TSimCardBindPkg.Ctx(ctx), &handler.Option{FilterTenant: true, FilterAuth: true}). Fields( dao.TSimCardBindPkg.Columns().Iccid, dao.TSimCardBindPkg.Columns().PackageCode, dao.TSimCardBindPkg.Columns().EffectiveStatus, dao.TSimCardBindPkg.Columns().ExpectedExpireTime, dao.TSimCardBindPkg.Columns().TenantId, dao.TSimCardBindPkg.Columns().DeptId, dao.TSimCardBindPkg.Columns().CreatedDept, dao.TSimCardBindPkg.Columns().CreatedBy, ). WhereIn(dao.TSimCardBindPkg.Columns().Iccid, iccids). WhereIn(dao.TSimCardBindPkg.Columns().PackageType, []int{101, 102}). Where(dao.TSimCardBindPkg.Columns().EffectiveStatus, consts.EffectiveStatusEffective). OrderDesc(dao.TSimCardBindPkg.Columns().ExpectedExpireTime). OrderDesc(dao.TSimCardBindPkg.Columns().Id). Scan(&binds); err != nil { return nil, gerror.Wrap(err, "查询卡最新订购记录失败") } return binds, nil } func bindExpired(bind *cardLatestBindTarget, now *gtime.Time) bool { if bind == nil { return true } if bind.EffectiveStatus == consts.EffectiveStatusExpired { return true } if bind.ExpectedExpireTime == nil || now == nil { return false } return !bind.ExpectedExpireTime.After(now) } func (p *Processor) validateTargetPackage(row *Row, packages map[string]*entity.TSimFlowPackageInfo, packageType int) error { if row == nil { return nil } pkg := packages[row.PackageCode] if pkg == nil { return gerror.New("加油包不存在") } if pkg.Enabled != 1 { return gerror.New("加油包未启用") } if pkg.PackageType != packageType { return gerror.Newf("加油包类型错误,应为%d", packageType) } return nil } func (p *Processor) loadPackageMap(ctx context.Context, codes []string) (map[string]*entity.TSimFlowPackageInfo, error) { out := make(map[string]*entity.TSimFlowPackageInfo) codes = uniqueStrings(codes) if len(codes) == 0 { return out, nil } var packages []*entity.TSimFlowPackageInfo columns := dao.TSimFlowPackageInfo.Columns() if err := handler.Model(dao.TSimFlowPackageInfo.Ctx(ctx), &handler.Option{FilterTenant: true, FilterAuth: false}). WhereIn(columns.PackageCode, codes). Scan(&packages); err != nil { return nil, gerror.Wrap(err, "查询加油包套餐信息失败") } for _, pkg := range packages { if pkg == nil { continue } out[strings.TrimSpace(pkg.PackageCode)] = pkg } return out, nil } func collectTypedRows(rows []batchimport.Row) ([]*Row, []string, error) { typedRows := make([]*Row, 0, len(rows)) targetCodes := make([]string, 0, len(rows)) for _, item := range rows { row, ok := item.(*Row) if !ok || row == nil { return nil, nil, gerror.New("加油包批次行类型错误") } typedRows = append(typedRows, row) if row.TargetCode != "" { targetCodes = append(targetCodes, row.TargetCode) } } return typedRows, uniqueStrings(targetCodes), nil } func rowPackageCodes(rows []*Row) []string { codes := make([]string, 0, len(rows)) for _, row := range rows { if row != nil { codes = append(codes, row.PackageCode) } } return codes } func packageCodesFromJSON(jsonValue *gjson.Json) []string { if jsonValue == nil || jsonValue.IsNil() { return nil } values := jsonValue.Interfaces() out := make([]string, 0, len(values)) for _, raw := range values { code := strings.TrimSpace(fmt.Sprint(raw)) if code != "" { out = append(out, code) } } return uniqueStrings(out) } func containsString(values []string, target string) bool { target = strings.TrimSpace(target) if target == "" { return false } for _, value := range values { if strings.TrimSpace(value) == target { return true } } return false } func uniqueStrings(in []string) []string { if len(in) == 0 { return nil } seen := make(map[string]struct{}, len(in)) out := make([]string, 0, len(in)) for _, value := range in { value = strings.TrimSpace(value) if value == "" { continue } if _, ok := seen[value]; ok { continue } seen[value] = struct{}{} out = append(out, value) } return out }