chore: 并入 iotlcm-worker-svr 历史到 worker-svr/(原提交 96f0f4e)
This commit is contained in:
@@ -0,0 +1,576 @@
|
||||
package renewchange
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
|
||||
rc "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/renewchange"
|
||||
)
|
||||
|
||||
var errRenewAlreadyHandled = errors.New("renew batch was already handled")
|
||||
|
||||
// RenewExecutor executes a confirmed type=101 batch. All business writes and
|
||||
// the success state are committed by one database transaction.
|
||||
type RenewExecutor struct {
|
||||
repo *Repository
|
||||
nowFunc NowFunc
|
||||
}
|
||||
|
||||
func NewRenewExecutor(repo *Repository) *RenewExecutor {
|
||||
if repo == nil {
|
||||
repo = NewRepository()
|
||||
}
|
||||
return &RenewExecutor{repo: repo, nowFunc: timeNow}
|
||||
}
|
||||
|
||||
func (r *RenewExecutor) SetNowFunc(now NowFunc) { r.nowFunc = now }
|
||||
|
||||
func (r *RenewExecutor) Execute(ctx context.Context, batchNo string) error {
|
||||
batchNo = strings.TrimSpace(batchNo)
|
||||
if batchNo == "" {
|
||||
return gerror.New("renew batch number is empty")
|
||||
}
|
||||
|
||||
main, err := r.repo.LoadProcessingBatch(ctx, batchNo, consts.BatchTypeRenew)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(main.OrderSn) != "" {
|
||||
g.Log().Warningf(ctx, "renew batch already has order, skip duplicate: batchNo=%s orderSn=%s", batchNo, main.OrderSn)
|
||||
return nil
|
||||
}
|
||||
details, err := r.repo.LoadRunningDetails(ctx, batchNo, consts.BatchTypeRenew)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "load renew details failed")
|
||||
}
|
||||
if len(details) == 0 {
|
||||
return r.finalizeFailure(ctx, main, "renew batch has no runnable details")
|
||||
}
|
||||
now := pnow(r.nowFunc)
|
||||
orderSN, err := r.apply(ctx, main, details, now)
|
||||
if errors.Is(err, errRenewAlreadyHandled) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return r.finalizeFailure(ctx, main, err.Error())
|
||||
}
|
||||
|
||||
snap, _ := r.repo.ResolveResultSnapshot(ctx, main.BatchNo, main.BatchType)
|
||||
_ = r.repo.WriteResultFileSafe(ctx, main.BatchNo, main.BatchType, main.TenantId, main.CreatedDept, main.CreatedBy, snap)
|
||||
g.Log().Infof(ctx, "renew batch success, batchNo=%s, orderSn=%s, tenant=%s", main.BatchNo, orderSN, main.TenantId)
|
||||
return nil
|
||||
}
|
||||
|
||||
type renewExecution struct {
|
||||
detail *rc.BatchDetailRow
|
||||
card *rc.SimCardInfo
|
||||
bind *rc.SimCardBindPkg
|
||||
begin time.Time
|
||||
end time.Time
|
||||
}
|
||||
|
||||
func (r *RenewExecutor) apply(ctx context.Context, main *rc.BatchMainRow, details []*rc.BatchDetailRow, now time.Time) (string, error) {
|
||||
iccids, err := strictIccidsFromDetails(details)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var orderSN string
|
||||
err = r.repo.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
lockedMain, err := r.repo.Tables().LockRenewBatchMain(ctx, tx, main.BatchNo, consts.BatchTypeRenew)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(lockedMain.OrderSn) != "" || lockedMain.BatchAsyncStatus != consts.BatchAsyncStatusProcessing {
|
||||
return errRenewAlreadyHandled
|
||||
}
|
||||
lockedDetails, err := r.repo.Tables().LockRenewDetails(ctx, tx, main.BatchNo, consts.BatchTypeRenew)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = validateRunningRenewDetails(lockedDetails, details); err != nil {
|
||||
return err
|
||||
}
|
||||
details = lockedDetails
|
||||
|
||||
cards, err := r.repo.Tables().LockCardsByIccids(ctx, tx, iccids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bindRows, err := r.repo.Tables().LockRenewBindRowsByIccids(ctx, tx, iccids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binds, futureBinds, err := resolveCurrentSubscriptions(bindRows, iccids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contract, err := r.ensureRenewExecutionContract(ctx, tx, lockedMain, details, cards, binds, futureBinds, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
executions, err := validateRenewExecutions(contract, details, cards, binds, futureBinds, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg, err := r.repo.Tables().LoadRenewPackage(ctx, tx, contract.TenantID, contract.PackageCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orderSN, err = rc.NextOrderSN(now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
order, err := BuildOrderDO(contract, orderSN, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orderID, err := r.repo.Tables().InsertContractOrder(ctx, tx, order)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "insert renew order failed")
|
||||
}
|
||||
minBegin, maxEnd := renewServiceRange(executions)
|
||||
item, err := BuildOrderItemDO(contract, OrderProductContract{
|
||||
ID: pkg.Id,
|
||||
Code: pkg.PackageCode,
|
||||
Name: pkg.PackageName,
|
||||
Type: pkg.PackageType,
|
||||
}, orderID, orderSN, minBegin, maxEnd, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = r.repo.Tables().InsertContractOrderItem(ctx, tx, item); err != nil {
|
||||
return gerror.Wrap(err, "insert renew order item failed")
|
||||
}
|
||||
|
||||
for _, execution := range executions {
|
||||
subscriptionNo, err := generateRenewSubscriptionNo(now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newBind := buildRenewBind(contract, pkg, execution, orderSN, subscriptionNo, now)
|
||||
if err = r.repo.Tables().InsertBind(ctx, tx, newBind); err != nil {
|
||||
return err
|
||||
}
|
||||
packageStatus := renewPackageStatus(now, execution.card.FlowPkgBeginAt, newBind.ExpectedExpireTime)
|
||||
cardUpdate, err := BuildRenewCardUpdate(contract, execution.end, packageStatus, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = r.repo.Tables().UpdateRenewCard(ctx, tx, execution.card.Iccid, cardUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
snapshot := buildRenewDetailSnapshot(execution, contract, pkg, orderSN, subscriptionNo)
|
||||
if err = r.repo.MarkDetailSuccess(ctx, tx, main.BatchNo, consts.BatchTypeRenew, execution.detail.RowNo, snapshot, contract.OperatorUserID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
info := mergeBatchInfo(lockedMain.BatchInfo, map[string]any{
|
||||
"orderSn": orderSN,
|
||||
"packageCode": contract.PackageCode,
|
||||
"packageName": pkg.PackageName,
|
||||
"packageType": pkg.PackageType,
|
||||
"billingMethod": contract.BillingMethod,
|
||||
"renewDurationUnit": contract.RenewDurationUnit,
|
||||
"renewDurationUnitText": contract.RenewDurationUnitText,
|
||||
"durationValue": contract.DisplayDurationValue(),
|
||||
"actualDurationMonths": contract.DurationMonths,
|
||||
"durationMonths": contract.DurationMonths,
|
||||
"giftDurationMonths": contract.GiftDurationMonths,
|
||||
"amount": contract.TotalAmount(),
|
||||
"quantity": contract.Quantity,
|
||||
"renewTime": now.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
if err = r.repo.UpdateBatchMainInfo(ctx, tx, main.BatchNo, consts.BatchTypeRenew, &rc.BatchMainRow{
|
||||
BatchType: consts.BatchTypeRenew,
|
||||
BatchAsyncStatus: consts.BatchAsyncStatusSuccess,
|
||||
RunResult: consts.BatchRunResultSuccess,
|
||||
TotalCount: lockedMain.TotalCount,
|
||||
ValidCount: len(executions),
|
||||
SuccessCount: len(executions),
|
||||
FailCount: 0,
|
||||
BatchInfo: gjson.New(info),
|
||||
OrderSn: orderSN,
|
||||
StartTime: lockedMain.StartTime,
|
||||
EndTime: gtime.NewFromTime(now),
|
||||
TargetDeptId: contract.CustomerAgentID,
|
||||
UpdatedBy: contract.OperatorUserID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return orderSN, err
|
||||
}
|
||||
|
||||
func validateRenewExecutions(contract *ExecutionContract, details []*rc.BatchDetailRow, cards map[string]*rc.SimCardInfo, binds map[string]*rc.SimCardBindPkg, futureBinds map[string][]*rc.SimCardBindPkg, now time.Time) ([]renewExecution, error) {
|
||||
if contract == nil {
|
||||
return nil, gerror.New("renew execution contract is missing")
|
||||
}
|
||||
if contract.Quantity != len(details) {
|
||||
return nil, gerror.New("renew detail quantity does not match confirmed contract")
|
||||
}
|
||||
seen := make(map[string]struct{}, len(details))
|
||||
result := make([]renewExecution, 0, len(details))
|
||||
for _, detail := range details {
|
||||
if detail == nil {
|
||||
return nil, gerror.New("renew detail is nil")
|
||||
}
|
||||
iccid := strings.TrimSpace(detail.ItemId)
|
||||
if iccid == "" {
|
||||
return nil, gerror.New("renew detail ICCID is empty")
|
||||
}
|
||||
if _, exists := seen[iccid]; exists {
|
||||
return nil, gerror.Newf("renew detail ICCID is duplicated: %s", iccid)
|
||||
}
|
||||
seen[iccid] = struct{}{}
|
||||
card := cards[iccid]
|
||||
if card == nil {
|
||||
return nil, gerror.Newf("renew card not found: %s", iccid)
|
||||
}
|
||||
bind := binds[iccid]
|
||||
if bind == nil {
|
||||
return nil, gerror.Newf("renew latest subscription not found: %s", iccid)
|
||||
}
|
||||
var expectedMSISDN, expectedIMSI string
|
||||
if detail.Snapshot != nil {
|
||||
expectedMSISDN = strings.TrimSpace(detail.Snapshot.Get("msisdn").String())
|
||||
expectedIMSI = strings.TrimSpace(detail.Snapshot.Get("imsi").String())
|
||||
}
|
||||
if err := validateCurrentSubscription(card, bind, subscriptionValidation{
|
||||
ICCID: iccid,
|
||||
MSISDN: expectedMSISDN,
|
||||
IMSI: expectedIMSI,
|
||||
TenantID: contract.TenantID,
|
||||
CustomerDeptID: contract.CustomerAgentID,
|
||||
PackageCode: contract.PackageCode,
|
||||
SourceBatchNo: contract.SourceBatchNo,
|
||||
}); err != nil {
|
||||
return nil, gerror.Wrapf(err, "validate renew current subscription failed, iccid:%s", iccid)
|
||||
}
|
||||
// The latest subscription is the source of truth for the next renewal
|
||||
// start. The legacy Admin transaction did not require the denormalized
|
||||
// card expiration to equal that subscription; future subscriptions can
|
||||
// legitimately make the two values differ.
|
||||
renewBase := renewBaseSubscriptionForCard(card, bind, futureBinds[iccid])
|
||||
if err := validateRenewBaseWindow(renewBase, now, iccid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
begin, ok := renewBeginTimeFromExpire(renewBase.ExpectedExpireTime)
|
||||
if !ok {
|
||||
return nil, gerror.Newf("ICCID没有预计到期时间:%s", strings.TrimSpace(iccid))
|
||||
}
|
||||
if contract.PackageBeginTime != nil {
|
||||
begin = contract.PackageBeginTime.Time
|
||||
}
|
||||
result = append(result, renewExecution{
|
||||
detail: detail,
|
||||
card: card,
|
||||
bind: renewBase,
|
||||
begin: begin,
|
||||
end: contract.ServiceEnd(begin),
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func renewBaseSubscription(current *rc.SimCardBindPkg, future []*rc.SimCardBindPkg) *rc.SimCardBindPkg {
|
||||
base := current
|
||||
for _, bind := range future {
|
||||
if bind == nil {
|
||||
continue
|
||||
}
|
||||
if base == nil || subscriptionAfter(bind, base) {
|
||||
base = bind
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func renewBaseSubscriptionForCard(card *rc.SimCardInfo, current *rc.SimCardBindPkg, future []*rc.SimCardBindPkg) *rc.SimCardBindPkg {
|
||||
base := renewBaseSubscription(current, future)
|
||||
cardExpireAt := latestCardRenewExpireTime(card)
|
||||
if cardExpireAt == nil {
|
||||
return base
|
||||
}
|
||||
if base == nil {
|
||||
return &rc.SimCardBindPkg{
|
||||
Iccid: strings.TrimSpace(card.Iccid),
|
||||
PackageCode: strings.TrimSpace(card.CurrentPackageCode),
|
||||
ExpectedExpireTime: cardExpireAt,
|
||||
}
|
||||
}
|
||||
if base.ExpectedExpireTime != nil && !cardExpireAt.After(base.ExpectedExpireTime) {
|
||||
return base
|
||||
}
|
||||
clone := *base
|
||||
clone.ExpectedExpireTime = cardExpireAt
|
||||
return &clone
|
||||
}
|
||||
|
||||
func latestCardRenewExpireTime(card *rc.SimCardInfo) *gtime.Time {
|
||||
if card == nil {
|
||||
return nil
|
||||
}
|
||||
var expireAt *gtime.Time
|
||||
for _, candidate := range []*gtime.Time{
|
||||
card.FlowPkgEndAt,
|
||||
card.FlowPkgPresentAt,
|
||||
card.FlowPkgRealPresent,
|
||||
} {
|
||||
if candidate == nil || candidate.IsZero() {
|
||||
continue
|
||||
}
|
||||
if expireAt == nil || candidate.After(expireAt) {
|
||||
expireAt = candidate
|
||||
}
|
||||
}
|
||||
return expireAt
|
||||
}
|
||||
|
||||
func validateRenewBaseWindow(base *rc.SimCardBindPkg, now time.Time, iccid string) error {
|
||||
if base == nil || base.ExpectedExpireTime == nil {
|
||||
return gerror.Newf("ICCID没有预计到期时间:%s", strings.TrimSpace(iccid))
|
||||
}
|
||||
if renewExpireAfterAllowedWindow(base.ExpectedExpireTime, now) {
|
||||
return gerror.Newf("%s:%s", renewExpireFailureReason, strings.TrimSpace(iccid))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRunningRenewDetails(locked, loaded []*rc.BatchDetailRow) error {
|
||||
if len(locked) != len(loaded) {
|
||||
return gerror.New("renew detail count changed during execution")
|
||||
}
|
||||
for i, detail := range locked {
|
||||
if detail == nil || detail.DetailStatus != consts.BatchDetailStatusRunning {
|
||||
return gerror.New("renew detail is no longer running")
|
||||
}
|
||||
loadedDetail := loaded[i]
|
||||
if loadedDetail == nil || detail.Id != loadedDetail.Id || detail.RowNo != loadedDetail.RowNo ||
|
||||
strings.TrimSpace(detail.ItemId) != strings.TrimSpace(loadedDetail.ItemId) {
|
||||
return gerror.New("renew detail changed during execution")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func renewServiceRange(executions []renewExecution) (time.Time, time.Time) {
|
||||
minBegin := executions[0].begin
|
||||
maxEnd := executions[0].end
|
||||
for _, execution := range executions[1:] {
|
||||
if execution.begin.Before(minBegin) {
|
||||
minBegin = execution.begin
|
||||
}
|
||||
if execution.end.After(maxEnd) {
|
||||
maxEnd = execution.end
|
||||
}
|
||||
}
|
||||
return minBegin, maxEnd
|
||||
}
|
||||
|
||||
func generateRenewSubscriptionNo(now time.Time) (string, error) {
|
||||
orderSN, err := rc.NextOrderSN(now)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "SUB" + strings.TrimPrefix(orderSN, "SO"), nil
|
||||
}
|
||||
|
||||
func buildRenewBind(contract *ExecutionContract, pkg *rc.FlowPackageInfo, execution renewExecution, orderSN, subscriptionNo string, now time.Time) *rc.SimCardBindPkg {
|
||||
remark := contract.Remark
|
||||
if remark == "" {
|
||||
remark = "card renew subscription"
|
||||
}
|
||||
return &rc.SimCardBindPkg{
|
||||
SubscriptionNo: subscriptionNo,
|
||||
BatchNo: contract.BatchNo,
|
||||
Iccid: execution.card.Iccid,
|
||||
Msisdn: execution.card.Msisdn,
|
||||
DeptId: contract.CustomerAgentID,
|
||||
PackageType: pkg.PackageType,
|
||||
PackageCode: pkg.PackageCode,
|
||||
PreviousPackageCode: pkg.PackageCode,
|
||||
BusinessType: consts.PackageBusinessTypeRenew,
|
||||
EffectiveStatus: consts.EffectiveStatusPending,
|
||||
HandleTime: gtime.NewFromTime(now),
|
||||
EffectiveTime: gtime.NewFromTime(execution.begin),
|
||||
ExpectedExpireTime: gtime.NewFromTime(execution.end),
|
||||
DurationMonths: contract.DurationMonths,
|
||||
GiftDurationMonths: contract.GiftDurationMonths,
|
||||
OrderCode: orderSN,
|
||||
Remark: remark,
|
||||
TenantId: contract.TenantID,
|
||||
CreatedDept: contract.CreatedDeptID,
|
||||
CreatedBy: contract.OperatorUserID,
|
||||
CreatedAt: gtime.NewFromTime(now),
|
||||
UpdatedBy: contract.OperatorUserID,
|
||||
UpdatedAt: gtime.NewFromTime(now),
|
||||
}
|
||||
}
|
||||
|
||||
func buildRenewDetailSnapshot(execution renewExecution, contract *ExecutionContract, pkg *rc.FlowPackageInfo, orderSN, subscriptionNo string) *gjson.Json {
|
||||
data := map[string]any{}
|
||||
if execution.detail.Snapshot != nil {
|
||||
for key, value := range execution.detail.Snapshot.Map() {
|
||||
data[key] = value
|
||||
}
|
||||
}
|
||||
data["iccid"] = execution.card.Iccid
|
||||
data["msisdn"] = execution.card.Msisdn
|
||||
data["imsi"] = execution.card.Imsi
|
||||
data["renewBatchNo"] = contract.BatchNo
|
||||
data["packageCode"] = pkg.PackageCode
|
||||
data["packageName"] = pkg.PackageName
|
||||
data["oldSubscriptionNo"] = execution.bind.SubscriptionNo
|
||||
data["newSubscriptionNo"] = subscriptionNo
|
||||
data["orderSn"] = orderSN
|
||||
data["oldExpectedExpireTime"] = formatRenewTime(execution.bind.ExpectedExpireTime)
|
||||
data["renewEffectiveTime"] = formatRenewTime(gtime.NewFromTime(execution.begin))
|
||||
data["renewExpectedExpireTime"] = formatRenewTime(gtime.NewFromTime(execution.end))
|
||||
data["cardFlowPkgEndAt"] = formatRenewTime(gtime.NewFromTime(execution.end))
|
||||
data["durationValue"] = contract.DisplayDurationValue()
|
||||
data["actualDurationMonths"] = contract.DurationMonths
|
||||
data["durationMonths"] = contract.DurationMonths
|
||||
data["giftDurationMonths"] = contract.GiftDurationMonths
|
||||
data["billingMethod"] = contract.BillingMethod
|
||||
data["renewDurationUnit"] = contract.RenewDurationUnit
|
||||
data["renewDurationUnitText"] = contract.RenewDurationUnitText
|
||||
data["renewDate"] = contract.RenewDate
|
||||
data["amount"] = contract.TotalAmount()
|
||||
data["renewUnitPrice"] = contract.UnitPrice
|
||||
data["priceSource"] = contract.PriceSource
|
||||
data["rowNo"] = execution.detail.RowNo
|
||||
if contract.SourceBatchNo != "" {
|
||||
data["sourceBatchNo"] = contract.SourceBatchNo
|
||||
}
|
||||
return gjson.New(data)
|
||||
}
|
||||
|
||||
func formatRenewTime(value *gtime.Time) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
return value.Format("Y-m-d H:i:s")
|
||||
}
|
||||
|
||||
func renewPackageStatus(now time.Time, begin, end *gtime.Time) int {
|
||||
if begin != nil && begin.After(gtime.NewFromTime(now)) {
|
||||
return consts.PackageStatusPending
|
||||
}
|
||||
if end != nil && !end.After(gtime.NewFromTime(now)) {
|
||||
return consts.PackageStatusExpired
|
||||
}
|
||||
return consts.PackageStatusEffective
|
||||
}
|
||||
|
||||
func strictIccidsFromDetails(details []*rc.BatchDetailRow) ([]string, error) {
|
||||
seen := make(map[string]struct{}, len(details))
|
||||
result := make([]string, 0, len(details))
|
||||
for _, detail := range details {
|
||||
if detail == nil {
|
||||
return nil, gerror.New("renew detail is nil")
|
||||
}
|
||||
iccid := strings.TrimSpace(detail.ItemId)
|
||||
if iccid == "" {
|
||||
return nil, gerror.New("renew detail ICCID is empty")
|
||||
}
|
||||
if _, exists := seen[iccid]; exists {
|
||||
return nil, gerror.Newf("renew detail ICCID is duplicated: %s", iccid)
|
||||
}
|
||||
seen[iccid] = struct{}{}
|
||||
result = append(result, iccid)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// uniqueIccidsFromDetails remains available to the change executor while the
|
||||
// renew path uses strictIccidsFromDetails to reject malformed input.
|
||||
func uniqueIccidsFromDetails(details []*rc.BatchDetailRow) []string {
|
||||
seen := make(map[string]struct{}, len(details))
|
||||
result := make([]string, 0, len(details))
|
||||
for _, detail := range details {
|
||||
if detail == nil {
|
||||
continue
|
||||
}
|
||||
iccid := strings.TrimSpace(detail.ItemId)
|
||||
if iccid == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[iccid]; exists {
|
||||
continue
|
||||
}
|
||||
seen[iccid] = struct{}{}
|
||||
result = append(result, iccid)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *RenewExecutor) finalizeFailure(ctx context.Context, main *rc.BatchMainRow, reason string) error {
|
||||
now := pnow(r.nowFunc)
|
||||
contract, _ := ParseExecutionContract(main)
|
||||
if contract == nil {
|
||||
contract = renewRefundContractFromBatch(main)
|
||||
}
|
||||
nowTime := gtime.NewFromTime(now)
|
||||
err := r.repo.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
lockedMain, err := r.repo.Tables().LockRenewBatchMain(ctx, tx, main.BatchNo, main.BatchType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if contract != nil {
|
||||
if err := refundRenewBalanceIfNeeded(ctx, tx, lockedMain, contract, reason, nowTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := r.repo.MarkAllDetailsFailed(ctx, tx, main.BatchNo, main.BatchType, reason, main.CreatedBy); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.repo.FinalizeBatchMain(ctx, tx, main.BatchNo, main.BatchType, &rc.BatchMainRow{
|
||||
BatchAsyncStatus: consts.BatchAsyncStatusFailed,
|
||||
RunResult: consts.BatchRunResultFailed,
|
||||
TotalCount: main.TotalCount,
|
||||
FailCount: main.TotalCount,
|
||||
FailReason: reason,
|
||||
EndTime: nowTime,
|
||||
UpdatedBy: main.CreatedBy,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "finalize renew failure failed")
|
||||
}
|
||||
snap, _ := r.repo.ResolveResultSnapshot(ctx, main.BatchNo, main.BatchType)
|
||||
_ = r.repo.WriteResultFileSafe(ctx, main.BatchNo, main.BatchType, main.TenantId, main.CreatedDept, main.CreatedBy, snap)
|
||||
g.Log().Warningf(ctx, "renew batch failed, batchNo=%s, reason=%s, tenant=%s", main.BatchNo, reason, main.TenantId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeBatchInfo(prev *gjson.Json, addition map[string]any) map[string]any {
|
||||
out := map[string]any{}
|
||||
if prev != nil {
|
||||
for key, value := range prev.Map() {
|
||||
out[key] = value
|
||||
}
|
||||
}
|
||||
for key, value := range addition {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func pnow(fn NowFunc) time.Time {
|
||||
if fn == nil {
|
||||
return timeNow()
|
||||
}
|
||||
return fn()
|
||||
}
|
||||
Reference in New Issue
Block a user