package cardpackage import ( "context" "encoding/json" "fmt" "sort" "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/model/do" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) func (s *Service) TerminateCurrentPackage(ctx context.Context, tx gdb.TX, in *TerminateCurrentPackageInput) (*TerminateCurrentPackageOutput, error) { if in == nil || len(in.Iccids) == 0 { return nil, gerror.New("退订卡不能为空") } iccids := uniqueStrings(in.Iccids) cardsByIccid, err := s.repo.LockCardsByIccids(ctx, tx, iccids) if err != nil { return nil, gerror.Wrap(err, "锁定卡信息失败") } if len(cardsByIccid) != len(iccids) { return nil, gerror.New("部分卡信息已变化,请刷新后重试") } bindsByIccid, err := s.repo.LockCurrentActiveBindsByIccids(ctx, tx, iccids) if err != nil { return nil, gerror.Wrap(err, "锁定当前订购失败") } if len(bindsByIccid) != len(iccids) { return nil, gerror.New("部分卡没有当前订购,不能退订") } now := gtime.Now() sourceBatchNo := strings.TrimSpace(in.SourceBatchNo) packageCode := "" orderCodeSet := make(map[string]struct{}, len(iccids)) orderUnsubCounts := make(map[string]int, len(iccids)) logRows := make([]do.TSimBindPackageLogs, 0, len(iccids)) subscriptionNos := make([]string, 0, len(iccids)) for _, iccid := range iccids { card := cardsByIccid[iccid] bind := bindsByIccid[iccid] if card == nil { return nil, gerror.Newf("ICCID不存在正式卡记录:%s", iccid) } if bind == nil { return nil, gerror.Newf("ICCID当前订购数据异常或不存在:%s", iccid) } if card.DeptId != in.Operator.DeptId && card.CreatedDept != in.Operator.DeptId { return nil, gerror.Newf("ICCID不属于当前机构:%s", iccid) } if strings.TrimSpace(card.CurrentPackageCode) == "" || card.PackageStatus == consts.PackageStatusNone { return nil, gerror.Newf("ICCID没有可退订套餐:%s", iccid) } if packageCode == "" { packageCode = strings.TrimSpace(bind.PackageCode) } if packageCode != strings.TrimSpace(bind.PackageCode) { return nil, gerror.New("当前批次下存在多个套餐,不能统一退订") } if strings.TrimSpace(card.CurrentPackageCode) != strings.TrimSpace(bind.PackageCode) { return nil, gerror.Newf("ICCID当前套餐与订购记录不一致:%s", iccid) } if sourceBatchNo != "" && strings.TrimSpace(bind.BatchNo) != sourceBatchNo { return nil, gerror.Newf("ICCID当前订购不属于来源出库批次:%s", iccid) } orderCode := strings.TrimSpace(bind.OrderCode) if orderCode != "" { orderCodeSet[orderCode] = struct{}{} orderUnsubCounts[orderCode]++ } subscriptionNos = append(subscriptionNos, bind.SubscriptionNo) logRows = append(logRows, bindToHistory(bind, now, in.BatchNo, in.Operator.UserId)) } if len(logRows) > 0 { if _, err = tx.Ctx(ctx).Model(dao.TSimBindPackageLogs.Table()). Data(logRows). OnConflict(dao.TSimBindPackageLogs.Columns().SubscriptionNo). Save(); err != nil { return nil, gerror.Wrap(err, "写入订购历史失败") } } affected, err := s.repo.DeleteBindsBySubscriptionNos(ctx, tx, subscriptionNos) if err != nil { return nil, gerror.Wrap(err, "删除当前订购失败") } if int(affected) != len(iccids) { return nil, gerror.New("部分当前订购已变化,请刷新后重试") } orderCodes := mapKeys(orderCodeSet) if err = s.updateOrdersAfterUnsub(ctx, tx, orderCodes, orderUnsubCounts, in.BatchNo, now, in.Operator.UserId); err != nil { return nil, err } return &TerminateCurrentPackageOutput{ Iccids: iccids, CardsByIccid: cardsByIccid, BindsByIccid: bindsByIccid, PackageCode: packageCode, OrderCodes: orderCodes, UnsubTime: now, }, nil } func bindToHistory(bind *entity.TSimCardBindPkg, actualExpireAt *gtime.Time, unsubBatchNo string, userId int64) do.TSimBindPackageLogs { return do.TSimBindPackageLogs{ SubscriptionNo: bind.SubscriptionNo, BatchNo: bind.BatchNo, Iccid: bind.Iccid, Msisdn: bind.Msisdn, DeptId: bind.DeptId, PackageType: bind.PackageType, PackageCode: bind.PackageCode, PreviousPackageCode: bind.PreviousPackageCode, BusinessType: bind.BusinessType, EffectiveStatus: consts.EffectiveStatusTerminated, HandleTime: bind.HandleTime, EffectiveTime: bind.EffectiveTime, ExpectedExpireTime: bind.ExpectedExpireTime, ActualExpireTime: actualExpireAt, DurationMonths: bind.DurationMonths, GiftDurationMonths: bind.GiftDurationMonths, OrderCode: bind.OrderCode, Remark: appendRemark(bind.Remark, fmt.Sprintf("unsubscribeBatchNo=%s", unsubBatchNo)), TenantId: bind.TenantId, CreatedDept: bind.CreatedDept, CreatedBy: bind.CreatedBy, CreatedAt: bind.CreatedAt, UpdatedBy: userId, UpdatedAt: actualExpireAt, } } func (s *Service) updateOrdersAfterUnsub(ctx context.Context, tx gdb.TX, orderCodes []string, unsubCounts map[string]int, batchNo string, unsubTime *gtime.Time, userId int64) error { if len(orderCodes) == 0 { return nil } activeCounts, err := s.repo.ActiveBindCountsByOrderCodes(ctx, tx, orderCodes) if err != nil { return gerror.Wrap(err, "统计订单剩余订购失败") } for _, orderCode := range orderCodes { returnStatus := consts.OrderReturnStatusSuccess if activeCounts[orderCode] > 0 { returnStatus = consts.OrderReturnStatusPartial } if _, err = tx.Ctx(ctx).Model(dao.TOrder.Table()). Where(dao.TOrder.Columns().OrderSn, orderCode). Data(do.TOrder{ ReturnStatus: returnStatus, UpdatedBy: userId, UpdatedAt: unsubTime, }). Update(); err != nil { return gerror.Wrap(err, "更新订单退订状态失败") } if err = updateOrderItemExtra(ctx, tx, orderCode, batchNo, unsubCounts[orderCode], unsubTime, userId); err != nil { return err } } return nil } func updateOrderItemExtra(ctx context.Context, tx gdb.TX, orderCode, batchNo string, unsubQuantity int, unsubTime *gtime.Time, userId int64) error { var item *entity.TOrderItem if err := tx.Ctx(ctx).Model(dao.TOrderItem.Table()). Where(dao.TOrderItem.Columns().OrderSn, orderCode). Limit(1). Scan(&item); err != nil { return gerror.Wrap(err, "查询订单项失败") } if item == nil { return nil } extra := g.Map{} if strings.TrimSpace(item.ExtraSkuData) != "" { _ = json.Unmarshal([]byte(item.ExtraSkuData), &extra) } extra["lastUnsubscribeBatchNo"] = batchNo extra["lastUnsubscribeTime"] = formatTime(unsubTime) extra["unsubscribedQuantity"] = unsubQuantity data, err := json.Marshal(extra) if err != nil { return gerror.Wrap(err, "生成订单项退订扩展失败") } if _, err = tx.Ctx(ctx).Model(dao.TOrderItem.Table()). Where(dao.TOrderItem.Columns().ItemId, item.ItemId). Data(do.TOrderItem{ ExtraSkuData: string(data), UpdatedBy: userId, UpdatedAt: unsubTime, }). Update(); err != nil { return gerror.Wrap(err, "更新订单项退订扩展失败") } return nil } func appendRemark(old, addition string) string { old = strings.TrimSpace(old) addition = strings.TrimSpace(addition) if old == "" { return addition } if addition == "" { return old } return old + "; " + addition } func mapKeys(set map[string]struct{}) []string { result := make([]string, 0, len(set)) for key := range set { if strings.TrimSpace(key) != "" { result = append(result, key) } } sort.Strings(result) return result } func formatTime(t *gtime.Time) string { if t == nil { return "" } return t.Format("Y-m-d H:i:s") }