11
This commit is contained in:
1 parent
cf35795f3d
commit
dbf48af330
16 files changed
+207
-47
No files matched your search
@@ -0,0 +1,16 @@
|
||||
-- 硬件订单新增独立核销时间。
|
||||
-- 历史二维码核销订单只能使用最后更新时间近似回填;外卖等非核销完成状态不参与回填。
|
||||
-- 新核销订单由业务事务写入准确时间,后续订单更新不再改变该值。
|
||||
|
||||
ALTER TABLE `pjcloudx_hardware`.`hard_order`
|
||||
ADD COLUMN `verification_time` datetime DEFAULT NULL
|
||||
COMMENT '订单实际核销完成时间,后续订单更新不得覆盖' AFTER `refund_status`,
|
||||
ADD KEY `idx_hard_order_verification_time`
|
||||
(`tenant_id`, `verification_time`, `del_flag`);
|
||||
|
||||
UPDATE `pjcloudx_hardware`.`hard_order`
|
||||
SET `verification_time` = `update_time`
|
||||
WHERE ((`order_status` = '20' AND `order_type` IN ('1', '2', '3', '4'))
|
||||
OR `pay_type` IN ('5', '6', '50'))
|
||||
AND `verification_time` IS NULL
|
||||
AND `update_time` IS NOT NULL;
|
||||
+4
-1
@@ -38,9 +38,12 @@ public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeTreeIn
|
||||
|
||||
private LocalDateTime[] createTimeList;
|
||||
|
||||
/** 更新时间范围采用左闭右开区间,供按订单实际状态变更时间查询。 */
|
||||
/** 更新时间范围采用左闭右开区间,供兼容旧调用方按订单状态变更时间查询。 */
|
||||
private LocalDateTime[] updateTimeList;
|
||||
|
||||
/** 订单核销时间闭区间,仅按订单实际核销完成时间查询。 */
|
||||
private LocalDateTime[] verificationTimeList;
|
||||
|
||||
private LocalDateTime[] payTimeList;
|
||||
|
||||
private LocalDateTime[] refundTimeList;
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 终端订单识别证据的订单定位参数。
|
||||
*
|
||||
* <p>消费机现场下单继续使用终端订单号对账;预约核销订单不是当前终端创建的,核销成功后改用
|
||||
* 服务端订单 ID 定位。两种方式都必须结合请求头中的设备 ID 校验订单归属。</p>
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单识别证据的订单定位参数")
|
||||
public abstract class HardOrderEvidenceOrderRefDto extends HardOrderEvidenceMetadataDto {
|
||||
|
||||
/**
|
||||
* 终端本地稳定订单号,用于消费机现场下单后对账;预约核销证据允许为空。
|
||||
*/
|
||||
@Size(max = 128, message = "终端订单号不能超过128个字符")
|
||||
@Schema(description = "终端订单号,与服务端订单ID二选一")
|
||||
private String androidOrderCode;
|
||||
|
||||
/**
|
||||
* 已存在的服务端订单主键,仅用于当前设备已经成功核销的预约订单;现场下单证据允许为空。
|
||||
*/
|
||||
@Positive(message = "服务端订单ID必须大于0")
|
||||
@Schema(description = "服务端订单ID,与终端订单号二选一")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 校验至少提供一种订单定位信息,防止无条件查询或写入订单证据。
|
||||
*
|
||||
* @return 提供终端订单号或服务端订单 ID 时返回 {@code true}
|
||||
*/
|
||||
@AssertTrue(message = "终端订单号和服务端订单ID不能同时为空")
|
||||
public boolean isOrderReferencePresent() {
|
||||
return StrUtil.isNotBlank(androidOrderCode) || orderId != null;
|
||||
}
|
||||
}
|
||||
+1
-9
@@ -12,15 +12,7 @@ import lombok.EqualsAndHashCode;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单证据元数据提交请求")
|
||||
public class HardOrderEvidenceSubmitDto extends HardOrderEvidenceMetadataDto {
|
||||
|
||||
/**
|
||||
* 终端本地稳定订单号,用于接口响应丢失后的服务端订单对账。
|
||||
*/
|
||||
@NotBlank(message = "终端订单号不能为空")
|
||||
@Size(max = 128, message = "终端订单号不能超过128个字符")
|
||||
@Schema(description = "终端订单号")
|
||||
private String androidOrderCode;
|
||||
public class HardOrderEvidenceSubmitDto extends HardOrderEvidenceOrderRefDto {
|
||||
|
||||
/**
|
||||
* 终端请求写入的目标状态,仅允许 PENDING 或 UNAVAILABLE;现场图片上传成功由服务端改为 AVAILABLE。
|
||||
|
||||
+1
-10
@@ -1,7 +1,6 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -12,15 +11,7 @@ import lombok.EqualsAndHashCode;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单现场证据上传请求")
|
||||
public class HardOrderEvidenceUploadDto extends HardOrderEvidenceMetadataDto {
|
||||
|
||||
/**
|
||||
* 终端本地稳定订单号,服务端通过该编号解析真实订单主键。
|
||||
*/
|
||||
@NotBlank(message = "终端订单号不能为空")
|
||||
@Size(max = 128, message = "终端订单号不能超过128个字符")
|
||||
@Schema(description = "终端订单号")
|
||||
private String androidOrderCode;
|
||||
public class HardOrderEvidenceUploadDto extends HardOrderEvidenceOrderRefDto {
|
||||
|
||||
/**
|
||||
* 产生识别证据的终端应用版本,用于问题追溯。
|
||||
|
||||
+6
@@ -235,6 +235,12 @@ public class HardOrderEntity extends Model<HardOrderEntity> {
|
||||
@Schema(description = "退款状态")
|
||||
private String refundStatus;
|
||||
|
||||
/**
|
||||
* 订单核销完成时间;仅在订单实际完成核销时写入,避免被后续退款等订单更新覆盖。
|
||||
*/
|
||||
@Schema(description = "订单核销时间")
|
||||
private LocalDateTime verificationTime;
|
||||
|
||||
/**
|
||||
* 订单取消时间(退款订单)
|
||||
*/
|
||||
|
||||
+2
-2
@@ -39,9 +39,9 @@ public class HardOrderEvidenceImageEntity extends Model<HardOrderEvidenceImageEn
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 终端本地稳定订单号快照,来源于 hard_order.android_order_code。
|
||||
* 证据订单引用快照;现场订单取终端订单号,预约等服务端订单取订单号或服务端主键内部引用。
|
||||
*/
|
||||
@Schema(description = "终端订单号")
|
||||
@Schema(description = "证据订单引用快照")
|
||||
private String androidOrderCode;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public class HardCanteenUserApptOrderVo {
|
||||
@Schema(description = "订单状态协议编码")
|
||||
private String orderStatus;
|
||||
|
||||
/** 订单最近更新时间;已核销订单中作为核销时间展示。 */
|
||||
/** 核销时间兼容字段;优先来源于订单独立核销时间,历史空值回退到订单最近更新时间。 */
|
||||
@Schema(description = "核销时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
+6
-4
@@ -43,14 +43,16 @@ public class HardOrderEvidenceImageController {
|
||||
* 对账终端订单并返回服务端证据状态。
|
||||
*
|
||||
* @param hardId 当前绑定设备主键
|
||||
* @param androidOrderCode 终端本地订单号
|
||||
* @param androidOrderCode 终端本地订单号,预约核销证据允许为空
|
||||
* @param orderId 服务端订单主键,现场下单证据允许为空
|
||||
* @return 服务端订单和证据列表
|
||||
*/
|
||||
@GetMapping("/ad/resolve")
|
||||
@Operation(summary = "按终端订单号对账识别证据")
|
||||
@Operation(summary = "按终端订单号或服务端订单ID对账识别证据")
|
||||
public R<HardOrderEvidenceResolveVo> resolve(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@RequestParam String androidOrderCode) {
|
||||
return R.ok(hardOrderEvidenceImageService.resolve(hardId, androidOrderCode));
|
||||
@RequestParam(required = false) String androidOrderCode,
|
||||
@RequestParam(required = false) Long orderId) {
|
||||
return R.ok(hardOrderEvidenceImageService.resolve(hardId, androidOrderCode, orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-3
@@ -19,13 +19,14 @@ import java.util.List;
|
||||
public interface HardOrderEvidenceImageService extends IService<HardOrderEvidenceImageEntity> {
|
||||
|
||||
/**
|
||||
* 按终端订单号对账服务端订单和已登记证据。
|
||||
* 按终端订单号或服务端订单 ID 对账订单和已登记证据。
|
||||
*
|
||||
* @param hardId 请求头中的当前设备主键
|
||||
* @param androidOrderCode 终端本地稳定订单号
|
||||
* @param androidOrderCode 终端本地稳定订单号,预约核销证据允许为空
|
||||
* @param orderId 服务端订单主键,现场下单证据允许为空
|
||||
* @return 服务端订单及证据状态
|
||||
*/
|
||||
HardOrderEvidenceResolveVo resolve(Long hardId, String androidOrderCode);
|
||||
HardOrderEvidenceResolveVo resolve(Long hardId, String androidOrderCode, Long orderId);
|
||||
|
||||
/**
|
||||
* 提交证据元数据、触发参考图复制或报告永久缺失。
|
||||
|
||||
+28
-1
@@ -22,15 +22,28 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/** 终端预约核销记录查询服务,统一使用订单独立核销时间完成筛选和展示。 */
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VerificationRecordServiceImpl implements VerificationRecordService {
|
||||
|
||||
/** 当前接口支持的预约核销类型协议值。 */
|
||||
private static final String TYPE_RESERVATION = "RESERVATION";
|
||||
|
||||
/** 设备服务,用于校验请求设备存在且与查询设备一致。 */
|
||||
private final HardManagerService hardManagerService;
|
||||
|
||||
/** 订单服务,用于按设备、订单类型、状态和核销时间分页查询。 */
|
||||
private final HardOrderService hardOrderService;
|
||||
|
||||
/**
|
||||
* 分页查询指定设备在日期范围内完成的预约核销记录。
|
||||
*
|
||||
* @param page 分页参数,页码最小为 1、每页最大为 50
|
||||
* @param hardId 当前请求头中的设备主键
|
||||
* @param dto 核销类型、设备和日期范围查询条件
|
||||
* @return 已按核销时间倒序排列的终端核销记录分页
|
||||
*/
|
||||
@Override
|
||||
public Page<VerificationRecordVo> page(Page<?> page, Long hardId, VerificationRecordQueryDto dto) {
|
||||
if (!Objects.equals(hardId, dto.getHardId())) {
|
||||
@@ -57,7 +70,7 @@ public class VerificationRecordServiceImpl implements VerificationRecordService
|
||||
query.setOrderStatusCodes(List.of(HardOrderStatusEnum.CANCEL.getType()));
|
||||
// 活动核销历史上复用了预约订单类型,支付方式范围需明确排除 ACT_CANCEL。
|
||||
query.setExcludeOrderPayTypeCodes(List.of(HardPayTypeEnum.ACT_CANCEL.getType()));
|
||||
query.setUpdateTimeList(new LocalDateTime[]{startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay()});
|
||||
query.setVerificationTimeList(new LocalDateTime[]{startDate.atStartOfDay(), endDate.atTime(23, 59, 59)});
|
||||
|
||||
long current = Math.max(1, page.getCurrent());
|
||||
long size = Math.min(50, Math.max(1, page.getSize()));
|
||||
@@ -71,6 +84,12 @@ public class VerificationRecordServiceImpl implements VerificationRecordService
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将订单查询结果转换为终端核销记录展示对象。
|
||||
*
|
||||
* @param order 已完成核销的预约订单
|
||||
* @return 终端核销记录
|
||||
*/
|
||||
private static VerificationRecordVo convert(HardOrderVo order) {
|
||||
VerificationRecordVo vo = new VerificationRecordVo();
|
||||
vo.setOrderId(order.getId());
|
||||
@@ -87,7 +106,15 @@ public class VerificationRecordServiceImpl implements VerificationRecordService
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析核销记录的展示时间;新数据使用独立核销时间,旧数据按更新时间、支付时间、创建时间依次降级。
|
||||
*
|
||||
* @param order 已核销订单
|
||||
* @return 可供终端展示的核销时间,历史数据全部缺失时返回 {@code null}
|
||||
*/
|
||||
private static LocalDateTime resolveVerificationTime(HardOrderVo order) {
|
||||
if (order.getVerificationTime() != null) return order.getVerificationTime();
|
||||
// 历史订单在新增独立字段前没有核销时间,兼容返回最近更新时间,避免旧记录在终端列表中变为空。
|
||||
if (order.getUpdateTime() != null) return order.getUpdateTime();
|
||||
if (order.getPayTime() != null) return order.getPayTime();
|
||||
return order.getCreateTime();
|
||||
|
||||
+1
-1
@@ -885,7 +885,7 @@ public class HardCanteenApptTotalExportServiceImpl implements HardCanteenApptTot
|
||||
detail.setMerchantName(merchantNameMap.get(order.getMerchantId()));
|
||||
detail.setTotalPrice(order.getTotalPrice());
|
||||
detail.setOrderStatus(order.getOrderStatus());
|
||||
detail.setUpdateTime(order.getUpdateTime());
|
||||
detail.setUpdateTime(order.getVerificationTime() != null ? order.getVerificationTime() : order.getUpdateTime());
|
||||
detail.setUpdateBy(order.getUpdateBy());
|
||||
return detail;
|
||||
}
|
||||
|
||||
+1
-1
@@ -773,7 +773,7 @@ public class HardCanteenApptTotalServiceImpl implements HardCanteenApptTotalServ
|
||||
detail.setMerchantName(merchantNameMap.get(order.getMerchantId()));
|
||||
detail.setTotalPrice(order.getTotalPrice());
|
||||
detail.setOrderStatus(order.getOrderStatus());
|
||||
detail.setUpdateTime(order.getUpdateTime());
|
||||
detail.setUpdateTime(order.getVerificationTime() != null ? order.getVerificationTime() : order.getUpdateTime());
|
||||
detail.setUpdateBy(order.getUpdateBy());
|
||||
return detail;
|
||||
}
|
||||
|
||||
+50
-11
@@ -101,6 +101,11 @@ public class HardOrderEvidenceImageServiceImpl extends ServiceImpl<HardOrderEvid
|
||||
*/
|
||||
private static final String FILE_TYPE = "order-evidence";
|
||||
|
||||
/**
|
||||
* 服务端创建订单缺少终端订单号时使用的证据内部引用前缀;仅写入证据快照,不回写订单主表。
|
||||
*/
|
||||
private static final String SERVER_ORDER_REFERENCE_PREFIX = "SERVER_ORDER_";
|
||||
|
||||
/**
|
||||
* UPMS 人脸图片受信下载接口的路径后缀;其他来源地址一律拒绝。
|
||||
*/
|
||||
@@ -132,11 +137,16 @@ public class HardOrderEvidenceImageServiceImpl extends ServiceImpl<HardOrderEvid
|
||||
private final LockTemplate lockTemplate;
|
||||
|
||||
/**
|
||||
* 按终端订单号解析服务端订单并返回当前证据状态。
|
||||
* 按终端订单号或服务端订单 ID 解析订单并返回当前证据状态。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @param androidOrderCode 终端本地订单号,预约核销证据允许为空
|
||||
* @param orderId 服务端订单主键,现场下单证据允许为空
|
||||
* @return 当前订单状态及已登记证据
|
||||
*/
|
||||
@Override
|
||||
public HardOrderEvidenceResolveVo resolve(Long hardId, String androidOrderCode) {
|
||||
HardOrderEntity order = requireOrder(hardId, androidOrderCode, false);
|
||||
public HardOrderEvidenceResolveVo resolve(Long hardId, String androidOrderCode, Long orderId) {
|
||||
HardOrderEntity order = requireOrder(hardId, androidOrderCode, orderId, false);
|
||||
return HardOrderEvidenceResolveVo.builder()
|
||||
.orderId(order.getId())
|
||||
.orderStatus(order.getOrderStatus())
|
||||
@@ -149,7 +159,7 @@ public class HardOrderEvidenceImageServiceImpl extends ServiceImpl<HardOrderEvid
|
||||
*/
|
||||
@Override
|
||||
public HardOrderEvidenceUploadResultVo submit(Long hardId, HardOrderEvidenceSubmitDto dto) {
|
||||
HardOrderEntity order = requireOrder(hardId, dto.getAndroidOrderCode(), true);
|
||||
HardOrderEntity order = requireOrder(hardId, dto.getAndroidOrderCode(), dto.getOrderId(), true);
|
||||
validateMetadata(dto, order);
|
||||
HardOrderEvidenceStatusEnum requestedStatus = HardOrderEvidenceStatusEnum.valueOfCode(dto.getStatus());
|
||||
if (requestedStatus != HardOrderEvidenceStatusEnum.PENDING
|
||||
@@ -185,7 +195,7 @@ public class HardOrderEvidenceImageServiceImpl extends ServiceImpl<HardOrderEvid
|
||||
*/
|
||||
@Override
|
||||
public HardOrderEvidenceUploadResultVo upload(Long hardId, HardOrderEvidenceUploadDto dto, MultipartFile file) {
|
||||
HardOrderEntity order = requireOrder(hardId, dto.getAndroidOrderCode(), true);
|
||||
HardOrderEntity order = requireOrder(hardId, dto.getAndroidOrderCode(), dto.getOrderId(), true);
|
||||
validateMetadata(dto, order);
|
||||
if (HardOrderEvidenceRoleEnum.FACE_REFERENCE.getCode().equals(dto.getEvidenceRole())) {
|
||||
throw new RuntimeException("后台参考人脸必须由服务端复制,终端不能直接上传");
|
||||
@@ -255,16 +265,29 @@ public class HardOrderEvidenceImageServiceImpl extends ServiceImpl<HardOrderEvid
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验设备存在,并按设备和终端订单号限定订单,避免跨设备探测或写入证据。
|
||||
* 校验设备存在,并按设备和至少一种订单标识限定订单,避免跨设备探测或写入证据。
|
||||
*
|
||||
* <p>预约订单只有在核销成功后才会把 {@code hard_id} 更新为当前设备,因此终端即使提前知道
|
||||
* 服务端订单 ID,也不能在核销前或其他设备上写入证据。</p>
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @param androidOrderCode 终端本地订单号,允许为空
|
||||
* @param orderId 服务端订单主键,允许为空
|
||||
* @param requirePaid 是否要求订单处于已支付、已退款或已核销状态
|
||||
* @return 属于当前设备的订单
|
||||
*/
|
||||
private HardOrderEntity requireOrder(Long hardId, String androidOrderCode, boolean requirePaid) {
|
||||
private HardOrderEntity requireOrder(Long hardId, String androidOrderCode, Long orderId, boolean requirePaid) {
|
||||
if (hardId == null) throw new RuntimeException("DEVICE-ID不能为空");
|
||||
if (StrUtil.isBlank(androidOrderCode)) throw new RuntimeException("终端订单号不能为空");
|
||||
if (StrUtil.isBlank(androidOrderCode) && orderId == null) {
|
||||
throw new RuntimeException("终端订单号和服务端订单ID不能同时为空");
|
||||
}
|
||||
HardManagerEntity hard = hardManagerMapper.selectById(hardId);
|
||||
if (hard == null) throw new RuntimeException("当前租户下设备不存在");
|
||||
HardOrderEntity order = hardOrderMapper.selectOne(new LambdaQueryWrapper<HardOrderEntity>()
|
||||
LambdaQueryWrapper<HardOrderEntity> query = new LambdaQueryWrapper<HardOrderEntity>()
|
||||
.eq(HardOrderEntity::getHardId, hardId)
|
||||
.eq(HardOrderEntity::getAndroidOrderCode, androidOrderCode));
|
||||
.eq(orderId != null, HardOrderEntity::getId, orderId)
|
||||
.eq(StrUtil.isNotBlank(androidOrderCode), HardOrderEntity::getAndroidOrderCode, androidOrderCode);
|
||||
HardOrderEntity order = hardOrderMapper.selectOne(query);
|
||||
if (order == null) throw new RuntimeException("订单不存在或不属于当前设备");
|
||||
if (requirePaid && !HardOrderStatusEnum.getPayPrice().contains(order.getOrderStatus())) {
|
||||
throw new RuntimeException("订单尚未确认支付成功,暂不能保存识别证据");
|
||||
@@ -332,13 +355,29 @@ public class HardOrderEvidenceImageServiceImpl extends ServiceImpl<HardOrderEvid
|
||||
private HardOrderEvidenceImageEntity buildEntity(HardOrderEntity order, HardOrderEvidenceMetadataDto dto, String appVersion) {
|
||||
HardOrderEvidenceImageEntity entity = new HardOrderEvidenceImageEntity();
|
||||
entity.setOrderId(order.getId());
|
||||
entity.setAndroidOrderCode(order.getAndroidOrderCode());
|
||||
// 预约核销订单不是消费机现场创建的,通常没有 android_order_code;历史证据表该列为非空,
|
||||
// 因此保存现有服务端订单号或订单主键生成的内部引用。现场订单仍保留真实终端订单号。
|
||||
entity.setAndroidOrderCode(resolveEvidenceOrderReference(order));
|
||||
entity.setHardId(order.getHardId());
|
||||
entity.setTenantId(TenantContextHolder.getTenantId());
|
||||
fillMetadata(entity, dto, appVersion);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析证据表必须保存的稳定订单引用,避免预约订单因没有终端订单号而无法写入证据。
|
||||
*
|
||||
* @param order 已完成持久化的订单;现场订单应包含终端订单号,服务端订单至少应包含订单号或主键
|
||||
* @return 真实终端订单号、服务端订单号或基于服务端主键生成的内部引用
|
||||
*/
|
||||
private String resolveEvidenceOrderReference(HardOrderEntity order) {
|
||||
if (order == null) throw new RuntimeException("订单证据缺少订单信息");
|
||||
if (StrUtil.isNotBlank(order.getAndroidOrderCode())) return order.getAndroidOrderCode();
|
||||
if (StrUtil.isNotBlank(order.getOrderNo())) return order.getOrderNo();
|
||||
if (order.getId() == null) throw new RuntimeException("订单证据缺少服务端订单ID");
|
||||
return SERVER_ORDER_REFERENCE_PREFIX + order.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新终端识别快照;文件中心字段只能由 storeImage 写入,不能由终端覆盖。
|
||||
*/
|
||||
|
||||
+31
-3
@@ -795,6 +795,9 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
getSelf().fastReduceBalance(orderObj);
|
||||
// 核销二维码
|
||||
getSelf().cancelCode(orderObj, userDetail);
|
||||
if (isVerify) {
|
||||
recordVerificationTime(orderObj);
|
||||
}
|
||||
}
|
||||
// 现金支付
|
||||
if (isOffline) {
|
||||
@@ -3391,6 +3394,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
if (ObjectUtil.isEmpty(errMsg)) errMsg = "核销失败,位置的错误";
|
||||
throw new RuntimeException(errMsg);
|
||||
}
|
||||
recordVerificationTime(orderObj);
|
||||
// 钱包扣款
|
||||
getSelf().fastReduceBalance(orderObj);
|
||||
return orderObj.getId();
|
||||
@@ -3698,11 +3702,13 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
if (isCancel) throw new RuntimeException("该二维码已被核销");
|
||||
// 核销二维码
|
||||
getSelf().cancelCode(order, userInfo);
|
||||
LocalDateTime verificationTime = LocalDateTime.now();
|
||||
HardOrderEntity updateObj = HardOrderEntity.builder()
|
||||
.id(order.getId())
|
||||
.androidOrderCode(dto.getAndroidOrderCode())
|
||||
.hardId(dto.getHardwareId())
|
||||
.orderStatus(HardOrderStatusEnum.CANCEL.getType())
|
||||
.verificationTime(verificationTime)
|
||||
.build();
|
||||
boolean isMealVerification = ObjectUtil.equals(order.getSysType(), HardTypeEnum.CANTEEN.getType())
|
||||
&& ObjectUtil.equals(order.getOrderType(), HardOrderTypeEnum.RESERVE.getType())
|
||||
@@ -3716,7 +3722,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
throw new RuntimeException("订单状态已变化,核销失败");
|
||||
}
|
||||
if (isMealVerification) {
|
||||
createMealVerifiedOutbox(order);
|
||||
createMealVerifiedOutbox(order, verificationTime);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -3776,8 +3782,9 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
* 在预约订单PAY到CANCEL状态转换的同一本地事务内保存用餐核销消息快照。
|
||||
*
|
||||
* @param order 状态转换前的食堂预约订单
|
||||
* @param verificationTime 与订单核销时间一致的业务发生时间,保证消息与订单记录使用同一时刻
|
||||
*/
|
||||
private void createMealVerifiedOutbox(HardOrderEntity order) {
|
||||
private void createMealVerifiedOutbox(HardOrderEntity order, LocalDateTime verificationTime) {
|
||||
if (order.getTenantId() == null || order.getUserId() == null || order.getMerchantId() == null) {
|
||||
throw new IllegalStateException("食堂预约核销缺少租户、用餐人或商户,无法创建消息Outbox");
|
||||
}
|
||||
@@ -3789,7 +3796,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
outbox.setSourceMchId(order.getMerchantId());
|
||||
outbox.setSourceMchName(resolveMessageMerchantName(order.getMerchantId()));
|
||||
outbox.setMealName(StrUtil.blankToDefault(order.getProductDesc(), "预约用餐"));
|
||||
outbox.setOccurredTime(LocalDateTime.now());
|
||||
outbox.setOccurredTime(verificationTime);
|
||||
outbox.setStatus("PENDING");
|
||||
outbox.setAttemptCount(0);
|
||||
if (hardMessageOutboxMapper.insert(outbox) != 1) {
|
||||
@@ -3899,11 +3906,32 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
if (ObjectUtil.isEmpty(isCancelSuccess) || !isCancelSuccess) {
|
||||
throw new RuntimeException("核销失败");
|
||||
}
|
||||
recordVerificationTime(orderObj);
|
||||
// 钱包扣款
|
||||
getSelf().fastReduceBalance(orderObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为核销时新建的交易订单记录独立核销时间,避免后续支付状态更新覆盖业务发生时间。
|
||||
*
|
||||
* @param order 已持久化且对应远程核销已成功的订单
|
||||
*/
|
||||
private void recordVerificationTime(HardOrderEntity order) {
|
||||
if (order == null || order.getId() == null) {
|
||||
throw new IllegalArgumentException("记录订单核销时间时订单不能为空");
|
||||
}
|
||||
LocalDateTime verificationTime = LocalDateTime.now();
|
||||
HardOrderEntity update = HardOrderEntity.builder()
|
||||
.id(order.getId())
|
||||
.verificationTime(verificationTime)
|
||||
.build();
|
||||
if (baseMapper.updateById(update) != 1) {
|
||||
throw new IllegalStateException("记录订单核销时间失败");
|
||||
}
|
||||
order.setVerificationTime(verificationTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
// @GlobalTransactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
||||
+10
@@ -50,6 +50,7 @@
|
||||
a.refund_price,
|
||||
a.refund_time,
|
||||
a.refund_status,
|
||||
a.verification_time,
|
||||
a.apply_cancel_time,
|
||||
a.android_order_code,
|
||||
a.is_offline_order,
|
||||
@@ -196,6 +197,9 @@
|
||||
and a.update_time >= #{dto.updateTimeList[0]}
|
||||
and a.update_time < #{dto.updateTimeList[1]}
|
||||
</if>
|
||||
<if test="dto.verificationTimeList != null and dto.verificationTimeList.length == 2">
|
||||
and a.verification_time between #{dto.verificationTimeList[0]} and #{dto.verificationTimeList[1]}
|
||||
</if>
|
||||
<if test="dto.apptUseDateTimeList != null and dto.apptUseDateTimeList.length == 2">
|
||||
and a.appt_use_date between #{dto.apptUseDateTimeList[0]} and #{dto.apptUseDateTimeList[1]}
|
||||
</if>
|
||||
@@ -315,6 +319,9 @@
|
||||
</if>
|
||||
</where>
|
||||
<choose>
|
||||
<when test="dto.verificationTimeList != null and dto.verificationTimeList.length == 2">
|
||||
order by a.verification_time desc, a.id desc
|
||||
</when>
|
||||
<when test="dto.updateTimeList != null and dto.updateTimeList.length == 2">
|
||||
order by a.update_time desc, a.id desc
|
||||
</when>
|
||||
@@ -338,6 +345,9 @@
|
||||
<include refid="appRelationFilterWhere"/>
|
||||
</where>
|
||||
<choose>
|
||||
<when test="dto.verificationTimeList != null and dto.verificationTimeList.length == 2">
|
||||
order by a.verification_time desc, a.id desc
|
||||
</when>
|
||||
<when test="dto.updateTimeList != null and dto.updateTimeList.length == 2">
|
||||
order by a.update_time desc, a.id desc
|
||||
</when>
|
||||
|
||||
Reference in new issue
Block a user