package renewchange import ( "context" "strings" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model/do" ) // LockRenewBatchMain locks the business batch before the renew transaction // starts. This closes the gap between runtime claiming and business writes // when a duplicate message is delivered concurrently. func (r *Repository) LockRenewBatchMain(ctx context.Context, tx gdb.TX, batchNo string, batchType int) (*BatchMainRow, error) { var main BatchMainRow err := tx.Ctx(ctx).Model(TableBatchMain). Where("batch_no", strings.TrimSpace(batchNo)). Where("batch_type", batchType). LockUpdate(). Scan(&main) if err != nil { return nil, gerror.Wrap(err, "lock renew batch main failed") } if main.Id == 0 { return nil, gerror.New("renew batch main not found") } return &main, nil } // LockRenewDetails reloads every detail row inside the business transaction. // The initial consumer read is only a routing hint; the transaction must lock // the exact rows and let the service reject any row that is no longer running. func (r *Repository) LockRenewDetails(ctx context.Context, tx gdb.TX, batchNo string, batchType int) ([]*BatchDetailRow, error) { var rows []*BatchDetailRow if err := tx.Ctx(ctx).Model(TableBatchDetail). Where("batch_no", strings.TrimSpace(batchNo)). Where("batch_type", batchType). OrderAsc("row_no"). LockUpdate(). Scan(&rows); err != nil { return nil, gerror.Wrap(err, "lock renew details failed") } return rows, nil } // LoadRenewPackage reads the package inside the business transaction so the // order item is built from a real package row, not a hand-written snapshot. func (r *Repository) LoadRenewPackage(ctx context.Context, tx gdb.TX, tenantID, packageCode string) (*FlowPackageInfo, error) { var pkg FlowPackageInfo query := tx.Ctx(ctx).Model(TableSimFlowPackageInfo). Where("package_code", strings.TrimSpace(packageCode)) if strings.TrimSpace(tenantID) != "" { query = query.Where("tenant_id", strings.TrimSpace(tenantID)) } query = query.LockUpdate() if err := query.Scan(&pkg); err != nil { return nil, gerror.Wrap(err, "load renew package failed") } if pkg.Id == 0 { return nil, gerror.New("renew package not found") } return &pkg, nil } // UpdateRenewCard writes only the fields allowed by the renew contract. func (r *Repository) UpdateRenewCard(ctx context.Context, tx gdb.TX, iccid string, update *do.TSimCardInfo) error { if strings.TrimSpace(iccid) == "" { return gerror.New("renew card ICCID is empty") } if update == nil { return gerror.New("renew card update is empty") } result, err := tx.Ctx(ctx).Model(TableSimCardInfo). Where("iccid", strings.TrimSpace(iccid)). Data(update). Update() if err != nil { return gerror.Wrap(err, "update renew card failed") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "read renew card update count failed") } if affected != 1 { return gerror.Newf("renew card changed during execution: %s", iccid) } return nil }