feat:消费机更新
feat:人员调用钱包部门不调动的bug
This commit is contained in:
1 parent
1c86c227ee
commit
2ca14aa1e0
38 files changed
+1903
-35
No files matched your search
+21
@@ -0,0 +1,21 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.hard;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Android 终端批量删除服务端人脸请求。
|
||||
*/
|
||||
@Data
|
||||
public class HardTerminalFaceDeleteDto {
|
||||
|
||||
/**
|
||||
* 待删除的虹软样本编号,设备部门由请求头解析,客户端不得指定。
|
||||
*/
|
||||
@NotNull(message = "人脸样本编号不能为空")
|
||||
@Size(min = 1, max = 500, message = "单次最多删除500条人脸")
|
||||
private List<String> sampleIds;
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.hard;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Android 终端批量上报在库人脸质量请求。
|
||||
*/
|
||||
@Data
|
||||
public class HardTerminalFaceQualityReportDto {
|
||||
|
||||
/**
|
||||
* 本批人脸质量结果;设备和部门身份仅从 DEVICE-ID 解析,单批最多 500 条。
|
||||
*/
|
||||
@Valid
|
||||
@NotEmpty(message = "人脸质量结果不能为空")
|
||||
@Size(max = 500, message = "单次最多上报500条人脸质量结果")
|
||||
private List<HardTerminalFaceQualityReportItemDto> items;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.hard;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Android 终端提交的一条人脸静态图片质量结果。
|
||||
*/
|
||||
@Data
|
||||
public class HardTerminalFaceQualityReportItemDto {
|
||||
|
||||
/**
|
||||
* 虹软 32 位搜索样本编号;终端不得用用户编号替代。
|
||||
*/
|
||||
@NotBlank(message = "人脸样本编号不能为空")
|
||||
private String sampleId;
|
||||
|
||||
/**
|
||||
* 检测状态,跨端协议值仅允许 detected 或 failed。
|
||||
*/
|
||||
@NotBlank(message = "人脸质量检测状态不能为空")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 虹软静态图片质量分,detected 时必填且范围为 0 到 1。
|
||||
*/
|
||||
private BigDecimal score;
|
||||
|
||||
/**
|
||||
* 终端检测所依据的人脸图片版本,来源于服务端人脸快照的 sourceUpdatedAt。
|
||||
*/
|
||||
@NotNull(message = "人脸图片版本不能为空")
|
||||
private LocalDateTime sourceUpdatedAt;
|
||||
|
||||
/**
|
||||
* 终端完成本次检测的本地时间,用于服务端拒绝旧结果覆盖新结果。
|
||||
*/
|
||||
@NotNull(message = "人脸质量检测时间不能为空")
|
||||
private LocalDateTime checkedAt;
|
||||
|
||||
/**
|
||||
* 虹软 SDK 或终端插件错误码,检测成功时为空。
|
||||
*/
|
||||
private Integer errorCode;
|
||||
}
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import com.bsd.pjcloud.admin.api.vo.base.BaseIsIncludeTreeInterface;
|
||||
import com.bsd.pjcloud.admin.api.vo.base.BaseIsIncludeDeptMchTreeInterface;
|
||||
import com.bsd.pjcloud.common.core.constant.enums.DateRangeTypeEnum;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -11,7 +11,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeTreeInterface {
|
||||
public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeDeptMchTreeInterface {
|
||||
|
||||
private DateRangeTypeEnum dateRangeType = DateRangeTypeEnum.YESTERDAY;
|
||||
|
||||
@@ -25,6 +25,8 @@ public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeTreeIn
|
||||
|
||||
private List<Long> merchantDeptIds;
|
||||
|
||||
private Boolean isIncludeMerchantDept = false;
|
||||
|
||||
private List<Long> orderIds;
|
||||
|
||||
private List<Long> hardIds;
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.manager;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 终端申请商户部门人脸质量上报租约的结果。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HardTerminalFaceQualityLeaseVo {
|
||||
|
||||
/**
|
||||
* 当前设备是否取得租约;未取得时必须跳过本轮质量批量上报。
|
||||
*/
|
||||
private boolean granted;
|
||||
|
||||
/**
|
||||
* 当前租约剩余秒数;Redis 未返回有效 TTL 时为 {@code 0}。
|
||||
*/
|
||||
private long remainingSeconds;
|
||||
}
|
||||
+51
@@ -1,6 +1,8 @@
|
||||
package com.bsd.pjcloud.hard.controller.hard;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.bsd.pjcloud.admin.api.vo.SysUserFaceTerminalDeleteVo;
|
||||
import com.bsd.pjcloud.admin.api.vo.SysUserFaceTerminalQualityReportVo;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
@@ -18,6 +20,7 @@ import com.bsd.pjcloud.hard.api.dto.order.HardChartLatitudeVo;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardCreateCarDto;
|
||||
import com.bsd.pjcloud.hard.api.vo.HardInvoiceVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardManagerVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalFaceQualityLeaseVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.android.HardAdUserVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.HardCarVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.HardCreateHardQrCodeScanOrderVo;
|
||||
@@ -57,6 +60,11 @@ public class HardApiController {
|
||||
*/
|
||||
private final HardTerminalStatusService hardTerminalStatusService;
|
||||
|
||||
/**
|
||||
* 终端在库人脸维护服务。
|
||||
*/
|
||||
private final HardTerminalFaceService hardTerminalFaceService;
|
||||
|
||||
@PostMapping("/bind")
|
||||
@Operation(summary = "绑定服务端设备")
|
||||
public R<HardManagerVo> bind(@RequestBody HardBindDto dto) {
|
||||
@@ -77,6 +85,49 @@ public class HardApiController {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前设备商户部门及子部门范围内的人脸。
|
||||
*
|
||||
* @param hardId 当前绑定设备 ID
|
||||
* @param dto 最多 500 条样本编号
|
||||
* @return 实际删除及已不存在的样本编号
|
||||
*/
|
||||
@PostMapping("/face/delete")
|
||||
@Operation(summary = "终端批量删除人脸")
|
||||
public R<SysUserFaceTerminalDeleteVo> deleteTerminalFaces(
|
||||
@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardTerminalFaceDeleteDto dto) {
|
||||
return R.ok(hardTerminalFaceService.delete(hardId, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 竞争当前商户及绑定部门的人脸质量上报租约。
|
||||
*
|
||||
* @param hardId 当前绑定设备 ID
|
||||
* @return 是否允许当前设备执行本轮质量上报
|
||||
*/
|
||||
@PostMapping("/face/quality/lease")
|
||||
@Operation(summary = "终端申请人脸质量上报租约")
|
||||
public R<HardTerminalFaceQualityLeaseVo> acquireTerminalFaceQualityLease(
|
||||
@RequestHeader("DEVICE-ID") Long hardId) {
|
||||
return R.ok(hardTerminalFaceService.acquireQualityLease(hardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存当前设备检测得到的人脸静态图片质量结果。
|
||||
*
|
||||
* @param hardId 当前绑定设备 ID
|
||||
* @param dto 最多 500 条质量结果,客户端不得指定部门或来源设备
|
||||
* @return 已更新、按版本或状态优先级忽略和已不存在的样本编号
|
||||
*/
|
||||
@PostMapping("/face/quality/report")
|
||||
@Operation(summary = "终端批量上报人脸质量")
|
||||
public R<SysUserFaceTerminalQualityReportVo> reportTerminalFaceQuality(
|
||||
@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardTerminalFaceQualityReportDto dto) {
|
||||
return R.ok(hardTerminalFaceService.reportQuality(hardId, dto));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取硬件配置信息")
|
||||
public R<HardManagerVo> get(@RequestParam Long id) {
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.bsd.pjcloud.hard.service;
|
||||
|
||||
import com.bsd.pjcloud.admin.api.vo.SysUserFaceTerminalDeleteVo;
|
||||
import com.bsd.pjcloud.admin.api.vo.SysUserFaceTerminalQualityReportVo;
|
||||
import com.bsd.pjcloud.hard.api.dto.hard.HardTerminalFaceDeleteDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.hard.HardTerminalFaceQualityReportDto;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalFaceQualityLeaseVo;
|
||||
|
||||
/**
|
||||
* 终端在库人脸维护服务。
|
||||
*/
|
||||
public interface HardTerminalFaceService {
|
||||
|
||||
/**
|
||||
* 以 DEVICE-ID 解析真实商户部门后请求用户服务删除人脸。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @param dto 样本编号集合
|
||||
* @return 实际删除及已不存在的样本编号
|
||||
*/
|
||||
SysUserFaceTerminalDeleteVo delete(Long hardId, HardTerminalFaceDeleteDto dto);
|
||||
|
||||
/**
|
||||
* 以 DEVICE-ID 竞争当前商户部门的人脸质量上报租约。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @return 是否取得租约及当前租约剩余时间
|
||||
*/
|
||||
HardTerminalFaceQualityLeaseVo acquireQualityLease(Long hardId);
|
||||
|
||||
/**
|
||||
* 以 DEVICE-ID 解析真实设备和商户部门后请求用户服务保存人脸质量结果。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @param dto 最多 500 条质量结果
|
||||
* @return 已更新、按版本或状态优先级忽略和已不存在的样本编号
|
||||
*/
|
||||
SysUserFaceTerminalQualityReportVo reportQuality(Long hardId, HardTerminalFaceQualityReportDto dto);
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
package com.bsd.pjcloud.hard.service.impl.manager;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.bsd.pjcloud.admin.api.dto.SysUserFaceQualityReportItemDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.SysUserFaceTerminalDeleteDto;
|
||||
import com.bsd.pjcloud.admin.api.dto.SysUserFaceTerminalQualityReportDto;
|
||||
import com.bsd.pjcloud.admin.api.feign.RemoteUserFaceService;
|
||||
import com.bsd.pjcloud.admin.api.vo.SysUserFaceTerminalDeleteVo;
|
||||
import com.bsd.pjcloud.admin.api.vo.SysUserFaceTerminalQualityReportVo;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.hard.api.dto.hard.HardTerminalFaceDeleteDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.hard.HardTerminalFaceQualityReportDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.hard.HardTerminalFaceQualityReportItemDto;
|
||||
import com.bsd.pjcloud.hard.api.entity.manager.HardManagerEntity;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardManagerVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalFaceQualityLeaseVo;
|
||||
import com.bsd.pjcloud.hard.mapper.manager.HardManagerMapper;
|
||||
import com.bsd.pjcloud.hard.service.HardManagerService;
|
||||
import com.bsd.pjcloud.hard.service.HardTerminalFaceService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 终端在库人脸维护服务实现。
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class HardTerminalFaceServiceImpl implements HardTerminalFaceService {
|
||||
|
||||
/**
|
||||
* 商户部门人脸质量上报租约 Redis 键前缀,租户隔离由 Redis 基础设施统一处理。
|
||||
*/
|
||||
private static final String FACE_QUALITY_LEASE_KEY_PREFIX = "hard:face-quality:reporter:";
|
||||
|
||||
/**
|
||||
* 单台终端持有人脸质量上报资格的时长,覆盖常规批量上报且允许异常设备自动让位。
|
||||
*/
|
||||
private static final Duration FACE_QUALITY_LEASE_TTL = Duration.ofMinutes(10);
|
||||
|
||||
/**
|
||||
* 设备 Mapper,用于在当前租户内验证 DEVICE-ID。
|
||||
*/
|
||||
private final HardManagerMapper hardManagerMapper;
|
||||
|
||||
/**
|
||||
* 设备服务,用于解析与终端人脸同步一致的商户部门。
|
||||
*/
|
||||
private final HardManagerService hardManagerService;
|
||||
|
||||
/**
|
||||
* UPMS 人脸远程服务,负责最终部门树范围校验和事务删除。
|
||||
*/
|
||||
private final RemoteUserFaceService remoteUserFaceService;
|
||||
|
||||
/**
|
||||
* Redis 字符串客户端,用于在同一商户部门的多台终端之间选出本轮唯一上报者。
|
||||
*/
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* 根据当前设备真实绑定关系删除部门范围内人脸。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @param dto 样本编号集合
|
||||
* @return 实际删除及已不存在的样本编号
|
||||
*/
|
||||
@Override
|
||||
public SysUserFaceTerminalDeleteVo delete(Long hardId, HardTerminalFaceDeleteDto dto) {
|
||||
HardManagerVo trustedDevice = resolveTrustedDevice(hardId);
|
||||
|
||||
SysUserFaceTerminalDeleteDto request = new SysUserFaceTerminalDeleteDto();
|
||||
request.setMerchantDeptId(trustedDevice.getMerchantDeptId());
|
||||
request.setSampleIds(dto.getSampleIds());
|
||||
R<SysUserFaceTerminalDeleteVo> response = remoteUserFaceService.terminalDelete(request);
|
||||
if (response == null || !response.isOk() || response.getData() == null) {
|
||||
String message = response == null || StrUtil.isBlank(response.getMsg())
|
||||
? "用户服务删除人脸失败"
|
||||
: response.getMsg();
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 竞争当前商户部门的人脸质量上报租约;同一设备重复请求时仍视为租约持有者。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @return 是否取得租约及租约剩余秒数
|
||||
*/
|
||||
@Override
|
||||
public HardTerminalFaceQualityLeaseVo acquireQualityLease(Long hardId) {
|
||||
HardManagerVo trustedDevice = resolveTrustedDevice(hardId);
|
||||
String leaseKey = qualityLeaseKey(trustedDevice);
|
||||
boolean granted = tryAcquireQualityLease(leaseKey, hardId);
|
||||
Long remainingSeconds = stringRedisTemplate.getExpire(leaseKey, TimeUnit.SECONDS);
|
||||
return HardTerminalFaceQualityLeaseVo.builder()
|
||||
.granted(granted)
|
||||
.remainingSeconds(remainingSeconds == null || remainingSeconds < 0 ? 0 : remainingSeconds)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前设备真实绑定关系上报部门范围内的人脸质量结果。
|
||||
*
|
||||
* @param hardId 当前设备主键
|
||||
* @param dto 最多 500 条质量结果
|
||||
* @return 已更新、按版本或状态优先级忽略和已不存在的样本编号
|
||||
*/
|
||||
@Override
|
||||
public SysUserFaceTerminalQualityReportVo reportQuality(Long hardId, HardTerminalFaceQualityReportDto dto) {
|
||||
if (dto == null || dto.getItems() == null || dto.getItems().isEmpty()) {
|
||||
throw new RuntimeException("人脸质量结果不能为空");
|
||||
}
|
||||
HardManagerVo trustedDevice = resolveTrustedDevice(hardId);
|
||||
if (!tryAcquireQualityLease(qualityLeaseKey(trustedDevice), hardId)) {
|
||||
throw new RuntimeException("当前设备未取得人脸质量上报租约");
|
||||
}
|
||||
SysUserFaceTerminalQualityReportDto request = new SysUserFaceTerminalQualityReportDto();
|
||||
request.setMerchantDeptId(trustedDevice.getMerchantDeptId());
|
||||
request.setQualityDeviceId(hardId);
|
||||
request.setItems(dto.getItems().stream().map(this::toUserFaceQualityItem).toList());
|
||||
R<SysUserFaceTerminalQualityReportVo> response = remoteUserFaceService.terminalReportQuality(request);
|
||||
if (response == null || !response.isOk() || response.getData() == null) {
|
||||
String message = response == null || StrUtil.isBlank(response.getMsg())
|
||||
? "用户服务保存人脸质量失败"
|
||||
: response.getMsg();
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将终端公开请求转换为 UPMS 内部质量条目,不携带任何客户端部门信息。
|
||||
*
|
||||
* @param item 终端质量结果
|
||||
* @return UPMS 内部质量结果
|
||||
*/
|
||||
private SysUserFaceQualityReportItemDto toUserFaceQualityItem(HardTerminalFaceQualityReportItemDto item) {
|
||||
SysUserFaceQualityReportItemDto converted = new SysUserFaceQualityReportItemDto();
|
||||
converted.setSampleId(item.getSampleId());
|
||||
converted.setStatus(item.getStatus());
|
||||
converted.setScore(item.getScore());
|
||||
converted.setSourceUpdatedAt(item.getSourceUpdatedAt());
|
||||
converted.setCheckedAt(item.getCheckedAt());
|
||||
converted.setErrorCode(item.getErrorCode());
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 DEVICE-ID 对应当前租户真实设备,并解析设备绑定商户与部门。
|
||||
*
|
||||
* @param hardId 请求头中的设备主键
|
||||
* @return 包含服务端可信商户及部门的设备详情
|
||||
*/
|
||||
private HardManagerVo resolveTrustedDevice(Long hardId) {
|
||||
if (hardId == null) {
|
||||
throw new RuntimeException("DEVICE-ID不能为空");
|
||||
}
|
||||
HardManagerEntity hard = hardManagerMapper.selectById(hardId);
|
||||
if (hard == null) {
|
||||
throw new RuntimeException("当前租户下设备不存在");
|
||||
}
|
||||
if (hard.getMerchantId() == null) {
|
||||
throw new RuntimeException("设备尚未绑定商户");
|
||||
}
|
||||
HardManagerVo hardDetail = hardManagerService.getByHardId(hardId);
|
||||
if (hardDetail == null || hardDetail.getMerchantDeptId() == null) {
|
||||
throw new RuntimeException("设备绑定商户缺少部门");
|
||||
}
|
||||
hardDetail.setMerchantId(hard.getMerchantId());
|
||||
return hardDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造商户与绑定部门共同隔离的质量上报租约键。
|
||||
*
|
||||
* @param trustedDevice 已通过 DEVICE-ID 校验的设备详情
|
||||
* @return 不包含人员、人脸或图片信息的 Redis 业务键
|
||||
*/
|
||||
private String qualityLeaseKey(HardManagerVo trustedDevice) {
|
||||
return FACE_QUALITY_LEASE_KEY_PREFIX
|
||||
+ trustedDevice.getMerchantId() + ":" + trustedDevice.getMerchantDeptId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 原子创建租约;租约已存在时仅允许同一 DEVICE-ID 继续完成后续批次。
|
||||
*
|
||||
* @param leaseKey 当前商户部门租约键
|
||||
* @param hardId 当前设备主键
|
||||
* @return 当前设备是否为租约持有者
|
||||
*/
|
||||
private boolean tryAcquireQualityLease(String leaseKey, Long hardId) {
|
||||
String owner = hardId.toString();
|
||||
Boolean acquired = stringRedisTemplate.opsForValue()
|
||||
.setIfAbsent(leaseKey, owner, FACE_QUALITY_LEASE_TTL);
|
||||
return Boolean.TRUE.equals(acquired) || owner.equals(stringRedisTemplate.opsForValue().get(leaseKey));
|
||||
}
|
||||
}
|
||||
+24
-10
@@ -164,7 +164,14 @@
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.merchantDeptIds != null and dto.merchantDeptIds.size() != 0">
|
||||
and a.merchant_dept_id in
|
||||
<choose>
|
||||
<when test="dto.isIncludeMerchantDept != null and dto.isIncludeMerchantDept == false">
|
||||
and a.merchant_dept_id NOT IN
|
||||
</when>
|
||||
<otherwise>
|
||||
and a.merchant_dept_id IN
|
||||
</otherwise>
|
||||
</choose>
|
||||
<foreach collection="dto.merchantDeptIds" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
@@ -275,13 +282,13 @@
|
||||
on w_m.id = a.wallet_id and w_m.tenant_id = a.tenant_id and w_m.del_flag = '0'
|
||||
left join pjcloudx_wallet.wallet_type w_t
|
||||
on w_t.id = w_m.wallet_type_id and w_t.tenant_id = a.tenant_id and w_t.del_flag = '0'
|
||||
left join (select tenant_id, order_id, max(id) as id
|
||||
from hard_after_sale
|
||||
where del_flag = '0'
|
||||
group by tenant_id, order_id) sx
|
||||
on sx.order_id = a.id and sx.tenant_id = a.tenant_id
|
||||
left join hard_after_sale s
|
||||
on s.id = sx.id and s.tenant_id = a.tenant_id and s.del_flag = '0'
|
||||
<!-- 多次退款后只关联最新售后单:相关子查询按 order_id 走索引逐单取最新 ID,避免全表 GROUP BY 派生表物化拖慢分页与 COUNT。 -->
|
||||
left join hard_after_sale s on s.id = (
|
||||
select max(latest_sale.id)
|
||||
from hard_after_sale latest_sale
|
||||
where latest_sale.order_id = a.id
|
||||
and latest_sale.del_flag = '0'
|
||||
)
|
||||
<where>
|
||||
<include refid="whereA"/>
|
||||
<if test="dto.hardTypeCode != null and dto.hardTypeCode != '' ">
|
||||
@@ -306,7 +313,8 @@
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
order by a.create_time desc, a.user_id desc
|
||||
<!-- id 作为唯一兜底排序,避免 (create_time, user_id) 相同的订单跨页读取时重复或遗漏。 -->
|
||||
order by a.create_time desc, a.user_id desc, a.id desc
|
||||
</sql>
|
||||
|
||||
<sql id="pageListCopy">
|
||||
@@ -393,7 +401,13 @@
|
||||
select count(0)
|
||||
from hard_order a
|
||||
left join hard_manager m on m.id = a.hard_id
|
||||
left join hard_after_sale s on s.order_id = a.id
|
||||
<!-- 只关联最新售后单,避免一单多次售后时计数被关联放大。 -->
|
||||
left join hard_after_sale s on s.id = (
|
||||
select max(latest_sale.id)
|
||||
from hard_after_sale latest_sale
|
||||
where latest_sale.order_id = a.id
|
||||
and latest_sale.del_flag = '0'
|
||||
)
|
||||
<where>
|
||||
<include refid="whereA"/>
|
||||
<if test="dto.hardTypeCode != null and dto.hardTypeCode != '' ">
|
||||
|
||||
Reference in new issue
Block a user