feat:修改流水记录加上乐观锁

This commit is contained in:
dzc committed 2026-09-11 15:28:21 +08:00
1 parent 9dbd345fc9
commit ddfc970928
2 files changed
+30 -18

No files matched your search

@@ -76,7 +76,7 @@ public class HardApiController {
* 接收食堂、超市 Android 终端的在线心跳,设备身份由请求头中的设备 ID 确认。
*
* @param hardId 当前绑定设备 ID
* @param dto APK 版本与心跳场景
* @param dto APK 版本与心跳场景
* @return 无业务数据的成功响应
*/
@PostMapping("/terminal-heartbeat")
@@ -90,7 +90,7 @@ public class HardApiController {
* 删除当前设备商户部门及子部门范围内的人脸。
*
* @param hardId 当前绑定设备 ID
* @param dto 最多 500 条样本编号
* @param dto 最多 500 条样本编号
* @return 实际删除及已不存在的样本编号
*/
@PostMapping("/face/delete")
@@ -118,7 +118,7 @@ public class HardApiController {
* 保存当前设备检测得到的人脸静态图片质量结果。
*
* @param hardId 当前绑定设备 ID
* @param dto 最多 500 条质量结果,客户端不得指定部门或来源设备
* @param dto 最多 500 条质量结果,客户端不得指定部门或来源设备
* @return 已更新、按版本或状态优先级忽略和已不存在的样本编号
*/
@PostMapping("/face/quality/report")
@@ -239,7 +239,7 @@ public class HardApiController {
* 由当前食堂或超市终端上报其实际连接打印机的型号、VID 和 PID。
*
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param dto Android USB Host 枚举出的打印机信息
* @param dto Android USB Host 枚举出的打印机信息
* @return 保留纸宽、切刀和图片模式后的完整打印机配置
*/
@PostMapping("/printer/info")
@@ -278,7 +278,7 @@ public class HardApiController {
/**
* 业务终端按可信 DEVICE-ID 创建打印任务,禁止请求体跨商户指定来源设备。
*
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param request 订单和打印类型
* @return 创建、复用或重试后的任务
*/
@@ -293,21 +293,21 @@ public class HardApiController {
/**
* 查询当前业务终端为指定订单生成的发票、兑换券及最终打印结果。
*
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param orderId 核销记录中的服务端订单 ID
* @return 当前设备范围内的服务端打印记录,结果不会读取终端本地数据库
*/
@GetMapping("/printer/records")
@Operation(summary = "查询业务终端订单打印记录")
public R<List<HardInvoiceVo>> printerRecords(@RequestHeader("DEVICE-ID") Long hardId,
@RequestParam Long orderId) {
@RequestParam Long orderId) {
return R.ok(hardTerminalPrintService.listRecords(hardId, orderId));
}
/**
* 由当前业务终端对本机已打印的核销兑换券创建下一打印序号。
*
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param request 已打印任务 ID 和操作员填写的重打原因
* @return 新建或并发场景下复用的兑换券打印任务
*/
@@ -321,7 +321,7 @@ public class HardApiController {
@PostMapping("/sync-offline-order")
@Operation(summary = "同步离线订单")
@Idempotent(expireTime = 1, info = "正在处理中,请勿重复操作")
@Idempotent(key = "'/hard-api/ad/sync-offline-order' + #dto.getHardwareId()", expireTime = 1, info = "正在处理中,请勿重复操作")
public R<List<String>> syncOfflineOrder(@Valid @RequestBody SyncOfflineReqVo dto) {
return R.ok(hardOrderSyncService.syncOfflineOrder(dto));
}
@@ -345,7 +345,7 @@ public class HardApiController {
* 按当前设备绑定部门树同步脱敏后的离线人员快照。
*
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
* @param dto 排他游标、分页大小和固定快照上界
* @param dto 排他游标、分页大小和固定快照上界
* @return 当前页离线人员、下一游标和快照上界
*/
@PostMapping("/offline-user/sync-snapshot")
@@ -220,14 +220,20 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
remark = remark.strip();
if (remark.isEmpty()) remark = null;
}
// 传入空实体会跳过 MyBatis-Plus 的修改人和修改时间填充,因此保留更新实体承载审计字段。
// 乐观锁依赖库中当前 version 做 CAS,new 出来的默认 0 会让条件永远匹配不到已递增过的行。
WalletTransactionEntity exist = baseMapper.selectById(dto.getId());
if (exist == null) {
throw new RuntimeException("钱包流水不存在或已删除");
}
// 保留更新实体承载审计字段填充;version 用库中当前值,乐观锁拦截器据此追加条件并自增。
WalletTransactionEntity updateEntity = new WalletTransactionEntity();
updateEntity.setVersion(exist.getVersion());
int updated = baseMapper.update(updateEntity, Wrappers.<WalletTransactionEntity>lambdaUpdate()
.eq(WalletTransactionEntity::getId, dto.getId())
.set(WalletTransactionEntity::getRemark, remark));
// 部分 MySQL affected-row 配置会把“值未变化”返回为 0,仅在记录也查不到时才判定失败。
if (updated == 0 && baseMapper.selectById(dto.getId()) == null) {
throw new RuntimeException("钱包流水不存在或已删除");
// SET 中 version 必然变化,0 行只可能是并发修改导致的版本冲突。
if (updated == 0) {
throw new RuntimeException("钱包流水已被其他操作修改,请刷新后重试");
}
return true;
}
@@ -245,15 +251,21 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
if (deptResult == null || !deptResult.isOk() || ObjectUtil.isEmpty(deptResult.getData())) {
throw new RuntimeException("流水归属部门不存在");
}
// 传入空实体会跳过 MyBatis-Plus 的修改人和修改时间填充,因此保留更新实体承载审计字段。
// 乐观锁依赖库中当前 version 做 CAS,new 出来的默认 0 会让条件永远匹配不到已递增过的行。
WalletTransactionEntity exist = baseMapper.selectById(dto.getId());
if (exist == null) {
throw new RuntimeException("钱包流水不存在或已删除");
}
// 保留更新实体承载审计字段填充;version 用库中当前值,乐观锁拦截器据此追加条件并自增。
WalletTransactionEntity updateEntity = new WalletTransactionEntity();
updateEntity.setVersion(exist.getVersion());
int updated = baseMapper.update(updateEntity, Wrappers.<WalletTransactionEntity>lambdaUpdate()
.eq(WalletTransactionEntity::getId, dto.getId())
.set(WalletTransactionEntity::getDeptId, dto.getDeptId())
.set(WalletTransactionEntity::getCreateTime, dto.getCreateTime()));
// 部分 MySQL affected-row 配置会把“值未变化”返回为 0,仅在记录也查不到时才判定失败。
if (updated == 0 && baseMapper.selectById(dto.getId()) == null) {
throw new RuntimeException("钱包流水不存在或已删除");
// SET 中 version 必然变化,0 行只可能是并发修改导致的版本冲突。
if (updated == 0) {
throw new RuntimeException("钱包流水已被其他操作修改,请刷新后重试");
}
return true;
}