package renew import ( "context" "strings" "time" "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/logic/iot/cardpackage" "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/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) func (p *Processor) Complete(ctx context.Context, tx gdb.TX, in *CompleteInput) (*CompleteOutput, error) { if in == nil || in.SubmitParam == nil { return nil, gerror.New("续费参数不能为空") } sourceBatchNo := strings.TrimSpace(in.SubmitParam.SourceBatchNo) if in.BatchNo == "" { return nil, gerror.New("续费批次号不能为空") } if len(in.Rows) == 0 { return nil, gerror.New("续费卡不能为空") } iccids := rowsToIccids(in.Rows) paymentSnapshot, err := executeRenewPayment(ctx, tx, paymentExecutionInput{ BatchNo: in.BatchNo, PriceSnapshot: in.PriceSnapshot, Quantity: len(iccids), SubmitParam: in.SubmitParam, Operator: in.Operator, }) if err != nil { return nil, err } in.SubmitParam.PaymentSnapshot = paymentSnapshot in.SubmitParam.PaymentMode = paymentSnapshot.PaymentMode in.SubmitParam.Amount = formatRenewAmount(paymentSnapshot.PayAmount) packageSvc := cardpackage.NewService() durationValue := renewDisplayDurationValue(in.SubmitParam) durationUnit := renewDurationUnitFromSnapshot(in.PriceSnapshot) actualDurationMonths := renewActualDurationMonths(in.PriceSnapshot, durationValue) if actualDurationMonths <= 0 { return nil, gerror.New("续费时长必须大于0") } packageBeginTime := strings.TrimSpace(in.SubmitParam.PackageBeginTime) var packageBeginAt *gtime.Time if strings.TrimSpace(packageBeginTime) != "" { packageBeginAt, err = gtime.StrToTime(packageBeginTime) if err != nil { return nil, gerror.Wrap(err, "续费开始日期格式不正确") } packageBeginAt = normalizeRenewDayEndTime(packageBeginAt) } packageEndTime := strings.TrimSpace(in.SubmitParam.PackageEndTime) var packageEndAt *gtime.Time if strings.TrimSpace(packageEndTime) != "" { packageEndAt, err = gtime.StrToTime(packageEndTime) if err != nil { return nil, gerror.Wrap(err, "续费结束日期格式不正确") } packageEndAt = normalizeRenewDayEndTime(packageEndAt) } renew, err := packageSvc.RenewCurrentPackage(ctx, tx, &cardpackage.RenewCurrentPackageInput{ BatchNo: in.BatchNo, SourceBatchNo: sourceBatchNo, PackageCode: strings.TrimSpace(in.SubmitParam.PackageCode), Iccids: iccids, DurationMonths: actualDurationMonths, GiftDurationMonths: in.SubmitParam.GiftDurationMonths, DurationDisplayValue: durationValue, DurationUnit: durationUnit, PackageBeginTime: packageBeginAt, PackageEndTime: packageEndAt, Amount: in.SubmitParam.Amount, PayType: paymentSnapshot.PayType, PayStatus: paymentSnapshot.PayStatus, BalanceAmount: paymentSnapshot.BalanceAmount, OfflinePaidAmount: paymentSnapshot.OfflinePaidAmount, PayTime: paymentSnapshot.PaymentTime, OfflineContractCompleted: in.SubmitParam.OfflineContractCompleted, BuyerNote: in.SubmitParam.BuyerNote, AdminNote: in.SubmitParam.AdminNote, OrderRemark: "卡续费业务订单载体", BindRemark: in.BatchNo, Operator: in.Operator, }) if err != nil { return nil, err } detailUpdates := make(map[string]*gjsonJsonHolder, len(iccids)) for _, row := range in.Rows { card := renew.CardsByIccid[row.Iccid] latestBind := renew.LatestBindsByIccid[row.Iccid] newBind := renew.NewBindsByIccid[row.Iccid] detailUpdates[row.Iccid] = &gjsonJsonHolder{Data: buildDetailSnapshot(row, card, latestBind, newBind, in, renew)} } if err = updateBatchDetailSnapshots(ctx, tx, in.BatchNo, detailUpdates, in.Operator.UserId); err != nil { return nil, err } if err = convertBatchDetailsAfterComplete(ctx, tx, in.BatchNo, in.Operator.UserId); err != nil { return nil, err } if err = updateBatchMainAfterComplete(ctx, tx, in, renew); err != nil { return nil, err } renewUnitPrice := renew.UnitPrice if in.PriceSnapshot != nil { renewUnitPrice = in.PriceSnapshot.RenewUnitPrice } return &CompleteOutput{ BatchNo: in.BatchNo, BatchType: consts.BatchTypeRenew, SourceBatchNo: sourceBatchNo, Quantity: len(iccids), PackageCode: renew.PackageCode, PackageName: renew.Package.PackageName, OrderSn: renew.OrderSn, Amount: renew.Amount, RenewUnitPrice: renewUnitPrice, RenewDate: in.RenewDate, RenewTime: renew.RenewTime, }, nil } func normalizeRenewDayEndTime(t *gtime.Time) *gtime.Time { if t == nil || t.IsZero() { return t } tt := t.Time return gtime.NewFromTime(time.Date(tt.Year(), tt.Month(), tt.Day(), 23, 59, 59, 0, tt.Location())) } type gjsonJsonHolder struct { Data any } func rowsToIccids(rows []*Row) []string { iccids := make([]string, 0, len(rows)) for _, row := range rows { if row == nil { continue } iccid := strings.TrimSpace(row.Iccid) if iccid != "" { iccids = append(iccids, iccid) } } return uniqueStrings(iccids) } func updateBatchDetailSnapshots(ctx context.Context, tx gdb.TX, batchNo string, snapshots map[string]*gjsonJsonHolder, userId int64) error { for iccid, snapshot := range snapshots { _, err := tx.Ctx(ctx).Model(dao.BatchDetail.Table()). Where(dao.BatchDetail.Columns().BatchNo, batchNo). Where(dao.BatchDetail.Columns().ItemId, iccid). Data(do.BatchDetail{ Snapshot: gjson.New(snapshot.Data), UpdatedBy: userId, UpdatedAt: gtime.Now(), }). Update() if err != nil { return gerror.Wrap(err, "更新续费批次明细快照失败") } } return nil } func updateBatchMainAfterComplete(ctx context.Context, tx gdb.TX, in *CompleteInput, renew *cardpackage.RenewCurrentPackageOutput) error { durationValue := renewDisplayDurationValue(in.SubmitParam) actualDurationMonths := renewActualDurationMonths(in.PriceSnapshot, durationValue) info := g.Map{ "orderSn": renew.OrderSn, "packageCode": renew.PackageCode, "packageName": renew.Package.PackageName, "packageType": renew.Package.PackageType, "durationYears": in.SubmitParam.DurationYears, "durationMonths": actualDurationMonths, "durationValue": durationValue, "actualDurationMonths": actualDurationMonths, "giftDurationMonths": in.SubmitParam.GiftDurationMonths, "amount": renew.Amount, "quantity": renew.Quantity, "renewDate": in.RenewDate, "packageBeginTime": in.SubmitParam.PackageBeginTime, "packageEndTime": in.SubmitParam.PackageEndTime, "offlineContractCompleted": in.SubmitParam.OfflineContractCompleted, "renewTime": renew.RenewTime, } if sourceBatchNo := strings.TrimSpace(in.SubmitParam.SourceBatchNo); sourceBatchNo != "" { info["sourceBatchNo"] = sourceBatchNo } if buyerNote := strings.TrimSpace(in.SubmitParam.BuyerNote); buyerNote != "" { info["buyerNote"] = buyerNote } if adminNote := strings.TrimSpace(in.SubmitParam.AdminNote); adminNote != "" { info["adminNote"] = adminNote } if remark := strings.TrimSpace(in.SubmitParam.Remark); remark != "" { info["remark"] = remark } if in.PriceSnapshot != nil { info["customerAgentId"] = in.PriceSnapshot.CustomerAgentId info["billingMethod"] = in.PriceSnapshot.BillingMethod info["renewDurationUnit"] = in.PriceSnapshot.RenewDurationUnit info["renewDurationUnitText"] = in.PriceSnapshot.RenewDurationUnitText info["priceOwnerDeptId"] = in.PriceSnapshot.PriceOwnerDeptId info["priceSource"] = in.PriceSnapshot.PriceSource info["agentDefaultPriceId"] = in.PriceSnapshot.AgentDefaultPriceId info["targetPriceId"] = in.PriceSnapshot.TargetPriceId info["renewUnitPrice"] = in.PriceSnapshot.RenewUnitPrice info["renewableDurationOptions"] = in.PriceSnapshot.RenewableDurationOptions info["renewablePackageCodes"] = in.PriceSnapshot.RenewablePackageCodes info["renewablePackages"] = in.PriceSnapshot.RenewablePackages } if in.SubmitParam.PaymentSnapshot != nil { appendRenewPaymentSnapshot(info, in.SubmitParam.PaymentSnapshot) } _, err := tx.Ctx(ctx).Model(dao.BatchMain.Table()). Where(dao.BatchMain.Columns().BatchNo, in.BatchNo). Data(do.BatchMain{ BatchType: consts.BatchTypeRenew, OrderSn: renew.OrderSn, TargetDeptId: renew.Customer.DeptId, BatchInfo: gjson.New(info), UpdatedBy: in.Operator.UserId, UpdatedAt: renew.RenewTime, }). Update() if err != nil { return gerror.Wrap(err, "更新续费批次失败") } return nil } func convertBatchDetailsAfterComplete(ctx context.Context, tx gdb.TX, batchNo string, userId int64) error { _, err := tx.Ctx(ctx).Model(dao.BatchDetail.Table()). Where(dao.BatchDetail.Columns().BatchNo, batchNo). Where(dao.BatchDetail.Columns().BatchType, consts.BatchTypePrepare). Data(do.BatchDetail{ BatchType: consts.BatchTypeRenew, UpdatedBy: userId, UpdatedAt: gtime.Now(), }). Update() if err != nil { return gerror.Wrap(err, "更新续费批次明细类型失败") } return nil } func buildDetailSnapshot(row *Row, card *entity.TSimCardInfo, latestBind, newBind *entity.TSimCardBindPkg, in *CompleteInput, renew *cardpackage.RenewCurrentPackageOutput) g.Map { durationValue := renewDisplayDurationValue(in.SubmitParam) actualDurationMonths := renewActualDurationMonths(in.PriceSnapshot, durationValue) data := g.Map{ "ICCID": card.Iccid, "MSISDN": card.Msisdn, "IMSI": card.Imsi, "renewBatchNo": in.BatchNo, "packageCode": renew.PackageCode, "packageName": renew.Package.PackageName, "oldSubscriptionNo": latestBind.SubscriptionNo, "newSubscriptionNo": newBind.SubscriptionNo, "orderSn": renew.OrderSn, "oldExpectedExpireTime": formatTime(latestBind.ExpectedExpireTime), "renewEffectiveTime": formatTime(newBind.EffectiveTime), "renewExpectedExpireTime": formatTime(newBind.ExpectedExpireTime), "cardFlowPkgEndAt": formatTime(newBind.ExpectedExpireTime), "durationYears": in.SubmitParam.DurationYears, "durationMonths": actualDurationMonths, "durationValue": durationValue, "actualDurationMonths": actualDurationMonths, "giftDurationMonths": in.SubmitParam.GiftDurationMonths, "renewDate": in.RenewDate, "packageBeginTime": in.SubmitParam.PackageBeginTime, "packageEndTime": in.SubmitParam.PackageEndTime, "amount": renew.Amount, "rowNo": row.RowNo, } if sourceBatchNo := strings.TrimSpace(in.SubmitParam.SourceBatchNo); sourceBatchNo != "" { data["sourceBatchNo"] = sourceBatchNo } if in.PriceSnapshot != nil { data["renewUnitPrice"] = in.PriceSnapshot.RenewUnitPrice data["billingMethod"] = in.PriceSnapshot.BillingMethod data["renewDurationUnit"] = in.PriceSnapshot.RenewDurationUnit data["renewDurationUnitText"] = in.PriceSnapshot.RenewDurationUnitText data["priceOwnerDeptId"] = in.PriceSnapshot.PriceOwnerDeptId data["priceSource"] = in.PriceSnapshot.PriceSource } if snapshot := in.SubmitParam.PaymentSnapshot; snapshot != nil { data["paymentMode"] = snapshot.PaymentMode data["payType"] = snapshot.PayType data["payAmount"] = snapshot.PayAmount data["balanceAmount"] = snapshot.BalanceAmount data["balanceBefore"] = snapshot.BalanceBefore data["balanceAfter"] = snapshot.BalanceAfter data["balanceDetailSerialNo"] = snapshot.BalanceDetailSerialNo } return data } func formatTime(t *gtime.Time) string { if t == nil { return "" } return t.Format("Y-m-d H:i:s") }