fix:修改部门统计的流水金额和退款金额不准确的问题
This commit is contained in:
1 parent
779bad9314
commit
0c19eff578
11 files changed
+449
-43
No files matched your search
+15
-2
@@ -16,6 +16,7 @@ import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardParentCarEntity;
|
||||
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.bsd.pjcloud.hard.mapper.order.model.HardOrderStatisticsAggregate;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
|
||||
@@ -212,9 +213,21 @@ public interface HardOrderMapper extends PjcloudBaseMapper<HardOrderEntity> {
|
||||
|
||||
List<HardOrderPayTypeVo> listOrerRefundPayType(@Param("dto") HardOrderDto refundDto);
|
||||
|
||||
List<HardOrderSumAndCountVo> sumAndCount(@Param("dto") HardOrderSumAndCountDto dto);
|
||||
/**
|
||||
* 查询符合订单状态、商户和创建时间条件的逐笔支付统计明细,不提前按订单部门过滤或汇总。
|
||||
*
|
||||
* @param dto 支付订单状态、商户和创建时间条件
|
||||
* @return 每笔订单的业务号、钱包、订单部门、支付金额和固定一次计数
|
||||
*/
|
||||
List<HardOrderDepartmentStatisticsDetail> listPayStatisticsDetails(@Param("dto") HardOrderSumAndCountDto dto);
|
||||
|
||||
List<HardOrderSumAndCountVo> refundSumAndCount(@Param("dto") HardOrderSumAndCountDto refundDto);
|
||||
/**
|
||||
* 查询符合售后状态、商户和完成时间条件的逐笔退款统计明细,不提前按原订单部门过滤或汇总。
|
||||
*
|
||||
* @param refundDto 退款对应的订单状态、售后状态、商户和完成时间条件
|
||||
* @return 每笔售后的业务号、原订单钱包、订单部门、退款金额和固定一次计数
|
||||
*/
|
||||
List<HardOrderDepartmentStatisticsDetail> listRefundStatisticsDetails(@Param("dto") HardOrderSumAndCountDto refundDto);
|
||||
|
||||
List<HardOrderSumAndCountVo> sumAndCountTotalByHourType(@Param("dto") HardOrderSumAndCountDto dto);
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
+116
-2
@@ -28,14 +28,17 @@ 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.wallet.api.constant.WalletBizEnum;
|
||||
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 lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -49,10 +52,13 @@ import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Validated
|
||||
@Slf4j
|
||||
@@ -426,8 +432,10 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
List<WalletTransactionSumAndCountVo> walletOnceReduceList = queryWalletStatistics(
|
||||
buildWalletStatisticsDto(dto, WalletBizEnum.UPDATE.getCode(), WalletTransactionSumAndCountDto.TYPE_EXPENSE));
|
||||
|
||||
List<HardOrderSumAndCountVo> payList = hardOrderMapper.sumAndCount(buildOrderStatisticsDto(dto, false));
|
||||
List<HardOrderSumAndCountVo> refundList = hardOrderMapper.refundSumAndCount(buildOrderStatisticsDto(dto, true));
|
||||
List<HardOrderSumAndCountVo> payList = aggregateByTransactionDepartment(
|
||||
hardOrderMapper.listPayStatisticsDetails(buildOrderStatisticsDto(dto, false)), WalletBizEnum.PAYMENT, dto);
|
||||
List<HardOrderSumAndCountVo> refundList = aggregateByTransactionDepartment(
|
||||
hardOrderMapper.listRefundStatisticsDetails(buildOrderStatisticsDto(dto, true)), WalletBizEnum.REFUND, dto);
|
||||
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletBatchMap = indexWalletStatistics(walletBatchList);
|
||||
Map<Long, WalletTransactionSumAndCountVo> walletReduceMap = indexWalletStatistics(walletReduceList);
|
||||
@@ -558,6 +566,112 @@ public class HardOrderTotalServiceImpl implements HardOrderTotalService {
|
||||
return orderDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将订单或售后明细按钱包流水发生时保存的部门重新归集,并在重新归属后应用页面部门条件。
|
||||
*
|
||||
* <p>现金、第三方支付或历史异常记录没有对应钱包流水时,继续使用订单创建时保存的部门快照,
|
||||
* 避免为了统一钱包口径而丢失原有非钱包交易能力。</p>
|
||||
*
|
||||
* @param details 符合订单状态、商户和时间条件的支付或售后明细
|
||||
* @param bizType 支付或退款的钱包流水业务类型
|
||||
* @param dto 页面部门包含或排除条件
|
||||
* @return 按最终历史部门归集后的金额和次数
|
||||
*/
|
||||
private List<HardOrderSumAndCountVo> aggregateByTransactionDepartment(
|
||||
List<HardOrderDepartmentStatisticsDetail> details, WalletBizEnum bizType, HardDeptTotalDto dto) {
|
||||
if (ObjectUtil.isEmpty(details)) {
|
||||
return List.of();
|
||||
}
|
||||
Map<String, Long> transactionDepartmentMap = queryTransactionDepartmentMap(details, bizType);
|
||||
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;
|
||||
}
|
||||
Long departmentId = transactionDepartmentMap.getOrDefault(detail.getBizId(), detail.getDeptId());
|
||||
if (departmentId == null || !matchesDepartmentFilter(departmentId, departmentFilter, dto.getIsInclude())) {
|
||||
continue;
|
||||
}
|
||||
HardOrderSumAndCountVo aggregate = aggregateMap.computeIfAbsent(departmentId, key -> {
|
||||
HardOrderSumAndCountVo value = new HardOrderSumAndCountVo();
|
||||
value.setDeptId(key);
|
||||
value.setSum(0L);
|
||||
value.setCount(0L);
|
||||
return value;
|
||||
});
|
||||
aggregate.setSum(Math.addExact(aggregate.getSum(), ObjectUtil.defaultIfNull(detail.getSum(), 0L)));
|
||||
aggregate.setCount(Math.addExact(aggregate.getCount(), ObjectUtil.defaultIfNull(detail.getCount(), 0L)));
|
||||
}
|
||||
return new ArrayList<>(aggregateMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分批查询订单或售后业务号对应的钱包流水历史部门;远程失败时终止统计,禁止静默回退造成混合口径。
|
||||
*
|
||||
* @param details 支付订单或退款售后明细
|
||||
* @param bizType 钱包支付或退款业务类型
|
||||
* @return 以订单 ID 或售后单 ID 字符串为键的流水历史部门
|
||||
* @throws IllegalStateException 钱包历史部门查询失败时抛出
|
||||
*/
|
||||
private Map<String, Long> queryTransactionDepartmentMap(
|
||||
List<HardOrderDepartmentStatisticsDetail> details, WalletBizEnum bizType) {
|
||||
List<HardOrderDepartmentStatisticsDetail> walletDetails = details.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));
|
||||
Map<String, Long> resultMap = 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(bizType.getCode());
|
||||
snapshotDto.setBizIds(batchDetails.stream().map(HardOrderDepartmentStatisticsDetail::getBizId).toList());
|
||||
snapshotDto.setWalletIds(batchDetails.stream().map(HardOrderDepartmentStatisticsDetail::getWalletId)
|
||||
.distinct().toList());
|
||||
R<List<WalletTransactionDepartmentSnapshotVo>> response =
|
||||
remoteWalletTransactionService.listDepartmentSnapshots(snapshotDto);
|
||||
if (response == null || !response.isOk()) {
|
||||
throw new IllegalStateException("查询钱包流水历史部门失败");
|
||||
}
|
||||
if (ObjectUtil.isEmpty(response.getData())) {
|
||||
continue;
|
||||
}
|
||||
for (WalletTransactionDepartmentSnapshotVo snapshot : response.getData()) {
|
||||
if (snapshot != null && ObjectUtil.isAllNotEmpty(
|
||||
snapshot.getBizId(), snapshot.getWalletId(), snapshot.getDeptId())
|
||||
&& ObjectUtil.equals(expectedWalletMap.get(snapshot.getBizId()), snapshot.getWalletId())) {
|
||||
// 钱包服务按流水主键倒序返回,异常重复业务号保留最新一笔历史快照。
|
||||
resultMap.putIfAbsent(snapshot.getBizId(), snapshot.getDeptId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断重新归属后的历史部门是否命中页面包含或排除条件。
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将钱包统计按部门建立索引,避免每个部门重复遍历全部统计结果。
|
||||
*
|
||||
|
||||
+13
-39
@@ -813,11 +813,14 @@
|
||||
group by a.pay_type
|
||||
</select>
|
||||
|
||||
<select id="sumAndCount" resultType="com.bsd.pjcloud.hard.api.vo.order.HardOrderSumAndCountVo">
|
||||
<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,
|
||||
sum(a.pay_price) as sum,
|
||||
count(0) as count
|
||||
a.pay_price as sum,
|
||||
1 as count
|
||||
from hard_order a
|
||||
<where>
|
||||
a.del_flag = 0
|
||||
@@ -830,22 +833,6 @@
|
||||
#{v}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.getUserDeptIds != null and dto.getUserDeptIds.size() != 0">
|
||||
<choose>
|
||||
<when test="dto.isInclude != null and dto.isInclude == true">
|
||||
and a.user_dept_id IN
|
||||
</when>
|
||||
<when test="dto.isInclude != null and dto.isInclude == false">
|
||||
and a.user_dept_id NOT IN
|
||||
</when>
|
||||
<otherwise>
|
||||
and a.user_dept_id IN
|
||||
</otherwise>
|
||||
</choose>
|
||||
<foreach collection="dto.getUserDeptIds" 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>
|
||||
@@ -856,11 +843,15 @@
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
group by a.user_dept_id
|
||||
</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
|
||||
<select id="listRefundStatisticsDetails"
|
||||
resultType="com.bsd.pjcloud.hard.mapper.order.model.HardOrderDepartmentStatisticsDetail">
|
||||
select cast(a.id as char) as bizId,
|
||||
o.wallet_id as walletId,
|
||||
o.user_dept_id as deptId,
|
||||
ifnull(a.refund_price, 0) as sum,
|
||||
1 as count
|
||||
from hard_after_sale a
|
||||
inner join hard_order o on o.id = a.order_id and o.del_flag = '0'
|
||||
<where>
|
||||
@@ -871,22 +862,6 @@
|
||||
#{v}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.getUserDeptIds != null and dto.getUserDeptIds.size() != 0">
|
||||
<choose>
|
||||
<when test="dto.isInclude != null and dto.isInclude == true">
|
||||
and o.user_dept_id IN
|
||||
</when>
|
||||
<when test="dto.isInclude != null and dto.isInclude == false">
|
||||
and o.user_dept_id NOT IN
|
||||
</when>
|
||||
<otherwise>
|
||||
and o.user_dept_id IN
|
||||
</otherwise>
|
||||
</choose>
|
||||
<foreach collection="dto.getUserDeptIds" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.createTimeList != null and dto.createTimeList.length == 2">
|
||||
and o.create_time between #{dto.createTimeList[0]} and #{dto.createTimeList[1]}
|
||||
</if>
|
||||
@@ -906,7 +881,6 @@
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
group by o.user_dept_id
|
||||
</select>
|
||||
|
||||
<select id="sumAndCountTotalByHourType" resultType="com.bsd.pjcloud.hard.api.vo.order.HardOrderSumAndCountVo">
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
package com.bsd.pjcloud.hard.service.impl.order;
|
||||
|
||||
import com.bsd.pjcloud.admin.api.entity.SysDept;
|
||||
import com.bsd.pjcloud.admin.api.feign.RemoteDeptService;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardDeptTotalDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardOrderSumAndCountDto;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.HardDeptTotalVo;
|
||||
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.wallet.api.constant.WalletBizEnum;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotDto;
|
||||
import com.bsd.pjcloud.wallet.api.feign.RemoteMchService;
|
||||
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletTransactionService;
|
||||
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 部门交易汇总历史部门口径测试,覆盖人员在测试部门和正常部门之间双向迁移的归属变化。
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HardOrderTotalServiceImplTest {
|
||||
|
||||
/** 硬件订单查询依赖,提供满足状态、商户和时间条件的支付及退款明细。 */
|
||||
@Mock
|
||||
private HardOrderMapper hardOrderMapper;
|
||||
|
||||
/** 设备管理 Mapper,本组测试不触发其余设备统计方法。 */
|
||||
@Mock
|
||||
private HardManagerMapper hardManagerMapper;
|
||||
|
||||
/** 设备管理服务,本组测试只为构造完整被测服务。 */
|
||||
@Mock
|
||||
private HardManagerService hardManagerService;
|
||||
|
||||
/** 商户远程服务,本组测试不设置商户筛选。 */
|
||||
@Mock
|
||||
private RemoteMchService remoteMchService;
|
||||
|
||||
/** 钱包流水远程服务,分别提供普通钱包汇总和交易历史部门快照。 */
|
||||
@Mock
|
||||
private RemoteWalletTransactionService remoteWalletTransactionService;
|
||||
|
||||
/** 部门远程服务,提供报表最终展示的正常部门。 */
|
||||
@Mock
|
||||
private RemoteDeptService remoteDeptService;
|
||||
|
||||
/** 被测部门交易汇总服务。 */
|
||||
private HardOrderTotalServiceImpl service;
|
||||
|
||||
/**
|
||||
* 构造真实汇总服务,并让与本用例无关的五类钱包充值统计返回空集合。
|
||||
*/
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new HardOrderTotalServiceImpl(hardOrderMapper, hardManagerMapper, hardManagerService,
|
||||
remoteMchService, remoteWalletTransactionService, remoteDeptService);
|
||||
when(remoteWalletTransactionService.sumAndCount(any())).thenReturn(R.ok(List.of()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证排除测试部门时以钱包流水部门为准,并保留没有钱包流水的非钱包交易订单部门快照。
|
||||
*/
|
||||
@Test
|
||||
void pageListDeptFiltersAfterApplyingTransactionDepartmentSnapshot() {
|
||||
when(remoteDeptService.getInnerAllDept(any())).thenReturn(R.ok(List.of(department(1L, "正常部门"))));
|
||||
when(hardOrderMapper.listPayStatisticsDetails(any(HardOrderSumAndCountDto.class))).thenReturn(List.of(
|
||||
detail("101", 11L, 9L, 100L),
|
||||
detail("102", 12L, 1L, 200L),
|
||||
detail("103", null, 1L, 50L)));
|
||||
when(hardOrderMapper.listRefundStatisticsDetails(any(HardOrderSumAndCountDto.class))).thenReturn(List.of(
|
||||
detail("201", 21L, 9L, 30L),
|
||||
detail("202", 22L, 1L, 40L),
|
||||
detail("203", null, 1L, 10L)));
|
||||
when(remoteWalletTransactionService.listDepartmentSnapshots(any())).thenAnswer(invocation -> {
|
||||
WalletTransactionDepartmentSnapshotDto query = invocation.getArgument(0);
|
||||
if (WalletBizEnum.PAYMENT.getCode().equals(query.getBizType())) {
|
||||
return R.ok(List.of(snapshot("101", 11L, 1L), snapshot("102", 12L, 9L)));
|
||||
}
|
||||
return R.ok(List.of(snapshot("201", 21L, 1L), snapshot("202", 22L, 9L)));
|
||||
});
|
||||
|
||||
HardDeptTotalDto query = new HardDeptTotalDto();
|
||||
query.setDeptIds(List.of(9L));
|
||||
query.setIsInclude(false);
|
||||
|
||||
List<HardDeptTotalVo> result = service.pageListDept(query);
|
||||
|
||||
assertEquals(1, result.size());
|
||||
HardDeptTotalVo normalDepartment = result.get(0);
|
||||
assertEquals(BigDecimal.valueOf(150L), normalDepartment.getOrderPayPrice());
|
||||
assertEquals(2L, normalDepartment.getOrderPayCount());
|
||||
assertEquals(BigDecimal.valueOf(40L), normalDepartment.getOrderRefundPrice());
|
||||
assertEquals(2L, normalDepartment.getOrderRefundCont());
|
||||
assertEquals(BigDecimal.valueOf(110L), normalDepartment.getOrderProfitPrice());
|
||||
assertEquals(0L, normalDepartment.getOrderProfitCont());
|
||||
|
||||
ArgumentCaptor<WalletTransactionDepartmentSnapshotDto> captor =
|
||||
ArgumentCaptor.forClass(WalletTransactionDepartmentSnapshotDto.class);
|
||||
verify(remoteWalletTransactionService, org.mockito.Mockito.times(2)).listDepartmentSnapshots(captor.capture());
|
||||
assertEquals(List.of("101", "102"), captor.getAllValues().get(0).getBizIds());
|
||||
assertEquals(List.of(11L, 12L), captor.getAllValues().get(0).getWalletIds());
|
||||
assertEquals(List.of("201", "202"), captor.getAllValues().get(1).getBizIds());
|
||||
assertEquals(List.of(21L, 22L), captor.getAllValues().get(1).getWalletIds());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一条订单或售后金额明细,部门表示原订单创建时的回退快照。
|
||||
*
|
||||
* @param bizId 订单 ID 或售后单 ID 字符串
|
||||
* @param walletId 原订单绑定的钱包 ID;非钱包交易为空
|
||||
* @param departmentId 原订单部门 ID
|
||||
* @param amount 支付或退款金额,单位分
|
||||
* @return 单笔待归属统计明细
|
||||
*/
|
||||
private HardOrderDepartmentStatisticsDetail detail(String bizId, Long walletId, Long departmentId, Long amount) {
|
||||
HardOrderDepartmentStatisticsDetail detail = new HardOrderDepartmentStatisticsDetail();
|
||||
detail.setBizId(bizId);
|
||||
detail.setWalletId(walletId);
|
||||
detail.setDeptId(departmentId);
|
||||
detail.setSum(amount);
|
||||
detail.setCount(1L);
|
||||
return detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建钱包流水发生时固化的部门快照。
|
||||
*
|
||||
* @param bizId 订单 ID 或售后单 ID 字符串
|
||||
* @param walletId 产生流水的钱包 ID
|
||||
* @param departmentId 流水创建时部门 ID
|
||||
* @return 钱包流水历史部门快照
|
||||
*/
|
||||
private WalletTransactionDepartmentSnapshotVo snapshot(String bizId, Long walletId, Long departmentId) {
|
||||
WalletTransactionDepartmentSnapshotVo snapshot = new WalletTransactionDepartmentSnapshotVo();
|
||||
snapshot.setBizId(bizId);
|
||||
snapshot.setWalletId(walletId);
|
||||
snapshot.setDeptId(departmentId);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建报表需要展示的部门基础资料。
|
||||
*
|
||||
* @param departmentId 部门 ID
|
||||
* @param name 部门名称
|
||||
* @return 部门实体
|
||||
*/
|
||||
private SysDept department(Long departmentId, String name) {
|
||||
SysDept department = new SysDept();
|
||||
department.setDeptId(departmentId);
|
||||
department.setName(name);
|
||||
return department;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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 {
|
||||
|
||||
/** 单次内部查询允许携带的最大业务号或钱包数量。 */
|
||||
public static final int MAX_QUERY_BATCH_SIZE = 500;
|
||||
|
||||
/** 钱包流水业务类型持久化编码,例如消费 PAYMENT 或退款 REFUND。 */
|
||||
@NotBlank(message = "钱包流水业务类型不能为空")
|
||||
private String bizType;
|
||||
|
||||
/** 待查询的钱包流水业务号集合;调用方应分批传入,避免生成超长 SQL。 */
|
||||
@NotEmpty(message = "钱包流水业务号不能为空")
|
||||
@Size(max = MAX_QUERY_BATCH_SIZE, message = "单次查询的钱包流水业务号不能超过500个")
|
||||
private List<String> bizIds;
|
||||
|
||||
/** 业务号对应的候选钱包 ID 集合,用于命中钱包流水现有索引并缩小扫描范围。 */
|
||||
@NotEmpty(message = "钱包ID不能为空")
|
||||
@Size(max = MAX_QUERY_BATCH_SIZE, message = "单次查询的钱包ID不能超过500个")
|
||||
private List<Long> walletIds;
|
||||
}
|
||||
+13
@@ -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.WalletTransactionDepartmentSnapshotDto;
|
||||
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;
|
||||
@@ -63,4 +65,15 @@ public interface RemoteWalletTransactionService {
|
||||
@PostMapping("/wallet-transaction/sum-and-count")
|
||||
R<List<WalletTransactionSumAndCountVo>> sumAndCount(@RequestBody WalletTransactionSumAndCountDto dto);
|
||||
|
||||
/**
|
||||
* 按业务类型和业务号批量查询流水创建时固化的部门快照。
|
||||
*
|
||||
* @param dto 钱包流水业务类型及稳定业务号集合
|
||||
* @return 按流水主键倒序返回的历史部门快照;同一业务号异常重复时调用方取第一条
|
||||
*/
|
||||
@NoToken
|
||||
@PostMapping("/wallet-transaction/department-snapshots")
|
||||
R<List<WalletTransactionDepartmentSnapshotVo>> listDepartmentSnapshots(
|
||||
@RequestBody WalletTransactionDepartmentSnapshotDto dto);
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.bsd.pjcloud.wallet.api.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 钱包流水历史部门快照,部门取自流水创建时固化的 {@code wallet_transaction.dept_id}。
|
||||
*/
|
||||
@Data
|
||||
public class WalletTransactionDepartmentSnapshotVo {
|
||||
|
||||
/** 钱包流水关联的稳定业务号;消费对应订单 ID,订单退款对应售后单 ID。 */
|
||||
private String bizId;
|
||||
|
||||
/** 产生该笔流水的钱包 ID,用于调用方核对业务号与订单钱包的一致性。 */
|
||||
private Long walletId;
|
||||
|
||||
/** 流水创建时的钱包所属部门 ID;历史异常流水可能为空。 */
|
||||
private Long deptId;
|
||||
}
|
||||
+15
@@ -15,10 +15,12 @@ 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.WalletTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionRemarkDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
|
||||
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;
|
||||
@@ -131,6 +133,19 @@ public class WalletTransactionController {
|
||||
public R<List<WalletTransactionSumAndCountVo>> sumAndCount(@RequestBody WalletTransactionSumAndCountDto dto) {
|
||||
return R.ok(walletTransactionMapper.sumAndCount(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按稳定业务号查询钱包流水创建时的部门快照,供订单统计还原流水历史归属。
|
||||
*
|
||||
* @param dto 单一业务类型及分批业务号集合
|
||||
* @return 命中钱包流水的历史部门快照,按流水主键倒序排列
|
||||
*/
|
||||
@Inner
|
||||
@PostMapping("/department-snapshots")
|
||||
public R<List<WalletTransactionDepartmentSnapshotVo>> listDepartmentSnapshots(
|
||||
@Valid @RequestBody WalletTransactionDepartmentSnapshotDto dto) {
|
||||
return R.ok(walletTransactionMapper.listDepartmentSnapshots(dto));
|
||||
}
|
||||
/* ************************************* end 远程调用 end ************************************* */
|
||||
|
||||
/* ************************************* start app start ************************************* */
|
||||
|
||||
+11
@@ -8,11 +8,13 @@ import com.bsd.pjcloud.common.core.util.BeanUtils;
|
||||
import com.bsd.pjcloud.common.data.datascope.DataScope;
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDepartmentSnapshotDto;
|
||||
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
|
||||
import com.bsd.pjcloud.wallet.api.entity.WalletTypeEntity;
|
||||
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.WalletTypeTotalVo;
|
||||
@@ -140,6 +142,15 @@ public interface WalletTransactionMapper extends PjcloudBaseMapper<WalletTransac
|
||||
|
||||
List<WalletTransactionSumAndCountVo> sumAndCount(@Param("dto") WalletTransactionSumAndCountDto dto);
|
||||
|
||||
/**
|
||||
* 查询指定业务流水创建时固化的历史部门,结果按流水主键倒序以便调用方兼容异常重复数据。
|
||||
*
|
||||
* @param dto 单一业务类型及分批业务号集合
|
||||
* @return 对应流水的业务号和历史部门;未生成钱包流水的业务号不会返回
|
||||
*/
|
||||
List<WalletTransactionDepartmentSnapshotVo> listDepartmentSnapshots(
|
||||
@Param("dto") WalletTransactionDepartmentSnapshotDto dto);
|
||||
|
||||
/**
|
||||
* 查询固定金额范围内的个人充值统计明细,并按财务标签筛选。
|
||||
*
|
||||
|
||||
+20
@@ -214,6 +214,26 @@
|
||||
group by a.dept_id
|
||||
</select>
|
||||
|
||||
<select id="listDepartmentSnapshots"
|
||||
resultType="com.bsd.pjcloud.wallet.api.vo.WalletTransactionDepartmentSnapshotVo">
|
||||
select a.biz_id as bizId,
|
||||
a.wallet_id as walletId,
|
||||
a.dept_id as deptId
|
||||
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