feat:优化下单速度
This commit is contained in:
1 parent
0ddf7e5d06
commit
3ca15d8f34
7 files changed
+176
-94
No files matched your search
+6
@@ -89,4 +89,10 @@ public class HardManagerVo extends HardManagerEntity implements BaseDeptInterfac
|
||||
/** 图片指令模式,首选 GS_V0,兼容 ESC_STAR。 */
|
||||
private String imageMode;
|
||||
|
||||
/**
|
||||
* 商户饭卡扇区密码(6 字节 hex);仅下发给已绑定的终端用于 M1 扇区 1 认证,
|
||||
* 管理端详情等其余路径不填充,避免明文扩散。
|
||||
*/
|
||||
private String cardKey;
|
||||
|
||||
}
|
||||
+2
-1
@@ -151,7 +151,8 @@ public class HardApiController {
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取硬件配置信息")
|
||||
public R<HardManagerVo> get(@RequestParam Long id) {
|
||||
return R.ok(hardManagerService.getByHardId(id));
|
||||
// 终端借此同步商户饭卡密码(mch_manage.card_key),管理端详情仍走不携带密钥的原方法。
|
||||
return R.ok(hardManagerService.getByHardId(id, true));
|
||||
}
|
||||
|
||||
@GetMapping("/get-by-order-id")
|
||||
|
||||
+8
@@ -33,6 +33,14 @@ public interface HardManagerService extends IService<HardManagerEntity> {
|
||||
|
||||
HardManagerVo getByHardId(Long hardId);
|
||||
|
||||
/**
|
||||
* 查询设备详情;终端路径 includeCardKey 传 true 时额外携带商户饭卡密码。
|
||||
*
|
||||
* @param hardId 设备 ID
|
||||
* @param includeCardKey 是否填充 mch_manage.card_key,仅限 /hard-api/ad 终端接口使用
|
||||
*/
|
||||
HardManagerVo getByHardId(Long hardId, boolean includeCardKey);
|
||||
|
||||
HardManagerVo getByUniqueCode(String uniqueCode);
|
||||
|
||||
/**
|
||||
|
||||
+10
-1
@@ -231,11 +231,17 @@ public class HardManagerServiceImpl extends ServiceImpl<HardManagerMapper, HardM
|
||||
}
|
||||
HardManagerEntity updateObj = HardManagerEntity.builder().id(detail.getId()).uniqueCode(dto.getUniqueCode()).build();
|
||||
baseMapper.updateById(updateObj);
|
||||
return getByHardId(dto.getId());
|
||||
// bind 仅被 /hard-api/ad 终端接口调用,直接携带饭卡密码供终端同步。
|
||||
return getByHardId(dto.getId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HardManagerVo getByHardId(Long hardId) {
|
||||
return getByHardId(hardId, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HardManagerVo getByHardId(Long hardId, boolean includeCardKey) {
|
||||
HardManagerEntity hard = baseMapper.selectById(hardId);
|
||||
if (ObjectUtil.isEmpty(hard)) {
|
||||
return null;
|
||||
@@ -252,6 +258,9 @@ public class HardManagerServiceImpl extends ServiceImpl<HardManagerMapper, HardM
|
||||
if (ObjectUtil.isNotEmpty(mch)) {
|
||||
WalletDataUtil.convertMch(vo, mch);
|
||||
vo.setMerchantDeptId(mch.getDeptId());
|
||||
if (includeCardKey) {
|
||||
vo.setCardKey(mch.getCardKey());
|
||||
}
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(hard.getHardGroupId())) {
|
||||
HardUtil.convertHardGroup(vo, hardGroupMapper.selectById(hard.getHardGroupId()));
|
||||
|
||||
+147
-89
@@ -824,94 +824,90 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
|
||||
/* ************************************* start 支付相关 start ************************************* */
|
||||
@Override
|
||||
// @GlobalTransactional
|
||||
// 终端超时重试携带同一 androidOrderCode 再次请求,按本地订单号加锁串行化,
|
||||
// 防止上一次请求仍在处理(曾观测到 15~47 秒)时重试穿透幂等复用再次建单扣款。
|
||||
// expire 覆盖远超正常耗时的抖动窗口,避免锁先于长事务过期。
|
||||
// 事务瘦身:商户/设备/用户/钱包/商品价格等只读查询统一放到无事务的 prepareCreateOrder,
|
||||
// 数据库事务只包住建单、扣库存、钱包扣款、核销等写副作用,避免餐峰远程查询慢
|
||||
// 把本地事务与数据库行锁拖长至几十秒(重复扣款事故的放大器)。
|
||||
@Lock4j(keys = {"#dto.androidOrderCode"}, expire = 90000)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createOrder(HardCreateOrderDto dto) {
|
||||
MchManageVo mch = validateCreateOrderDto(dto);
|
||||
if (ObjectUtil.isEmpty(mch)) throw new RuntimeException("机器绑定的商户不存在");
|
||||
// 是否需要钱包支付
|
||||
boolean isWalletPay = isPay(dto.getOrderType(), dto.getOrderPayType());
|
||||
// 是否为线下支付
|
||||
boolean isOffline = HardPayTypeEnum.isOffline.contains(dto.getOrderPayType());
|
||||
|
||||
// 是否需要核销
|
||||
boolean isVerify = isVerify(dto.getOrderPayType());
|
||||
// 是否微信支付
|
||||
boolean isWeChatPay = ObjectUtil.equals(dto.getOrderPayType(), HardPayTypeEnum.WECHANT_SELF_QR_CODE_PAY);
|
||||
// 是否支付宝支付
|
||||
boolean isAliPay = ObjectUtil.equals(dto.getOrderPayType(), HardPayTypeEnum.ALI_SELF_QR_CODE_PAY);
|
||||
// 查询用户
|
||||
UserInfo userDetail = null;
|
||||
SysUser sysUser = null;
|
||||
if (isWalletPay || isVerify) {
|
||||
userDetail = userService.getInnerByUserId(dto.getUserId()).getData();
|
||||
if (ObjectUtil.isEmpty(userDetail)) throw new I18nBizException(ErrorCodes.COMMON_USER_NOT_EXIST_DUP);
|
||||
sysUser = userDetail.getSysUser();
|
||||
CreateOrderContext ctx = prepareCreateOrder(dto);
|
||||
if (ObjectUtil.isNotEmpty(ctx.reusablePaidOrder)) {
|
||||
return ctx.reusablePaidOrder.getId();
|
||||
}
|
||||
// 是否需要支付额外金额
|
||||
boolean isExtraPrice = isExtraPrice(dto.getSysType());
|
||||
// 默认使用该商户的钱包如果没有就用饭卡钱包
|
||||
WalletManageEntity wallet = null;
|
||||
if (isWalletPay) {
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletId()) && ObjectUtil.equals(dto.getSysType(), HardTypeEnum.SUPERMARKET)) {
|
||||
R<WalletManageVo> walletResult = remoteWalletService.getWalletById(dto.getWalletId());
|
||||
wallet = Optional.of(walletResult).map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
} else {
|
||||
if (ObjectUtil.isEmpty(mch.getDefaultWalletTypeId())) {
|
||||
if (dto.getSysType().equals(HardTypeEnum.CANTEEN)) {
|
||||
R<WalletManageEntity> walletResult = remoteWalletService.getWallet(dto.getUserId(), WalletTypeEnum.EAT);
|
||||
wallet = Optional.of(walletResult).map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
} else {
|
||||
R<WalletManageEntity> walletResult = remoteWalletService.getWallet(dto.getUserId(), WalletTypeEnum.PAY);
|
||||
wallet = Optional.of(walletResult).map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
}
|
||||
} else {
|
||||
R<WalletManageEntity> walletResult = remoteWalletService.getWalletByWalletTypeId(dto.getUserId(), mch.getDefaultWalletTypeId());
|
||||
wallet = Optional.of(walletResult).map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
}
|
||||
}
|
||||
if (ObjectUtil.isEmpty(wallet)) {
|
||||
throw new I18nBizException(ErrorCodes.COMMON_WALLET_NOT_FOUND);
|
||||
} else {
|
||||
if (ObjectUtil.isNotEmpty(wallet.getUserId()) && ObjectUtil.isNotEmpty(dto.getUserId())) {
|
||||
if (ObjectUtil.notEqual(wallet.getUserId(), dto.getUserId())) {
|
||||
throw new I18nBizException(ErrorCodes.HARD_USER_NO_WALLET);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isVerify) {
|
||||
return getSelf().createOrderInTransaction(dto, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单只读准备:商户与设备校验、用户与钱包查询、核销码校验、金额与商品摘要计算、
|
||||
* 同号已支付订单幂等复用检查。全部为只读的远程与本地查询,刻意放在数据库事务之外执行;
|
||||
* 钱包余额仅做快速失败预检,权威校验在钱包服务扣款 SQL 的余额条件中。
|
||||
*/
|
||||
private CreateOrderContext prepareCreateOrder(HardCreateOrderDto dto) {
|
||||
CreateOrderContext ctx = new CreateOrderContext();
|
||||
ctx.mch = validateCreateOrderDto(dto);
|
||||
if (ObjectUtil.isEmpty(ctx.mch)) throw new RuntimeException("机器绑定的商户不存在");
|
||||
// 是否需要钱包支付
|
||||
ctx.walletPay = isPay(dto.getOrderType(), dto.getOrderPayType());
|
||||
// 是否为线下支付
|
||||
ctx.offline = HardPayTypeEnum.isOffline.contains(dto.getOrderPayType());
|
||||
// 是否需要核销
|
||||
ctx.verify = isVerify(dto.getOrderPayType());
|
||||
// 是否微信支付
|
||||
ctx.weChatPay = ObjectUtil.equals(dto.getOrderPayType(), HardPayTypeEnum.WECHANT_SELF_QR_CODE_PAY);
|
||||
// 是否支付宝支付
|
||||
ctx.aliPay = ObjectUtil.equals(dto.getOrderPayType(), HardPayTypeEnum.ALI_SELF_QR_CODE_PAY);
|
||||
// 查询用户
|
||||
if (ctx.walletPay || ctx.verify) {
|
||||
ctx.userDetail = userService.getInnerByUserId(dto.getUserId()).getData();
|
||||
if (ObjectUtil.isEmpty(ctx.userDetail)) throw new I18nBizException(ErrorCodes.COMMON_USER_NOT_EXIST_DUP);
|
||||
ctx.sysUser = ctx.userDetail.getSysUser();
|
||||
}
|
||||
WalletPaymentSnapshot wallet = null;
|
||||
if (ctx.walletPay) {
|
||||
// 默认使用该商户的钱包如果没有就用饭卡钱包
|
||||
wallet = resolvePaymentWallet(dto, ctx.mch);
|
||||
ctx.walletId = wallet.walletId();
|
||||
} else if (ctx.verify) {
|
||||
// 判断该二维码有没有被核销
|
||||
boolean isCancel = validateCodeIsCancel(dto.getOrderType(), dto.getOrderSourceTypeId());
|
||||
if (isCancel) throw new I18nBizException(ErrorCodes.HARD_QR_ALREADY_VERIFIED);
|
||||
}
|
||||
int totalPrice = 0, extraPrice = 0, payPrice = 0;
|
||||
totalPrice = hardCarService.computerCarTotalPrice(dto.getSysType(), dto.getOrderType(), dto.getList());
|
||||
if (isExtraPrice) extraPrice = computeExtraPrice(totalPrice, mch, sysUser);
|
||||
payPrice = totalPrice + extraPrice;
|
||||
// 是否需要支付额外金额
|
||||
boolean isExtraPrice = isExtraPrice(dto.getSysType());
|
||||
ctx.totalPrice = hardCarService.computerCarTotalPrice(dto.getSysType(), dto.getOrderType(), dto.getList());
|
||||
ctx.extraPrice = isExtraPrice ? computeExtraPrice(ctx.totalPrice, ctx.mch, ctx.sysUser) : 0;
|
||||
ctx.payPrice = ctx.totalPrice + ctx.extraPrice;
|
||||
// 商品描述(建单落库与幂等复用校验共用,准备阶段计算一次)
|
||||
ctx.subject = hardCarService.computerCarSubject(dto.getSysType(), dto.getOrderType(), dto.getList());
|
||||
// 幂等复用:终端请求超时后重试会携带同一 androidOrderCode,若服务端已存在同号已支付
|
||||
// 订单,直接返回原订单收尾(余额校验前返回,避免重复扣款后余额不足把重试误判为失败)。
|
||||
HardOrderEntity reusablePaidOrder = getReusablePaidOrder(dto);
|
||||
if (ObjectUtil.isNotEmpty(reusablePaidOrder)) {
|
||||
String subject = hardCarService.computerCarSubject(dto.getSysType(), dto.getOrderType(), dto.getList());
|
||||
validateReusablePaidOrder(reusablePaidOrder, dto, totalPrice, extraPrice, subject);
|
||||
ctx.reusablePaidOrder = getReusablePaidOrder(dto);
|
||||
if (ObjectUtil.isNotEmpty(ctx.reusablePaidOrder)) {
|
||||
validateReusablePaidOrder(ctx.reusablePaidOrder, dto, ctx.totalPrice, ctx.extraPrice, ctx.subject);
|
||||
log.info("|{}|终端订单号重复提交命中已支付订单,直接返回原订单|androidOrderCode:{}|orderId:{}|",
|
||||
TAG, dto.getAndroidOrderCode(), reusablePaidOrder.getId());
|
||||
return reusablePaidOrder.getId();
|
||||
TAG, dto.getAndroidOrderCode(), ctx.reusablePaidOrder.getId());
|
||||
return ctx;
|
||||
}
|
||||
if (isWalletPay) {
|
||||
if (ctx.walletPay) {
|
||||
// 如果是支付的话,不能小于等于0
|
||||
if (payPrice <= 0) throw new I18nBizException(ErrorCodes.HARD_PAY_AMOUNT_ZERO);
|
||||
// 判断余额够不够支付
|
||||
if (wallet.getBalance() < payPrice) throw new I18nBizException(ErrorCodes.COMMON_WALLET_BALANCE_INSUFFICIENT);
|
||||
if (ctx.payPrice <= 0) throw new I18nBizException(ErrorCodes.HARD_PAY_AMOUNT_ZERO);
|
||||
// 判断余额够不够支付(准备阶段快照预检,权威校验在钱包扣款 SQL 的余额条件)
|
||||
if (wallet.balance() < ctx.payPrice) throw new I18nBizException(ErrorCodes.COMMON_WALLET_BALANCE_INSUFFICIENT);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单写副作用事务:购物车与订单落库、库存扣减、钱包扣款、核销、消息与播报收尾。
|
||||
* 与 prepareCreateOrder 配合,事务内不再包含任何只读远程查询。
|
||||
*/
|
||||
// @GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createOrderInTransaction(HardCreateOrderDto dto, CreateOrderContext ctx) {
|
||||
// 添加购物车
|
||||
Long parentCarId = hardCarService.createCard(dto.getUserId(), dto.getMerchantId(), dto.getSysType(), dto.getOrderType(), dto.getList());
|
||||
// 获取商品描述
|
||||
String subject = hardCarService.computerCarSubject(dto.getSysType(), dto.getOrderType(), dto.getList());
|
||||
// 添加硬件订单
|
||||
HardOrderEntity orderObj = HardOrderEntity.builder()
|
||||
.androidOrderCode(dto.getAndroidOrderCode())
|
||||
@@ -921,59 +917,59 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
.hardId(dto.getHardwareId())
|
||||
.orderStatus(HardOrderStatusEnum.CREATE.getType())
|
||||
.merchantId(dto.getMerchantId())
|
||||
.merchantType(mch.getTypes())
|
||||
.merchantDeptId(mch.getDeptId())
|
||||
.merchantType(ctx.mch.getTypes())
|
||||
.merchantDeptId(ctx.mch.getDeptId())
|
||||
.deptId(dto.getDeptId())
|
||||
.orderType(dto.getOrderType().getType())
|
||||
.payType(dto.getOrderPayType().getType())
|
||||
.orderSourceTypeId(dto.getOrderSourceTypeId())
|
||||
.totalPrice(totalPrice)
|
||||
.extraPrice(extraPrice)
|
||||
.payPrice(totalPrice + extraPrice)
|
||||
.totalPrice(ctx.totalPrice)
|
||||
.extraPrice(ctx.extraPrice)
|
||||
.payPrice(ctx.payPrice)
|
||||
.isOfflineOrder(YesNoEnum.NO.getCode())
|
||||
.apptUseDate(LocalDate.now())
|
||||
.productDesc(subject)
|
||||
.productDesc(ctx.subject)
|
||||
.build();
|
||||
if (ObjectUtil.isNotEmpty(sysUser)) {
|
||||
orderObj.setUserId(sysUser.getUserId());
|
||||
orderObj.setUserDeptId(sysUser.getDeptId());
|
||||
if (ObjectUtil.isNotEmpty(ctx.sysUser)) {
|
||||
orderObj.setUserId(ctx.sysUser.getUserId());
|
||||
orderObj.setUserDeptId(ctx.sysUser.getDeptId());
|
||||
} else {
|
||||
orderObj.setUserId(-1L);
|
||||
}
|
||||
if (isWalletPay) {
|
||||
orderObj.setWalletId(wallet.getId());
|
||||
if (ctx.walletPay) {
|
||||
orderObj.setWalletId(ctx.walletId);
|
||||
}
|
||||
baseMapper.insert(orderObj);
|
||||
registerEvidenceAfterCommit(orderObj, dto);
|
||||
// 扣减库存
|
||||
getSelf().fastenerInventory(orderObj, YesNoEnum.YES);
|
||||
// 钱包支付或者核销
|
||||
if (isVerify || isWalletPay) {
|
||||
if (ctx.verify || ctx.walletPay) {
|
||||
// 钱包扣款
|
||||
getSelf().fastReduceBalance(orderObj);
|
||||
// 核销二维码
|
||||
getSelf().cancelCode(orderObj, userDetail);
|
||||
if (isVerify) {
|
||||
getSelf().cancelCode(orderObj, ctx.userDetail);
|
||||
if (ctx.verify) {
|
||||
recordVerificationTime(orderObj);
|
||||
}
|
||||
}
|
||||
// 现金支付
|
||||
if (isOffline) {
|
||||
if (ctx.offline) {
|
||||
// 更改为支付成功
|
||||
getSelf().orderSuccess(orderObj, Boolean.FALSE);
|
||||
}
|
||||
// 微信支付
|
||||
if (isWeChatPay) {
|
||||
if (ctx.weChatPay) {
|
||||
|
||||
}
|
||||
// 支付宝支付
|
||||
if (isAliPay) {
|
||||
if (ctx.aliPay) {
|
||||
|
||||
}
|
||||
// 发送mq消息
|
||||
getSelf().createOrderAfterSendMqMsg(orderObj.getId(), userDetail);
|
||||
getSelf().createOrderAfterSendMqMsg(orderObj.getId(), ctx.userDetail);
|
||||
// 云音箱播报
|
||||
String userPhone = Optional.of(userDetail).map(UserInfo::getSysUser).map(SysUser -> SysUser.getPhone()).orElse(null);
|
||||
String userPhone = Optional.of(ctx.userDetail).map(UserInfo::getSysUser).map(SysUser -> SysUser.getPhone()).orElse(null);
|
||||
CompletableFuture.runAsync(() -> {
|
||||
if (ObjectUtil.isNotEmpty(userPhone)) {
|
||||
yyxService.send(orderObj.getMerchantId(), orderObj.getPayPrice(), userPhone);
|
||||
@@ -982,6 +978,68 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
return orderObj.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按场景解析支付钱包:超市指定钱包 → 商户默认钱包类型 → 食堂饭卡(EAT)→ 通用钱包(PAY)。
|
||||
* 四个场景互斥,单次下单只发起一次钱包查询 Feign;余额快照仅用于下单前快速失败预检。
|
||||
*/
|
||||
private WalletPaymentSnapshot resolvePaymentWallet(HardCreateOrderDto dto, MchManageVo mch) {
|
||||
WalletPaymentSnapshot snapshot;
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletId()) && ObjectUtil.equals(dto.getSysType(), HardTypeEnum.SUPERMARKET)) {
|
||||
WalletManageVo wallet = Optional.of(remoteWalletService.getWalletById(dto.getWalletId()))
|
||||
.map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
snapshot = new WalletPaymentSnapshot(wallet.getId(), wallet.getUserId(), wallet.getBalance());
|
||||
} else if (ObjectUtil.isEmpty(mch.getDefaultWalletTypeId())) {
|
||||
if (dto.getSysType().equals(HardTypeEnum.CANTEEN)) {
|
||||
WalletManageEntity wallet = Optional.of(remoteWalletService.getWallet(dto.getUserId(), WalletTypeEnum.EAT))
|
||||
.map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
snapshot = new WalletPaymentSnapshot(wallet.getId(), wallet.getUserId(), wallet.getBalance());
|
||||
} else {
|
||||
WalletManageEntity wallet = Optional.of(remoteWalletService.getWallet(dto.getUserId(), WalletTypeEnum.PAY))
|
||||
.map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
snapshot = new WalletPaymentSnapshot(wallet.getId(), wallet.getUserId(), wallet.getBalance());
|
||||
}
|
||||
} else {
|
||||
WalletManageEntity wallet = Optional.of(remoteWalletService.getWalletByWalletTypeId(dto.getUserId(), mch.getDefaultWalletTypeId()))
|
||||
.map(R::getData).orElseThrow(() -> new RuntimeException("用户钱包不存在"));
|
||||
snapshot = new WalletPaymentSnapshot(wallet.getId(), wallet.getUserId(), wallet.getBalance());
|
||||
}
|
||||
if (ObjectUtil.isEmpty(snapshot.walletId())) {
|
||||
throw new I18nBizException(ErrorCodes.COMMON_WALLET_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(snapshot.userId()) && ObjectUtil.isNotEmpty(dto.getUserId())
|
||||
&& ObjectUtil.notEqual(snapshot.userId(), dto.getUserId())) {
|
||||
throw new I18nBizException(ErrorCodes.HARD_USER_NO_WALLET);
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 钱包查询快照:下单准备阶段仅需主键、归属人与余额三项用于建单和预检。
|
||||
*/
|
||||
private record WalletPaymentSnapshot(Long walletId, Long userId, Integer balance) {
|
||||
}
|
||||
|
||||
/**
|
||||
* createOrder 只读准备阶段产出的上下文:校验与查询结果、金额与支付标记,
|
||||
* 供写事务阶段使用,避免事务内重复发起远程只读查询。
|
||||
*/
|
||||
private static final class CreateOrderContext {
|
||||
private MchManageVo mch;
|
||||
private UserInfo userDetail;
|
||||
private SysUser sysUser;
|
||||
private Long walletId;
|
||||
private boolean walletPay;
|
||||
private boolean offline;
|
||||
private boolean verify;
|
||||
private boolean weChatPay;
|
||||
private boolean aliPay;
|
||||
private int totalPrice;
|
||||
private int extraPrice;
|
||||
private int payPrice;
|
||||
private String subject;
|
||||
private HardOrderEntity reusablePaidOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询与当前终端请求同本地订单号、同设备、同商户的已支付订单,供超时重试幂等复用。
|
||||
*
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ public interface WalletMessageOutboxMapper extends PjcloudBaseMapper<WalletMessa
|
||||
int physicalDeleteSuccessBefore(@Param("before") LocalDateTime before, @Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* 物理删除当前租户超过一年的失败终态Outbox,每次限制数量以保留排查窗口。
|
||||
* 物理删除当前租户超过保留期(30天)的失败终态Outbox,每次限制数量以控制锁范围。
|
||||
*
|
||||
* <p>必须在租户上下文内调用(租户拦截器自动附加 tenant_id 条件),
|
||||
* 与成功清理同样沿 (tenant_id, status, create_time) 索引范围删除。</p>
|
||||
|
||||
+2
-2
@@ -64,8 +64,8 @@ public class WalletMessageOutboxServiceImpl implements WalletMessageOutboxServic
|
||||
/** 成功Outbox保留天数。 */
|
||||
private static final long SUCCESS_RETENTION_DAYS = 30L;
|
||||
|
||||
/** 失败终态Outbox保留天数。 */
|
||||
private static final long FAILED_RETENTION_DAYS = 365L;
|
||||
/** 失败终态Outbox保留天数;与成功保留期对齐为30天,风暴期失败事件长期滞留会持续放大表体积与扫描成本。 */
|
||||
private static final long FAILED_RETENTION_DAYS = 30L;
|
||||
|
||||
/** 租户ID列表的本地缓存有效期,单位毫秒;扫描任务每10秒触发,缓存避免每次远程查询租户列表。 */
|
||||
private static final long TENANT_CACHE_TTL_MS = 60_000L;
|
||||
|
||||
Reference in new issue
Block a user