feat:对账以wallet_transaction表为准
This commit is contained in:
1 parent
b0b16ae58f
commit
c34b249d7f
13 files changed
+611
-188
No files matched your search
+9
@@ -16,6 +16,7 @@ import com.bsd.pjcloud.hard.api.entity.manager.HardManagerEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
|
||||
import com.bsd.pjcloud.hard.api.vo.hard.PanelPorductVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.*;
|
||||
import com.bsd.pjcloud.hard.mapper.order.model.HardOrderDepartmentStatisticsDetail;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@@ -169,6 +170,14 @@ public interface HardOrderMapper extends PjcloudBaseMapper<HardOrderEntity> {
|
||||
|
||||
List<HardOrderSumAndCountVo> refundSumAndCount(@Param("dto") HardOrderSumAndCountDto refundDto);
|
||||
|
||||
/**
|
||||
* 查询部门交易统计的逐笔订单支付明细;钱包支付会按流水历史部门重新归集,SQL 不做部门过滤。
|
||||
*
|
||||
* @param dto 订单状态、商户、时间和钱包类型筛选条件
|
||||
* @return 逐笔支付明细,bizId 为订单 ID 字符串
|
||||
*/
|
||||
List<HardOrderDepartmentStatisticsDetail> listPayStatisticsDetails(@Param("dto") HardOrderSumAndCountDto dto);
|
||||
|
||||
List<HardOrderSumAndCountVo> sumAndCountTotalByHourType(@Param("dto") HardOrderSumAndCountDto dto);
|
||||
|
||||
HardZyCardSyncVo totalYearMonthDay(@Param("dto") HardOrderDto hardOrderDto);
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bsd.pjcloud.hard.mapper.order.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 部门交易统计使用的逐笔订单或售后明细,仅在 Hardware 服务内部参与历史部门重新归集。
|
||||
*/
|
||||
@Data
|
||||
public class HardOrderDepartmentStatisticsDetail {
|
||||
|
||||
/** 钱包流水使用的稳定业务号;支付对应订单 ID,退款对应售后单 ID。 */
|
||||
private String bizId;
|
||||
|
||||
/** 原订单绑定的钱包 ID;非钱包交易和部分历史异常订单允许为空。 */
|
||||
private Long walletId;
|
||||
|
||||
/** 原订单创建时保存的部门 ID,在没有对应钱包流水时作为历史归属回退值。 */
|
||||
private Long deptId;
|
||||
|
||||
/** 单笔支付或退款金额,单位分;历史退款金额为空时由 SQL 转为零。 */
|
||||
private Long sum;
|
||||
|
||||
/** 当前明细对应的统计次数,支付订单和售后记录均固定为一。 */
|
||||
private Long count;
|
||||
}
|
||||
+353
-184
@@ -19,6 +19,7 @@ import com.bsd.pjcloud.admin.api.entity.SysDept;
|
||||
import com.bsd.pjcloud.admin.api.feign.RemoteDeptService;
|
||||
import com.bsd.pjcloud.common.core.util.BeanUtils;
|
||||
import com.bsd.pjcloud.common.core.util.CollectionUtils;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.data.datascope.DataScope;
|
||||
import com.bsd.pjcloud.common.data.datascope.DataScopeFuncEnum;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
@@ -35,15 +36,19 @@ import com.bsd.pjcloud.hard.api.utils.HardUtil;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.*;
|
||||
import com.bsd.pjcloud.hard.mapper.manager.HardManagerMapper;
|
||||
import com.bsd.pjcloud.hard.mapper.order.HardOrderMapper;
|
||||
import com.bsd.pjcloud.hard.mapper.order.model.HardOrderDepartmentStatisticsDetail;
|
||||
import com.bsd.pjcloud.hard.service.HardManagerService;
|
||||
import com.bsd.pjcloud.hard.service.HardOrderTotalService;
|
||||
import com.bsd.pjcloud.hard.utils.RemoteBatchQueryUtil;
|
||||
import com.bsd.pjcloud.wallet.api.constant.WalletBizEnum;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotBatchDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
|
||||
import com.bsd.pjcloud.wallet.api.feign.RemoteMchService;
|
||||
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletTransactionService;
|
||||
import com.bsd.pjcloud.wallet.api.utils.WalletDataUtil;
|
||||
import com.bsd.pjcloud.wallet.api.vo.MchManageVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionSumAndCountVo;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -62,6 +67,7 @@ import java.time.*;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Validated
|
||||
@Slf4j
|
||||
@@ -220,7 +226,7 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
} else {
|
||||
dto.setMchTypeCodes(dto.getMchTypeCodes());
|
||||
}
|
||||
// 查询总流水
|
||||
// 流水与退款的业务发生时间不同,必须分别查询后再按商户合并,避免直接关联造成一笔订单被多笔售后重复放大。
|
||||
HardOrderDto totalDto = BeanUtil.toBean(dto, HardOrderDto.class);
|
||||
totalDto.setOrderStatusCodes(HardOrderStatusEnum.getPayPrice());
|
||||
DataScope totalDataScope = DataScope.of();
|
||||
@@ -230,7 +236,9 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
HardOrderDto refundDto = BeanUtil.toBean(dto, HardOrderDto.class);
|
||||
refundDto.setPayTimeList(null);
|
||||
refundDto.setRefundTimeList(dto.getPayTimeList());
|
||||
refundDto.setOrderStatusCodes(HardOrderStatusEnum.getPayErrorCodes());
|
||||
// refundStatusCodes 属于订单退款状态,售后完成状态必须使用 saleStatusCodes,二者混用会把有效退款全部过滤掉。
|
||||
// 部分退款后订单仍是已支付状态,退款统计不能只查询整单退款状态的订单。
|
||||
refundDto.setOrderStatusCodes(HardOrderStatusEnum.getPayPrice());
|
||||
refundDto.setRefundStatusCodes(List.of(HardSaleStatusEnum.COMPLETE.getStatus()));
|
||||
DataScope refundDataScope = DataScope.of();
|
||||
refundDataScope.setScopeDeptName("merchant_dept_id");
|
||||
@@ -327,7 +335,8 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
refundDto.setRefundTimeList(dto.getPayTimeList());
|
||||
refundDto.setAuditTimeYearStr(dto.getPayTimeYearStr());
|
||||
refundDto.setPayTimeYearStr(null);
|
||||
refundDto.setOrderStatusCodes(HardOrderStatusEnum.getPayErrorCodes());
|
||||
// 部分退款后订单仍是已支付状态,退款统计不能只查询整单退款状态的订单;审核中的售后单未完成退款,不计入统计。
|
||||
refundDto.setOrderStatusCodes(HardOrderStatusEnum.getPayPrice());
|
||||
refundDto.setRefundStatusCodes(List.of(HardSaleStatusEnum.COMPLETE.getStatus()));
|
||||
if (ObjectUtil.isNotEmpty(dto.getMchIds())) {
|
||||
refundDto.setMchIds(dto.getMchIds());
|
||||
@@ -514,10 +523,99 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
|
||||
/**
|
||||
* 部门交易记录
|
||||
*
|
||||
* <p>钱包操作与退款金额直接按 wallet_transaction 流水汇总;订单支付先取逐笔明细,
|
||||
* 再按支付流水创建时固化的部门重新归集,现金、第三方支付回退订单部门快照。
|
||||
* 退款金额保留 REFUND 流水原始正负号,审核中的退款尚未生成流水,不会计入任何金额。</p>
|
||||
*
|
||||
* @param dto 当前用户权限范围内的部门、时间和钱包类型筛选条件
|
||||
* @return 部门交易统计;筛选范围内没有部门时返回空列表
|
||||
* @throws IllegalStateException 部门或钱包远程统计调用失败时抛出,避免返回不完整报表
|
||||
*/
|
||||
@Override
|
||||
public List<HardDeptTotalVo> pageListDept(HardDeptTotalDto dto) {
|
||||
// 查询部门
|
||||
List<SysDept> deptList = queryDepartmentList(dto);
|
||||
if (ObjectUtil.isEmpty(deptList)) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<WalletTransactionSumAndCountVo> walletBatchList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, List.of(WalletBizEnum.BATCH.getCode()), WalletTransactionSumAndCountDto.TYPE_INCOME));
|
||||
List<WalletTransactionSumAndCountVo> walletReduceList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, List.of(WalletBizEnum.BATCH.getCode()), WalletTransactionSumAndCountDto.TYPE_EXPENSE));
|
||||
List<WalletTransactionSumAndCountVo> walletReturnCanteenList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, List.of(WalletBizEnum.RETURN_CANTEEN.getCode()), null));
|
||||
List<WalletTransactionSumAndCountVo> walletOnceBatchList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, List.of(WalletBizEnum.UPDATE.getCode(), WalletBizEnum.RECHARGE.getCode()), WalletTransactionSumAndCountDto.TYPE_INCOME));
|
||||
List<WalletTransactionSumAndCountVo> walletOnceReduceList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, List.of(WalletBizEnum.UPDATE.getCode(), WalletBizEnum.RECHARGE.getCode()), WalletTransactionSumAndCountDto.TYPE_EXPENSE));
|
||||
List<WalletTransactionSumAndCountVo> walletRefundList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, List.of(WalletBizEnum.REFUND.getCode()), null));
|
||||
|
||||
List<HardOrderDepartmentStatisticsDetail> payDetails =
|
||||
hardOrderMapper.listPayStatisticsDetails(buildPayStatisticsDto(dto));
|
||||
Map<String, List<HardOrderDepartmentStatisticsDetail>> detailsByBizType = new LinkedHashMap<>();
|
||||
detailsByBizType.put(WalletBizEnum.PAYMENT.getCode(), payDetails);
|
||||
Map<String, Map<String, WalletTransactionDepartmentSnapshotVo>> transactionSnapshotMaps =
|
||||
queryTransactionDepartmentSnapshotMaps(detailsByBizType);
|
||||
List<HardOrderSumAndCountVo> payList = aggregatePaymentsByTransactionDepartment(payDetails,
|
||||
transactionSnapshotMaps.getOrDefault(WalletBizEnum.PAYMENT.getCode(), Map.of()), dto);
|
||||
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletBatchMap = indexWalletStatistics(walletBatchList);
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletReduceMap = indexWalletStatistics(walletReduceList);
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletReturnCanteenMap = indexWalletStatistics(walletReturnCanteenList);
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletOnceBatchMap = indexWalletStatistics(walletOnceBatchList);
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletOnceReduceMap = indexWalletStatistics(walletOnceReduceList);
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletRefundMap = indexWalletStatistics(walletRefundList);
|
||||
Map<Long, HardOrderSumAndCountVo> payMap = indexOrderStatistics(payList);
|
||||
|
||||
List<HardDeptTotalVo> resultList = new ArrayList<>(deptList.size());
|
||||
for (SysDept dept : deptList) {
|
||||
HardDeptTotalVo statistics = new HardDeptTotalVo();
|
||||
statistics.setDeptId(dept.getDeptId());
|
||||
statistics.setDeptName(dept.getName());
|
||||
|
||||
WalletTransactionSumAndCountVo walletBatch = walletBatchMap.get(dept.getDeptId());
|
||||
if (walletBatch != null) {
|
||||
statistics.setWalletPrice(BigDecimal.valueOf(ObjectUtil.defaultIfNull(walletBatch.getSum(), 0L)));
|
||||
statistics.setWalletCount(ObjectUtil.defaultIfNull(walletBatch.getCount(), 0L));
|
||||
}
|
||||
WalletTransactionSumAndCountVo walletReduce = walletReduceMap.get(dept.getDeptId());
|
||||
if (walletReduce != null) {
|
||||
statistics.setWalletReducePrice(Math.abs(ObjectUtil.defaultIfNull(walletReduce.getSum(), 0L)));
|
||||
statistics.setWalletReduceCount(ObjectUtil.defaultIfNull(walletReduce.getCount(), 0L));
|
||||
}
|
||||
WalletTransactionSumAndCountVo walletReturnCanteen = walletReturnCanteenMap.get(dept.getDeptId());
|
||||
if (walletReturnCanteen != null) {
|
||||
statistics.setWalletReturnCanteenPrice(BigDecimal.valueOf(Math.abs(
|
||||
ObjectUtil.defaultIfNull(walletReturnCanteen.getSum(), 0L))));
|
||||
statistics.setWalletReturnCanteenCount(ObjectUtil.defaultIfNull(walletReturnCanteen.getCount(), 0L));
|
||||
}
|
||||
WalletTransactionSumAndCountVo walletOnceBatch = walletOnceBatchMap.get(dept.getDeptId());
|
||||
if (walletOnceBatch != null) {
|
||||
statistics.setWalletOncePrice(BigDecimal.valueOf(ObjectUtil.defaultIfNull(walletOnceBatch.getSum(), 0L)));
|
||||
statistics.setWalletOnceCount(ObjectUtil.defaultIfNull(walletOnceBatch.getCount(), 0L));
|
||||
}
|
||||
WalletTransactionSumAndCountVo walletOnceReduce = walletOnceReduceMap.get(dept.getDeptId());
|
||||
if (walletOnceReduce != null) {
|
||||
statistics.setWalletOnceReducePrice(BigDecimal.valueOf(Math.abs(
|
||||
ObjectUtil.defaultIfNull(walletOnceReduce.getSum(), 0L))));
|
||||
statistics.setWalletOnceReduceCount(ObjectUtil.defaultIfNull(walletOnceReduce.getCount(), 0L));
|
||||
}
|
||||
applyOrderStatistics(statistics, payMap.get(dept.getDeptId()), walletRefundMap.get(dept.getDeptId()));
|
||||
resultList.add(statistics);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询统计结果需要展示的部门,并校验 UPMS 远程调用结果。
|
||||
*
|
||||
* @param dto 部门包含或排除筛选条件
|
||||
* @return 匹配的部门列表,远程响应数据为空时返回空列表
|
||||
* @throws IllegalStateException UPMS 部门远程调用失败时抛出
|
||||
*/
|
||||
private List<SysDept> queryDepartmentList(HardDeptTotalDto dto) {
|
||||
DeptAllListDto deptDto = new DeptAllListDto();
|
||||
deptDto.setIsInclude(dto.getIsInclude());
|
||||
if (ObjectUtil.isNotEmpty(dto.getDeptId())) {
|
||||
@@ -527,193 +625,264 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
deptDto.setDeptId(null);
|
||||
deptDto.setDeptIds(dto.getDeptIds());
|
||||
}
|
||||
List<SysDept> deptList = remoteDeptService.getInnerAllDept(deptDto).getData();
|
||||
if (ObjectUtil.isEmpty(deptList)) return null;
|
||||
// List<Long> deptIds = deptList.stream().map(SysDept::getDeptId).filter(ObjectUtil::isNotEmpty).distinct().toList();
|
||||
List<HardDeptTotalVo> resultList = deptList.stream().map(v -> {
|
||||
HardDeptTotalVo item = new HardDeptTotalVo();
|
||||
item.setDeptId(v.getDeptId());
|
||||
item.setDeptName(v.getName());
|
||||
return item;
|
||||
}).toList();
|
||||
// 查询钱包批量充值流水
|
||||
CompletableFuture<List<WalletTransactionSumAndCountVo>> walletBatchFuture = CompletableFuture.supplyAsync(() -> {
|
||||
WalletTransactionSumAndCountDto walletDto = new WalletTransactionSumAndCountDto();
|
||||
walletDto.setDeptIds(dto.getDeptIds());
|
||||
walletDto.setIsInclude(dto.getIsInclude());
|
||||
walletDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
walletDto.setBizTypes(List.of(WalletBizEnum.BATCH.getCode()));
|
||||
walletDto.setType(WalletTransactionSumAndCountDto.TYPE_INCOME);
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
walletDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
R<List<SysDept>> response = remoteDeptService.getInnerAllDept(deptDto);
|
||||
if (response == null || !response.isOk()) {
|
||||
throw new IllegalStateException("查询部门交易统计的部门信息失败");
|
||||
}
|
||||
return response.getData() == null ? List.of() : response.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建钱包部门统计条件,保证六类钱包统计使用完全一致的权限、时间和钱包类型范围。
|
||||
*
|
||||
* @param dto 部门统计页面筛选条件
|
||||
* @param bizTypes 钱包流水业务类型编码集合
|
||||
* @param type 收支方向;为空时不限制流水正负方向
|
||||
* @return 钱包流水汇总条件
|
||||
*/
|
||||
private WalletTransactionSumAndCountDto buildWalletStatisticsDto(HardDeptTotalDto dto, List<String> bizTypes, Integer type) {
|
||||
WalletTransactionSumAndCountDto walletDto = new WalletTransactionSumAndCountDto();
|
||||
walletDto.setDeptIds(dto.getDeptIds());
|
||||
walletDto.setIsInclude(dto.getIsInclude());
|
||||
walletDto.setBizTypes(bizTypes);
|
||||
walletDto.setType(type);
|
||||
walletDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
walletDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
walletDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
return walletDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用钱包服务查询部门流水统计;远程业务失败时终止本次汇总,避免把失败数据误展示为零。
|
||||
*
|
||||
* @param walletDto 钱包流水汇总条件
|
||||
* @return 钱包部门统计,远程响应数据为空时返回空列表
|
||||
* @throws IllegalStateException 钱包流水远程调用失败时抛出
|
||||
*/
|
||||
private List<WalletTransactionSumAndCountVo> queryWalletStatistics(WalletTransactionSumAndCountDto walletDto) {
|
||||
R<List<WalletTransactionSumAndCountVo>> response = remoteWalletTransactionService.sumAndCount(walletDto);
|
||||
if (response == null || !response.isOk()) {
|
||||
throw new IllegalStateException("查询钱包部门交易统计失败");
|
||||
}
|
||||
return response.getData() == null ? List.of() : response.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建硬件订单支付部门统计条件。订单金额仍保留非钱包支付能力,钱包支付会在后续按流水历史部门重新归属;
|
||||
* 明细查询不做部门过滤,部门包含/排除条件在重归集后统一应用。
|
||||
*
|
||||
* @param dto 部门统计页面筛选条件
|
||||
* @return 支付明细查询条件
|
||||
*/
|
||||
private HardOrderSumAndCountDto buildPayStatisticsDto(HardDeptTotalDto dto) {
|
||||
HardOrderSumAndCountDto orderDto = new HardOrderSumAndCountDto();
|
||||
orderDto.setUserDeptIds(dto.getDeptIds());
|
||||
orderDto.setIsInclude(dto.getIsInclude());
|
||||
orderDto.setOrderStatusCodes(HardOrderStatusEnum.getPayPrice());
|
||||
orderDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
orderDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
orderDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
return orderDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将订单支付明细按钱包流水发生时保存的部门重新归集,并在重新归属后应用页面部门条件。
|
||||
*
|
||||
* <p>现金、第三方支付或历史异常记录没有对应钱包流水时,继续使用订单创建时保存的部门快照,
|
||||
* 避免丢失原有非钱包交易能力。退款不再进入本方法,统一由 Wallet 按 REFUND 流水直接汇总。</p>
|
||||
*
|
||||
* @param details 符合订单状态、商户和时间条件的支付明细
|
||||
* @param transactionSnapshotMap 以订单业务号为键的钱包支付流水快照
|
||||
* @param dto 页面部门包含或排除条件
|
||||
* @return 按最终历史部门归集后的金额和次数
|
||||
*/
|
||||
private List<HardOrderSumAndCountVo> aggregatePaymentsByTransactionDepartment(
|
||||
List<HardOrderDepartmentStatisticsDetail> details,
|
||||
Map<String, WalletTransactionDepartmentSnapshotVo> transactionSnapshotMap,
|
||||
HardDeptTotalDto dto) {
|
||||
if (ObjectUtil.isEmpty(details)) {
|
||||
return List.of();
|
||||
}
|
||||
Set<Long> departmentFilter = ObjectUtil.isEmpty(dto.getDeptIds())
|
||||
? Set.of() : new HashSet<>(dto.getDeptIds());
|
||||
Map<Long, HardOrderSumAndCountVo> aggregateMap = new LinkedHashMap<>();
|
||||
for (HardOrderDepartmentStatisticsDetail detail : details) {
|
||||
if (detail == null) {
|
||||
continue;
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
walletDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
WalletTransactionDepartmentSnapshotVo snapshot = transactionSnapshotMap.get(detail.getBizId());
|
||||
Long departmentId = snapshot == null ? detail.getDeptId() : snapshot.getDeptId();
|
||||
if (departmentId == null || !matchesDepartmentFilter(departmentId, departmentFilter, dto.getIsInclude())) {
|
||||
continue;
|
||||
}
|
||||
return remoteWalletTransactionService.sumAndCount(walletDto).getData();
|
||||
});
|
||||
// 查询钱包批量扣减流水
|
||||
CompletableFuture<List<WalletTransactionSumAndCountVo>> walletReduceFuture = CompletableFuture.supplyAsync(() -> {
|
||||
WalletTransactionSumAndCountDto walletDto = new WalletTransactionSumAndCountDto();
|
||||
walletDto.setDeptIds(dto.getDeptIds());
|
||||
walletDto.setIsInclude(dto.getIsInclude());
|
||||
walletDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
walletDto.setBizTypes(List.of(WalletBizEnum.BATCH.getCode()));
|
||||
walletDto.setType(WalletTransactionSumAndCountDto.TYPE_EXPENSE);
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
walletDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
walletDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
}
|
||||
return remoteWalletTransactionService.sumAndCount(walletDto).getData();
|
||||
});
|
||||
// 查询钱包退还各自食堂
|
||||
CompletableFuture<List<WalletTransactionSumAndCountVo>> walletRetrunCanteenFuture = CompletableFuture.supplyAsync(() -> {
|
||||
WalletTransactionSumAndCountDto walletDto = new WalletTransactionSumAndCountDto();
|
||||
walletDto.setDeptIds(dto.getDeptIds());
|
||||
walletDto.setIsInclude(dto.getIsInclude());
|
||||
walletDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
walletDto.setBizTypes(List.of(WalletBizEnum.RETURN_CANTEEN.getCode()));
|
||||
// walletDto.setType(WalletTransactionSumAndCountDto.TYPE_EXPENSE);
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
walletDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
walletDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
}
|
||||
return remoteWalletTransactionService.sumAndCount(walletDto).getData();
|
||||
});
|
||||
// 查询钱包单次充值流水
|
||||
CompletableFuture<List<WalletTransactionSumAndCountVo>> walletOnceBatchFuture = CompletableFuture.supplyAsync(() -> {
|
||||
WalletTransactionSumAndCountDto walletDto = new WalletTransactionSumAndCountDto();
|
||||
walletDto.setDeptIds(dto.getDeptIds());
|
||||
walletDto.setIsInclude(dto.getIsInclude());
|
||||
walletDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
walletDto.setBizTypes(List.of(WalletBizEnum.UPDATE.getCode(), WalletBizEnum.RECHARGE.getCode()));
|
||||
walletDto.setType(WalletTransactionSumAndCountDto.TYPE_INCOME);
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
walletDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
walletDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
}
|
||||
return remoteWalletTransactionService.sumAndCount(walletDto).getData();
|
||||
});
|
||||
// 查询钱包单次扣减流水
|
||||
CompletableFuture<List<WalletTransactionSumAndCountVo>> walletOnceReduceFuture = CompletableFuture.supplyAsync(() -> {
|
||||
WalletTransactionSumAndCountDto walletDto = new WalletTransactionSumAndCountDto();
|
||||
walletDto.setDeptIds(dto.getDeptIds());
|
||||
walletDto.setIsInclude(dto.getIsInclude());
|
||||
walletDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
walletDto.setBizTypes(List.of(WalletBizEnum.UPDATE.getCode(), WalletBizEnum.RECHARGE.getCode()));
|
||||
walletDto.setType(WalletTransactionSumAndCountDto.TYPE_EXPENSE);
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
walletDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
walletDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
}
|
||||
return remoteWalletTransactionService.sumAndCount(walletDto).getData();
|
||||
});
|
||||
// 查询订单支付流水
|
||||
CompletableFuture<List<HardOrderSumAndCountVo>> payFuture = CompletableFuture.supplyAsync(() -> {
|
||||
HardOrderSumAndCountDto payDto = new HardOrderSumAndCountDto();
|
||||
payDto.setUserDeptIds(dto.getDeptIds());
|
||||
payDto.setIsInclude(dto.getIsInclude());
|
||||
payDto.setOrderStatusCodes(HardOrderStatusEnum.getPayPrice());
|
||||
payDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
payDto.setCreateTimeList(dto.getCreateTimeList());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
payDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
}
|
||||
return hardOrderMapper.sumAndCount(payDto);
|
||||
});
|
||||
// 查询订单退款流水
|
||||
CompletableFuture<List<HardOrderSumAndCountVo>> refundFuture = CompletableFuture.supplyAsync(() -> {
|
||||
HardOrderSumAndCountDto refundDto = new HardOrderSumAndCountDto();
|
||||
refundDto.setUserDeptIds(dto.getDeptIds());
|
||||
refundDto.setIsInclude(dto.getIsInclude());
|
||||
refundDto.setOrderStatusCodes(HardOrderStatusEnum.getPayErrorCodes());
|
||||
refundDto.setRefundStatusCodes(List.of(HardSaleStatusEnum.COMPLETE.getStatus()));
|
||||
refundDto.setWalletTypeIdList(dto.getWalletTypeIdList());
|
||||
if (ObjectUtil.isNotEmpty(dto.getCreateTimeList())) {
|
||||
refundDto.setRefundTimeList(dto.getCreateTimeList());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(dto.getWalletTypeId())) {
|
||||
refundDto.setWalletTypeId(dto.getWalletTypeId());
|
||||
}
|
||||
return hardOrderMapper.refundSumAndCount(refundDto);
|
||||
});
|
||||
CompletableFuture<Void> allFutures = CompletableFuture.allOf(walletBatchFuture, walletReduceFuture, payFuture, refundFuture, walletOnceBatchFuture, walletOnceReduceFuture);
|
||||
allFutures.join();
|
||||
List<WalletTransactionSumAndCountVo> walletBatchList = walletBatchFuture.join();
|
||||
List<WalletTransactionSumAndCountVo> walletReduceList = walletReduceFuture.join();
|
||||
List<WalletTransactionSumAndCountVo> walletRetrunCanteenList = walletRetrunCanteenFuture.join();
|
||||
List<WalletTransactionSumAndCountVo> walletOnceBatchList = walletOnceBatchFuture.join();
|
||||
List<WalletTransactionSumAndCountVo> walletOnceReduceList = walletOnceReduceFuture.join();
|
||||
List<HardOrderSumAndCountVo> payList = payFuture.join();
|
||||
List<HardOrderSumAndCountVo> refundList = refundFuture.join();
|
||||
// 聚合一下数据
|
||||
for (HardDeptTotalVo v : resultList) {
|
||||
if (ObjectUtil.isNotEmpty(walletBatchList)) {
|
||||
walletBatchList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setWalletPrice(BigDecimal.valueOf(i.getSum()));
|
||||
v.setWalletCount(i.getCount());
|
||||
});
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(walletReduceList)) {
|
||||
walletReduceList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setWalletReducePrice(Math.abs(i.getSum()));
|
||||
v.setWalletReduceCount(i.getCount());
|
||||
});
|
||||
}
|
||||
// 退还各自食堂
|
||||
if (ObjectUtil.isNotEmpty(walletRetrunCanteenList)) {
|
||||
walletRetrunCanteenList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setWalletReturnCanteenPrice(BigDecimal.valueOf(Math.abs(i.getSum())));
|
||||
v.setWalletReturnCanteenCount(i.getCount());
|
||||
});
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(walletOnceBatchList)) {
|
||||
walletOnceBatchList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setWalletOncePrice(BigDecimal.valueOf(i.getSum()));
|
||||
v.setWalletOnceCount(i.getCount());
|
||||
});
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(walletOnceReduceList)) {
|
||||
walletOnceReduceList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setWalletOnceReducePrice(BigDecimal.valueOf(Math.abs(i.getSum())));
|
||||
v.setWalletOnceReduceCount(i.getCount());
|
||||
});
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(payList)) {
|
||||
payList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setOrderPayPrice(BigDecimal.valueOf(i.getSum()));
|
||||
v.setOrderPayCount(i.getCount());
|
||||
});
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(refundList)) {
|
||||
refundList.stream().filter(i -> ObjectUtil.equals(i.getDeptId(), v.getDeptId())).findFirst().ifPresent(i -> {
|
||||
v.setOrderRefundPrice(BigDecimal.valueOf(i.getSum()));
|
||||
v.setOrderRefundCont(i.getCount());
|
||||
});
|
||||
HardOrderSumAndCountVo aggregate = aggregateMap.computeIfAbsent(departmentId, key -> {
|
||||
HardOrderSumAndCountVo value = new HardOrderSumAndCountVo();
|
||||
value.setDeptId(key);
|
||||
value.setSum(0L);
|
||||
value.setCount(0L);
|
||||
return value;
|
||||
});
|
||||
long amount = ObjectUtil.defaultIfNull(detail.getSum(), 0L);
|
||||
aggregate.setSum(Math.addExact(aggregate.getSum(), amount));
|
||||
aggregate.setCount(Math.addExact(aggregate.getCount(), ObjectUtil.defaultIfNull(detail.getCount(), 0L)));
|
||||
}
|
||||
return new ArrayList<>(aggregateMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将需要历史归属的交易明细整理成受控查询组,并用一次或极少数几次 Wallet 调用查询流水历史部门。
|
||||
*
|
||||
* <p>单个 SQL 查询组最多一万条,单次远程请求最多十组。常规报表只产生一次远程调用,
|
||||
* 极端大数据量会自动拆分远程请求,避免请求体或 SQL 参数失控。远程失败时终止统计,
|
||||
* 禁止静默回退造成历史部门和订单部门混合口径。</p>
|
||||
*
|
||||
* @param detailsByBizType 以钱包业务类型编码为键的待归属业务明细;部门页当前只传入支付订单
|
||||
* @return 第一层以业务类型编码为键、第二层以业务 ID 为键的钱包流水快照
|
||||
* @throws IllegalStateException 钱包历史部门查询失败时抛出
|
||||
*/
|
||||
private Map<String, Map<String, WalletTransactionDepartmentSnapshotVo>> queryTransactionDepartmentSnapshotMaps(
|
||||
Map<String, List<HardOrderDepartmentStatisticsDetail>> detailsByBizType) {
|
||||
Map<String, Map<String, Long>> expectedWalletMaps = new LinkedHashMap<>();
|
||||
Map<String, Map<String, WalletTransactionDepartmentSnapshotVo>> resultMaps = new LinkedHashMap<>();
|
||||
List<WalletTransactionDepartmentSnapshotDto> queryGroups = new ArrayList<>();
|
||||
for (Map.Entry<String, List<HardOrderDepartmentStatisticsDetail>> entry : detailsByBizType.entrySet()) {
|
||||
List<HardOrderDepartmentStatisticsDetail> sourceDetails =
|
||||
entry.getValue() == null ? List.of() : entry.getValue();
|
||||
List<HardOrderDepartmentStatisticsDetail> walletDetails = sourceDetails.stream()
|
||||
.filter(ObjectUtil::isNotEmpty)
|
||||
.filter(detail -> ObjectUtil.isAllNotEmpty(detail.getBizId(), detail.getWalletId()))
|
||||
.collect(Collectors.toMap(HardOrderDepartmentStatisticsDetail::getBizId,
|
||||
detail -> detail, (first, ignored) -> first, LinkedHashMap::new))
|
||||
.values().stream()
|
||||
.toList();
|
||||
Map<String, Long> expectedWalletMap = walletDetails.stream().collect(Collectors.toMap(
|
||||
HardOrderDepartmentStatisticsDetail::getBizId,
|
||||
HardOrderDepartmentStatisticsDetail::getWalletId,
|
||||
(first, ignored) -> first, LinkedHashMap::new));
|
||||
expectedWalletMaps.put(entry.getKey(), expectedWalletMap);
|
||||
resultMaps.put(entry.getKey(), new LinkedHashMap<>());
|
||||
for (int start = 0; start < walletDetails.size();
|
||||
start += WalletTransactionDepartmentSnapshotDto.MAX_QUERY_BATCH_SIZE) {
|
||||
int end = Math.min(start + WalletTransactionDepartmentSnapshotDto.MAX_QUERY_BATCH_SIZE,
|
||||
walletDetails.size());
|
||||
List<HardOrderDepartmentStatisticsDetail> batchDetails = walletDetails.subList(start, end);
|
||||
WalletTransactionDepartmentSnapshotDto snapshotDto = new WalletTransactionDepartmentSnapshotDto();
|
||||
snapshotDto.setBizType(entry.getKey());
|
||||
snapshotDto.setBizIds(batchDetails.stream()
|
||||
.map(HardOrderDepartmentStatisticsDetail::getBizId).toList());
|
||||
snapshotDto.setWalletIds(batchDetails.stream()
|
||||
.map(HardOrderDepartmentStatisticsDetail::getWalletId).distinct().toList());
|
||||
queryGroups.add(snapshotDto);
|
||||
}
|
||||
}
|
||||
// 计算净流水
|
||||
for (HardDeptTotalVo v : resultList) {
|
||||
// 流水金额
|
||||
if (ObjectUtil.isNotEmpty(v.getOrderPayPrice())) {
|
||||
v.setOrderProfitPrice(v.getOrderPayPrice().subtract(v.getOrderRefundPrice()));
|
||||
for (int start = 0; start < queryGroups.size();
|
||||
start += WalletTransactionDepartmentSnapshotBatchDto.MAX_QUERY_GROUP_SIZE) {
|
||||
int end = Math.min(start + WalletTransactionDepartmentSnapshotBatchDto.MAX_QUERY_GROUP_SIZE,
|
||||
queryGroups.size());
|
||||
WalletTransactionDepartmentSnapshotBatchDto batchDto =
|
||||
new WalletTransactionDepartmentSnapshotBatchDto();
|
||||
batchDto.setQueries(new ArrayList<>(queryGroups.subList(start, end)));
|
||||
R<List<WalletTransactionDepartmentSnapshotVo>> response =
|
||||
remoteWalletTransactionService.listDepartmentSnapshotsBatch(batchDto);
|
||||
if (response == null || !response.isOk()) {
|
||||
throw new IllegalStateException("查询钱包流水历史部门失败");
|
||||
}
|
||||
// 流水次数
|
||||
if (ObjectUtil.isNotEmpty(v.getOrderPayCount())) {
|
||||
v.setOrderProfitCont(v.getOrderPayCount() - v.getOrderRefundCont());
|
||||
if (ObjectUtil.isEmpty(response.getData())) {
|
||||
continue;
|
||||
}
|
||||
for (WalletTransactionDepartmentSnapshotVo snapshot : response.getData()) {
|
||||
if (snapshot != null && ObjectUtil.isAllNotEmpty(
|
||||
snapshot.getBizType(), snapshot.getBizId(), snapshot.getWalletId(), snapshot.getDeptId())) {
|
||||
Map<String, Long> expectedWalletMap = expectedWalletMaps.get(snapshot.getBizType());
|
||||
Map<String, WalletTransactionDepartmentSnapshotVo> resultMap = resultMaps.get(snapshot.getBizType());
|
||||
if (expectedWalletMap == null || resultMap == null
|
||||
|| !ObjectUtil.equals(expectedWalletMap.get(snapshot.getBizId()), snapshot.getWalletId())) {
|
||||
continue;
|
||||
}
|
||||
// 钱包服务按流水主键倒序返回,异常重复业务号保留最新一笔历史快照。
|
||||
resultMap.putIfAbsent(snapshot.getBizId(), snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
return resultMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断重新归属后的历史部门是否命中页面包含或排除条件。
|
||||
*
|
||||
* @param departmentId 流水历史部门或非钱包订单部门
|
||||
* @param departmentFilter 页面部门主键集合
|
||||
* @param isInclude true 表示包含,false 表示排除,空值兼容旧接口的包含语义
|
||||
* @return 当前历史部门是否参与统计
|
||||
*/
|
||||
private boolean matchesDepartmentFilter(Long departmentId, Set<Long> departmentFilter, Boolean isInclude) {
|
||||
if (departmentFilter.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
boolean contains = departmentFilter.contains(departmentId);
|
||||
return Boolean.FALSE.equals(isInclude) ? !contains : contains;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将钱包统计按部门建立索引,避免每个部门重复遍历全部统计结果。
|
||||
*
|
||||
* @param statisticsList 钱包部门统计列表
|
||||
* @return 以部门 ID 为键的统计索引
|
||||
*/
|
||||
private Map<Long, WalletTransactionSumAndCountVo> indexWalletStatistics(List<WalletTransactionSumAndCountVo> statisticsList) {
|
||||
Map<Long, WalletTransactionSumAndCountVo> statisticsMap = new LinkedHashMap<>();
|
||||
if (ObjectUtil.isNotEmpty(statisticsList)) {
|
||||
for (WalletTransactionSumAndCountVo statistics : statisticsList) {
|
||||
if (statistics != null && statistics.getDeptId() != null) {
|
||||
statisticsMap.put(statistics.getDeptId(), statistics);
|
||||
}
|
||||
}
|
||||
}
|
||||
return statisticsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将订单支付统计按部门建立索引,供支付结果与 Wallet 退款结果按相同键合并。
|
||||
*
|
||||
* @param statisticsList 订单部门统计列表
|
||||
* @return 以部门 ID 为键的统计索引
|
||||
*/
|
||||
private Map<Long, HardOrderSumAndCountVo> indexOrderStatistics(List<HardOrderSumAndCountVo> statisticsList) {
|
||||
Map<Long, HardOrderSumAndCountVo> statisticsMap = new LinkedHashMap<>();
|
||||
if (ObjectUtil.isNotEmpty(statisticsList)) {
|
||||
for (HardOrderSumAndCountVo statistics : statisticsList) {
|
||||
if (statistics != null && statistics.getDeptId() != null) {
|
||||
statisticsMap.put(statistics.getDeptId(), statistics);
|
||||
}
|
||||
}
|
||||
}
|
||||
return statisticsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并部门的订单支付与 Wallet 退款流水,并计算净金额和净次数。
|
||||
*
|
||||
* <p>退款金额保留 REFUND 流水原始正负号,退款次数按流水条数统计,因此退款到账、充值退款扣回
|
||||
* 与退款记录页面保持完全相同的流水口径;退款审核中的售后单尚未生成流水,不会计入。</p>
|
||||
*
|
||||
* @param target 部门统计结果
|
||||
* @param pay 支付统计,允许为空
|
||||
* @param refund Wallet REFUND 流水统计,允许为空
|
||||
*/
|
||||
private void applyOrderStatistics(HardDeptTotalVo target, HardOrderSumAndCountVo pay,
|
||||
WalletTransactionSumAndCountVo refund) {
|
||||
long payPrice = pay == null ? 0L : ObjectUtil.defaultIfNull(pay.getSum(), 0L);
|
||||
long payCount = pay == null ? 0L : ObjectUtil.defaultIfNull(pay.getCount(), 0L);
|
||||
long refundPrice = refund == null ? 0L : ObjectUtil.defaultIfNull(refund.getSum(), 0L);
|
||||
long refundCount = refund == null ? 0L : ObjectUtil.defaultIfNull(refund.getCount(), 0L);
|
||||
BigDecimal orderPayPrice = BigDecimal.valueOf(payPrice);
|
||||
BigDecimal orderRefundPrice = BigDecimal.valueOf(refundPrice);
|
||||
target.setOrderPayPrice(orderPayPrice);
|
||||
target.setOrderPayCount(payCount);
|
||||
target.setOrderRefundPrice(orderRefundPrice);
|
||||
target.setOrderRefundCont(refundCount);
|
||||
target.setOrderProfitPrice(orderPayPrice.subtract(orderRefundPrice));
|
||||
target.setOrderProfitCont(payCount - refundCount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+44
@@ -775,6 +775,50 @@
|
||||
group by a.user_dept_id
|
||||
</select>
|
||||
|
||||
<!-- 部门交易统计的逐笔支付明细:钱包支付随后按 wallet_transaction 流水历史部门重新归集,
|
||||
因此这里不做 user_dept_id 过滤,部门包含/排除条件在重归集后应用。 -->
|
||||
<select id="listPayStatisticsDetails"
|
||||
resultType="com.bsd.pjcloud.hard.mapper.order.model.HardOrderDepartmentStatisticsDetail">
|
||||
select
|
||||
cast(a.id as char) as bizId,
|
||||
a.wallet_id as walletId,
|
||||
a.user_dept_id as deptId,
|
||||
a.pay_price as sum,
|
||||
1 as count
|
||||
from hard_order a
|
||||
left join pjcloudx_wallet.wallet_manage w_m ON w_m.id = a.wallet_id
|
||||
<where>
|
||||
a.del_flag = 0
|
||||
<if test="dto.walletTypeId != null">
|
||||
and w_m.wallet_type_id = #{dto.walletTypeId}
|
||||
</if>
|
||||
<if test="dto.walletTypeIdList != null and dto.walletTypeIdList.size() != 0">
|
||||
and w_m.wallet_type_id in
|
||||
<foreach collection="dto.walletTypeIdList" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.isRefundSuccess != null and dto.isRefundSuccess == true ">
|
||||
and (a.refund_status is null or a.refund_status not in ('20'))
|
||||
</if>
|
||||
<if test="dto.orderStatusCodes != null and dto.orderStatusCodes.size() != 0">
|
||||
and a.order_status in
|
||||
<foreach collection="dto.orderStatusCodes" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.createTimeList != null and dto.createTimeList.length == 2">
|
||||
and a.create_time between #{dto.createTimeList[0]} and #{dto.createTimeList[1]}
|
||||
</if>
|
||||
<if test="dto.mchIds != null and dto.mchIds.size() != 0">
|
||||
and a.merchant_id in
|
||||
<foreach collection="dto.mchIds" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="refundSumAndCount" resultType="com.bsd.pjcloud.hard.api.vo.order.HardOrderSumAndCountVo">
|
||||
select o.user_dept_id as deptId,sum(ifnull(a.refund_price, 0)) as sum, count(0) as count
|
||||
from hard_after_sale a
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.bsd.pjcloud.wallet.api.dto;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 钱包流水历史部门快照批量查询条件,用一次跨服务请求承载多个受控 SQL 查询组。
|
||||
*/
|
||||
@Data
|
||||
public class WalletTransactionDepartmentSnapshotBatchDto {
|
||||
|
||||
/**
|
||||
* 单次远程请求允许携带的最大查询组数量,限制请求体和钱包服务单线程数据库占用时长。
|
||||
*/
|
||||
public static final int MAX_QUERY_GROUP_SIZE = 10;
|
||||
|
||||
/**
|
||||
* 按业务类型和 SQL 参数容量拆分后的查询组,组内条件继续沿用单组校验规则。
|
||||
*/
|
||||
@Valid
|
||||
@NotEmpty(message = "钱包流水历史部门查询组不能为空")
|
||||
@Size(max = MAX_QUERY_GROUP_SIZE, message = "单次查询的钱包流水历史部门查询组不能超过10个")
|
||||
private List<WalletTransactionDepartmentSnapshotDto> queries;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.bsd.pjcloud.wallet.api.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 钱包流水历史部门快照查询条件,供业务服务按稳定业务号批量还原流水发生时的部门。
|
||||
*/
|
||||
@Data
|
||||
public class WalletTransactionDepartmentSnapshotDto {
|
||||
|
||||
/**
|
||||
* 单个 SQL 查询组允许携带的最大业务号或钱包数量;两组 IN 参数合计仍低于 MySQL 预编译参数上限。
|
||||
*/
|
||||
public static final int MAX_QUERY_BATCH_SIZE = 10_000;
|
||||
|
||||
/** 钱包流水业务类型持久化编码,例如消费 PAYMENT 或退款 REFUND。 */
|
||||
@NotBlank(message = "钱包流水业务类型不能为空")
|
||||
private String bizType;
|
||||
|
||||
/** 待查询的钱包流水业务号集合;调用方应按单组上限拆分,避免超过数据库参数容量。 */
|
||||
@NotEmpty(message = "钱包流水业务号不能为空")
|
||||
@Size(max = MAX_QUERY_BATCH_SIZE, message = "单组查询的钱包流水业务号不能超过10000个")
|
||||
private List<String> bizIds;
|
||||
|
||||
/** 业务号对应的候选钱包 ID 集合,用于命中钱包流水现有索引并缩小扫描范围。 */
|
||||
@NotEmpty(message = "钱包ID不能为空")
|
||||
@Size(max = MAX_QUERY_BATCH_SIZE, message = "单组查询的钱包ID不能超过10000个")
|
||||
private List<Long> walletIds;
|
||||
}
|
||||
+10
@@ -4,8 +4,10 @@ import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.feign.annotation.NoToken;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletBalanceTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotBatchDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionSumAndCountVo;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -28,4 +30,12 @@ public interface RemoteWalletTransactionService {
|
||||
@PostMapping("/wallet-transaction/sum-and-count")
|
||||
R<List<WalletTransactionSumAndCountVo>> sumAndCount(@RequestBody WalletTransactionSumAndCountDto dto);
|
||||
|
||||
/**
|
||||
* 按业务类型和数据库参数容量拆分后的查询组,批量还原流水创建时固化的历史部门。
|
||||
*/
|
||||
@NoToken
|
||||
@PostMapping("/wallet-transaction/department-snapshots-batch")
|
||||
R<List<WalletTransactionDepartmentSnapshotVo>> listDepartmentSnapshotsBatch(
|
||||
@RequestBody WalletTransactionDepartmentSnapshotBatchDto dto);
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bsd.pjcloud.wallet.api.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 钱包流水历史部门快照,部门取自流水创建时固化的 {@code wallet_transaction.dept_id}。
|
||||
*/
|
||||
@Data
|
||||
public class WalletTransactionDepartmentSnapshotVo {
|
||||
|
||||
/** 钱包流水业务类型持久化编码,用于批量响应区分支付与退款业务号。 */
|
||||
private String bizType;
|
||||
|
||||
/** 钱包流水关联的稳定业务号;消费对应订单 ID,订单退款对应售后单 ID。 */
|
||||
private String bizId;
|
||||
|
||||
/** 产生该笔流水的钱包 ID,用于调用方核对业务号与订单钱包的一致性。 */
|
||||
private Long walletId;
|
||||
|
||||
/** 流水创建时的钱包所属部门 ID;历史异常流水可能为空。 */
|
||||
private Long deptId;
|
||||
|
||||
/** 钱包流水实际发生金额,单位分;退款统计以该值为准,避免混入没有资金流水的售后记录。 */
|
||||
private Long price;
|
||||
}
|
||||
+12
@@ -12,12 +12,14 @@ import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
import com.bsd.pjcloud.common.security.util.SecurityUtils;
|
||||
import com.bsd.pjcloud.wallet.api.constant.WalletBizEnum;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletBalanceTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotBatchDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionRemarkDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionUpdateInfoDto;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletCzTotalVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionSumAndCountVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTypeTotalVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.transaction.WalletTransactionVo;
|
||||
@@ -73,6 +75,16 @@ public class WalletTransactionController {
|
||||
public R<List<WalletTransactionSumAndCountVo>> sumAndCount(@RequestBody WalletTransactionSumAndCountDto dto) {
|
||||
return R.ok(walletTransactionMapper.sumAndCount(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务类型和数据库参数容量拆分后的查询组,批量还原流水创建时固化的历史部门。
|
||||
*/
|
||||
@Inner
|
||||
@PostMapping("/department-snapshots-batch")
|
||||
public R<List<WalletTransactionDepartmentSnapshotVo>> listDepartmentSnapshotsBatch(
|
||||
@Valid @RequestBody WalletTransactionDepartmentSnapshotBatchDto dto) {
|
||||
return R.ok(walletTransactionService.listDepartmentSnapshotsBatch(dto));
|
||||
}
|
||||
/* ************************************* end 远程调用 end ************************************* */
|
||||
|
||||
/* ************************************* start app start ************************************* */
|
||||
|
||||
+11
@@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.data.datascope.DataScope;
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletExportCzTotalPerson;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionSumAndCountVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTypeCountVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeApplySelectCompareVo;
|
||||
@@ -83,5 +85,14 @@ public interface WalletTransactionMapper extends PjcloudBaseMapper<WalletTransac
|
||||
|
||||
List<WalletTransactionSumAndCountVo> sumAndCount(@Param("dto") WalletTransactionSumAndCountDto dto);
|
||||
|
||||
/**
|
||||
* 查询指定业务流水创建时固化的历史部门,结果按流水主键倒序以便调用方兼容异常重复数据。
|
||||
*
|
||||
* @param dto 单一业务类型及分批业务号集合
|
||||
* @return 对应流水的业务号和历史部门;未生成钱包流水的业务号不会返回
|
||||
*/
|
||||
List<WalletTransactionDepartmentSnapshotVo> listDepartmentSnapshots(
|
||||
@Param("dto") WalletTransactionDepartmentSnapshotDto dto);
|
||||
|
||||
List<WalletExportCzTotalPerson> exportCzTotalPerson();
|
||||
}
|
||||
+10
@@ -4,11 +4,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bsd.pjcloud.common.data.datascope.DataScope;
|
||||
import com.bsd.pjcloud.wallet.api.constant.WalletBizEnum;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotBatchDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionRemarkDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionUpdateInfoDto;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletCzTotalVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTypeTotalVo;
|
||||
import com.bsd.pjcloud.wallet.api.vo.transaction.WalletTransactionVo;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -79,4 +81,12 @@ public interface WalletTransactionService extends IService<WalletTransactionEnti
|
||||
* 后台充值统计按照个人(导出)
|
||||
*/
|
||||
void exportCzTotalPerson(HttpServletResponse response, WalletTransactionDto dto, Object o);
|
||||
|
||||
/**
|
||||
* 在一次钱包服务调用中依次执行受控历史部门查询组,并合并支付、退款快照结果。
|
||||
*
|
||||
* @param dto 按业务类型和数据库参数容量拆分后的查询组
|
||||
* @return 所有查询组命中的流水历史部门快照
|
||||
*/
|
||||
List<WalletTransactionDepartmentSnapshotVo> listDepartmentSnapshotsBatch(WalletTransactionDepartmentSnapshotBatchDto dto);
|
||||
}
|
||||
+24
@@ -31,6 +31,8 @@ import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
|
||||
import com.bsd.pjcloud.wallet.api.constant.mch.WalletMchSupType;
|
||||
import com.bsd.pjcloud.wallet.api.dto.MchManageDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletManageDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotBatchDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionRemarkDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionUpdateInfoDto;
|
||||
@@ -107,6 +109,28 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
|
||||
return baseMapper.insert(entity) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一次钱包服务调用中依次执行受控历史部门查询组,并合并支付、退款快照结果。
|
||||
*
|
||||
* <p>分组边界留在钱包服务内部,Hardware 不再为每一万条明细承担一次串行网络往返;
|
||||
* 每组仍使用现有 wallet_id 索引缩小扫描范围,且不会生成超过数据库参数容量的 SQL。</p>
|
||||
*
|
||||
* @param dto 按业务类型和数据库参数容量拆分后的查询组
|
||||
* @return 所有查询组命中的流水历史部门快照
|
||||
*/
|
||||
@Override
|
||||
public List<WalletTransactionDepartmentSnapshotVo> listDepartmentSnapshotsBatch(
|
||||
WalletTransactionDepartmentSnapshotBatchDto dto) {
|
||||
List<WalletTransactionDepartmentSnapshotVo> result = new ArrayList<>();
|
||||
for (WalletTransactionDepartmentSnapshotDto query : dto.getQueries()) {
|
||||
List<WalletTransactionDepartmentSnapshotVo> snapshots = baseMapper.listDepartmentSnapshots(query);
|
||||
if (ObjectUtil.isNotEmpty(snapshots)) {
|
||||
result.addAll(snapshots);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
// @CacheEvict(value = CacheConstants.WALLET_DETAIL, key = "#walletId")
|
||||
|
||||
+26
-4
@@ -164,7 +164,7 @@
|
||||
from wallet_transaction a
|
||||
left join wallet_manage m on m.id = a.wallet_id
|
||||
<where>
|
||||
a.del_flag = 0 and m.dept_id is not null
|
||||
a.del_flag = 0 and a.dept_id is not null
|
||||
<if test="dto.walletId != null">
|
||||
a.wallet_id = ${dto.walletId}
|
||||
</if>
|
||||
@@ -194,13 +194,13 @@
|
||||
<if test="dto.deptIds != null and dto.deptIds.size() != 0">
|
||||
<choose>
|
||||
<when test="dto.isInclude != null and dto.isInclude == true">
|
||||
and m.dept_id IN
|
||||
and a.dept_id IN
|
||||
</when>
|
||||
<when test="dto.isInclude != null and dto.isInclude == false">
|
||||
and m.dept_id NOT IN
|
||||
and a.dept_id NOT IN
|
||||
</when>
|
||||
<otherwise>
|
||||
and m.dept_id IN
|
||||
and a.dept_id IN
|
||||
</otherwise>
|
||||
</choose>
|
||||
<foreach collection="dto.deptIds" item="v" open="(" close=")" separator=",">
|
||||
@@ -214,6 +214,28 @@
|
||||
group by a.dept_id
|
||||
</select>
|
||||
|
||||
<select id="listDepartmentSnapshots"
|
||||
resultType="com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo">
|
||||
select a.biz_type as bizType,
|
||||
a.biz_id as bizId,
|
||||
a.wallet_id as walletId,
|
||||
a.dept_id as deptId,
|
||||
a.price as price
|
||||
from wallet_transaction a
|
||||
where a.del_flag = '0'
|
||||
and a.dept_id is not null
|
||||
and a.biz_type = #{dto.bizType}
|
||||
and a.wallet_id in
|
||||
<foreach collection="dto.walletIds" item="walletId" open="(" close=")" separator=",">
|
||||
#{walletId}
|
||||
</foreach>
|
||||
and a.biz_id in
|
||||
<foreach collection="dto.bizIds" item="bizId" open="(" close=")" separator=",">
|
||||
#{bizId}
|
||||
</foreach>
|
||||
order by a.id desc
|
||||
</select>
|
||||
|
||||
<select id="sumAndCountUser" resultType="com.bsd.pjcloud.wallet.api.vo.WalletTransactionSumAndCountVo">
|
||||
select a.dept_id,
|
||||
sum(a.price) as sum,
|
||||
|
||||
Reference in new issue
Block a user