fix: exclude cancelled orders from performance stats
Some checks failed
Build and Push Backend / docker (push) Failing after 4s

This commit is contained in:
irving
2025-11-07 00:29:58 -05:00
parent cc59f859af
commit d7d7c64c01
4 changed files with 162 additions and 7 deletions

View File

@@ -241,7 +241,6 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
public List<PlayOrderInfoEntity> listByEndTime(String clerkId, LocalDateTime endTime) {
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
lambdaQueryWrapper.lt(PlayOrderInfoEntity::getOrderEndTime, endTime);
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderSettlementState, "0");
return this.baseMapper.selectList(lambdaQueryWrapper);

View File

@@ -5,6 +5,7 @@ import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
@@ -209,6 +210,7 @@ public class PlayClerkPerformanceController {
int orderContinueNumber = 0;
int orderRefundNumber = 0;
int ordersExpiredNumber = 0;
int completedOrders = 0;
BigDecimal orderMoney = BigDecimal.ZERO;
BigDecimal finalAmount = BigDecimal.ZERO;
BigDecimal orderFirstAmount = BigDecimal.ZERO;
@@ -217,6 +219,10 @@ public class PlayClerkPerformanceController {
BigDecimal orderRefundAmount = BigDecimal.ZERO;
BigDecimal estimatedRevenue = BigDecimal.ZERO;
for (PlayOrderInfoEntity orderInfoEntity : orderInfoEntities) {
if (!isCompletedOrder(orderInfoEntity)) {
continue;
}
completedOrders++;
customIds.add(orderInfoEntity.getPurchaserBy());
finalAmount = finalAmount.add(orderInfoEntity.getFinalAmount());
orderMoney = orderMoney.add(orderInfoEntity.getOrderMoney());
@@ -238,7 +244,7 @@ public class PlayClerkPerformanceController {
}
}
PlayClerkPerformanceInfoReturnVo returnVo = new PlayClerkPerformanceInfoReturnVo();
returnVo.setOrderNumber(orderInfoEntities.size());
returnVo.setOrderNumber(completedOrders);
returnVo.setOrderContinueNumber(orderContinueNumber);
returnVo.setOrderRefundNumber(orderRefundNumber);
returnVo.setOrdersExpiredNumber(ordersExpiredNumber);
@@ -281,6 +287,7 @@ public class PlayClerkPerformanceController {
int orderContinueNumber = 0;
int orderRefundNumber = 0;
int ordersExpiredNumber = 0;
int completedOrders = 0;
BigDecimal orderMoney = BigDecimal.ZERO;
BigDecimal finalAmount = BigDecimal.ZERO;
BigDecimal orderFirstAmount = BigDecimal.ZERO;
@@ -289,6 +296,10 @@ public class PlayClerkPerformanceController {
BigDecimal orderRefundAmount = BigDecimal.ZERO;
BigDecimal estimatedRevenue = BigDecimal.ZERO;
for (PlayOrderInfoEntity orderInfoEntity : itemOrderInfo) {
if (!isCompletedOrder(orderInfoEntity)) {
continue;
}
completedOrders++;
customIds.add(orderInfoEntity.getPurchaserBy());
finalAmount = finalAmount.add(orderInfoEntity.getFinalAmount());
orderMoney = orderMoney.add(orderInfoEntity.getOrderMoney());
@@ -311,7 +322,7 @@ public class PlayClerkPerformanceController {
}
PlayClerkPerformanceInfoReturnVo returnVo = new PlayClerkPerformanceInfoReturnVo();
returnVo.setPerformanceDate(performanceDate);
returnVo.setOrderNumber(itemOrderInfo.size());
returnVo.setOrderNumber(completedOrders);
returnVo.setOrderContinueNumber(orderContinueNumber);
returnVo.setOrderRefundNumber(orderRefundNumber);
returnVo.setOrdersExpiredNumber(ordersExpiredNumber);
@@ -326,4 +337,9 @@ public class PlayClerkPerformanceController {
return returnVo;
}
private boolean isCompletedOrder(PlayOrderInfoEntity orderInfoEntity) {
return orderInfoEntity != null
&& OrderConstant.OrderStatus.COMPLETED.getCode().equals(orderInfoEntity.getOrderStatus());
}
}

View File

@@ -77,6 +77,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
int orderContinueNumber = 0;
int orderRefundNumber = 0;
int ordersExpiredNumber = 0;
int completedOrderCount = 0;
BigDecimal finalAmount = BigDecimal.ZERO;
BigDecimal orderFirstAmount = BigDecimal.ZERO;
BigDecimal orderTotalAmount = BigDecimal.ZERO;
@@ -84,6 +85,10 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
BigDecimal orderRefundAmount = BigDecimal.ZERO;
for (PlayOrderInfoEntity orderInfoEntity : orderInfoEntities) {
if (!isCompletedOrder(orderInfoEntity)) {
continue;
}
completedOrderCount++;
customIds.add(orderInfoEntity.getPurchaserBy());
finalAmount = finalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
if (OrderConstant.YesNoFlag.YES.getCode().equals(orderInfoEntity.getFirstOrder())) {
@@ -121,7 +126,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
returnVo.setGroupName(infoEntity.getGroupName());
}
}
returnVo.setOrderNumber(orderInfoEntities.size());
returnVo.setOrderNumber(completedOrderCount);
returnVo.setOrderContinueNumber(orderContinueNumber);
returnVo.setOrderRefundNumber(orderRefundNumber);
returnVo.setOrdersExpiredNumber(ordersExpiredNumber);
@@ -223,7 +228,10 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
private List<ClerkPerformanceTrendPointVo> buildTrend(List<PlayOrderInfoEntity> orders, DateRange range,
int trendDays) {
if (CollectionUtil.isEmpty(orders)) {
List<PlayOrderInfoEntity> completedOrders = orders.stream()
.filter(this::isCompletedOrder)
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(completedOrders)) {
return buildEmptyTrend(range, trendDays);
}
LocalDate end = range.endDate;
@@ -231,7 +239,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
if (start.isBefore(range.startDate)) {
start = range.startDate;
}
Map<LocalDate, List<PlayOrderInfoEntity>> grouped = orders.stream()
Map<LocalDate, List<PlayOrderInfoEntity>> grouped = completedOrders.stream()
.filter(order -> order.getPurchaserTime() != null)
.collect(Collectors.groupingBy(order -> order.getPurchaserTime().toLocalDate()));
List<ClerkPerformanceTrendPointVo> points = new ArrayList<>();
@@ -421,6 +429,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
return BigDecimal.ZERO;
}
List<String> orderIds = orders.stream()
.filter(this::isCompletedOrder)
.map(PlayOrderInfoEntity::getId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toList());
@@ -453,7 +462,12 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
int refundCount = 0;
int expiredCount = 0;
Map<String, Integer> userOrderMap = new HashMap<>();
int orderCount = 0;
for (PlayOrderInfoEntity order : orders) {
if (!isCompletedOrder(order)) {
continue;
}
orderCount++;
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
gmv = gmv.add(finalAmount);
userOrderMap.merge(order.getPurchaserBy(), 1, Integer::sum);
@@ -475,7 +489,6 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
expiredCount++;
}
}
int orderCount = orders.size();
int userCount = userOrderMap.size();
int continuedUserCount = (int) userOrderMap.values().stream().filter(cnt -> cnt > 1).count();
BigDecimal estimatedRevenue = calculateEarningsAmount(clerk.getId(), orders, startTime, endTime);
@@ -568,6 +581,10 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
return value == null ? BigDecimal.ZERO : value;
}
private boolean isCompletedOrder(PlayOrderInfoEntity order) {
return order != null && OrderConstant.OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus());
}
private static final class DateRange {
private final String startTime;
private final String endTime;

View File

@@ -11,6 +11,7 @@ import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
@@ -21,6 +22,7 @@ import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQue
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewSummaryVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
import com.starry.admin.modules.statistics.service.impl.PlayClerkPerformanceServiceImpl;
import com.starry.admin.modules.withdraw.mapper.EarningsLineMapper;
import java.math.BigDecimal;
@@ -231,6 +233,121 @@ class PlayClerkPerformanceServiceImplTest {
assertEquals("2024-08-02 23:59:59", endCaptor.getValue());
}
@Test
@DisplayName("queryOverview ignores non-completed orders when computing summary/GMV")
void queryOverviewSkipsNonCompletedOrders() {
ClerkPerformanceOverviewQueryVo vo = new ClerkPerformanceOverviewQueryVo();
vo.setIncludeSummary(true);
vo.setIncludeRankings(true);
vo.setEndOrderTime(Arrays.asList("2024-08-01 00:00:00", "2024-08-10 23:59:59"));
PlayClerkUserInfoEntity clerk = buildClerk("c10", "Neo", "g10", "l10");
when(playPersonnelGroupInfoService.getValidClerkIdList(any(), any()))
.thenReturn(Collections.singletonList("c10"));
when(clerkUserInfoService.list((Wrapper<PlayClerkUserInfoEntity>) any()))
.thenReturn(Collections.singletonList(clerk));
when(playClerkLevelInfoService.selectAll()).thenReturn(Collections.singletonList(level("l10", "至尊")));
when(playPersonnelGroupInfoService.selectAll()).thenReturn(Collections.singletonList(group("g10", "十组")));
PlayOrderInfoEntity o1 = order("c10", "user1", "1", "0", "0", new BigDecimal("100.00"),
new BigDecimal("60.00"), LocalDateTime.of(2024, Month.AUGUST, 1, 10, 0));
PlayOrderInfoEntity o2 = order("c10", "user2", "0", "0", "0", new BigDecimal("80.00"),
new BigDecimal("40.00"), LocalDateTime.of(2024, Month.AUGUST, 2, 11, 0));
PlayOrderInfoEntity o3 = withRefund(
order("c10", "user3", "0", "0", "1", new BigDecimal("60.00"), new BigDecimal("35.00"),
LocalDateTime.of(2024, Month.AUGUST, 3, 9, 30)),
new BigDecimal("15.00"));
PlayOrderInfoEntity o4 = order("c10", "user4", "0", "2", "0", new BigDecimal("40.00"),
new BigDecimal("20.00"), LocalDateTime.of(2024, Month.AUGUST, 4, 16, 45));
PlayOrderInfoEntity o5 = order("c10", "user5", "1", "0", "0", new BigDecimal("20.00"),
new BigDecimal("10.00"), LocalDateTime.of(2024, Month.AUGUST, 5, 13, 15));
PlayOrderInfoEntity o6 = withStatus(
order("c10", "user6", "0", "0", "0", new BigDecimal("70.00"), new BigDecimal("30.00"),
LocalDateTime.of(2024, Month.AUGUST, 6, 14, 0)),
OrderConstant.OrderStatus.CANCELLED.getCode());
PlayOrderInfoEntity o7 = withStatus(
order("c10", "user7", "0", "0", "0", new BigDecimal("50.00"), new BigDecimal("25.00"),
LocalDateTime.of(2024, Month.AUGUST, 6, 18, 20)),
OrderConstant.OrderStatus.IN_PROGRESS.getCode());
PlayOrderInfoEntity o8 = withStatus(
order("c10", "user8", "0", "0", "0", new BigDecimal("45.00"), new BigDecimal("22.00"),
LocalDateTime.of(2024, Month.AUGUST, 7, 15, 5)),
OrderConstant.OrderStatus.ACCEPTED.getCode());
PlayOrderInfoEntity o9 = withStatus(
order("c10", "user9", "0", "0", "0", new BigDecimal("30.00"), new BigDecimal("15.00"),
LocalDateTime.of(2024, Month.AUGUST, 8, 17, 40)),
OrderConstant.OrderStatus.PENDING.getCode());
PlayOrderInfoEntity o10 = withStatus(
order("c10", "user10", "1", "0", "0", new BigDecimal("25.00"), new BigDecimal("12.00"),
LocalDateTime.of(2024, Month.AUGUST, 9, 19, 10)),
OrderConstant.OrderStatus.CANCELLED.getCode());
List<PlayOrderInfoEntity> mixedOrders =
Arrays.asList(o1, o2, o3, o4, o5, o6, o7, o8, o9, o10);
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c10"), anyString(), anyString()))
.thenReturn(mixedOrders);
when(earningsLineMapper.sumAmountByClerkAndOrderIds(eq("c10"), anyCollection(), anyString(), anyString()))
.thenReturn(new BigDecimal("210.00"));
setAuthentication();
ClerkPerformanceOverviewResponseVo response;
try {
response = service.queryOverview(vo);
} finally {
clearAuthentication();
}
assertEquals(1, response.getRankings().size());
ClerkPerformanceSnapshotVo snapshot = response.getRankings().get(0);
assertEquals(5, snapshot.getOrderCount());
assertEquals(new BigDecimal("300.00"), snapshot.getGmv());
assertEquals(new BigDecimal("40.00"), snapshot.getRewardAmount());
assertEquals(new BigDecimal("15.00"), snapshot.getRefundAmount());
assertEquals(new BigDecimal("210.00"), snapshot.getEstimatedRevenue());
ClerkPerformanceOverviewSummaryVo summary = response.getSummary();
assertEquals(5, summary.getTotalOrderCount());
assertEquals(new BigDecimal("300.00"), summary.getTotalGmv());
assertEquals(1, summary.getTotalRefundOrderCount());
ArgumentCaptor<Collection<String>> idCaptor = ArgumentCaptor.forClass(Collection.class);
verify(earningsLineMapper).sumAmountByClerkAndOrderIds(eq("c10"), idCaptor.capture(),
anyString(), anyString());
assertEquals(5, idCaptor.getValue().size());
assertTrue(idCaptor.getValue().containsAll(
Arrays.asList(o1.getId(), o2.getId(), o3.getId(), o4.getId(), o5.getId())));
}
@Test
@DisplayName("getClerkPerformanceInfo should ignore non-completed orders when aggregating GMV")
void getClerkPerformanceInfoSkipsNonCompletedOrders() {
PlayClerkUserInfoEntity clerk = buildClerk("c9", "Nine", "gX", "lX");
List<PlayClerkLevelInfoEntity> levels = Collections.singletonList(level("lX", "钻石"));
List<PlayPersonnelGroupInfoEntity> groups = Collections.singletonList(group("gX", "特战组"));
PlayOrderInfoEntity completed = order("c9", "userA", "1", "0", "0", new BigDecimal("120.00"),
new BigDecimal("60.00"), LocalDateTime.of(2024, Month.AUGUST, 1, 10, 0));
PlayOrderInfoEntity cancelled = withStatus(
order("c9", "userB", "0", "0", "0", new BigDecimal("200.00"), new BigDecimal("100.00"),
LocalDateTime.of(2024, Month.AUGUST, 1, 12, 0)),
OrderConstant.OrderStatus.CANCELLED.getCode());
when(earningsLineMapper.sumAmountByClerkAndOrderIds(eq("c9"), anyCollection(), anyString(), anyString()))
.thenReturn(new BigDecimal("60.00"));
PlayClerkPerformanceInfoReturnVo result = service.getClerkPerformanceInfo(clerk,
Arrays.asList(completed, cancelled), levels, groups, "2024-08-01 00:00:00", "2024-08-02 23:59:59");
assertEquals(new BigDecimal("120.00"), result.getFinalAmount());
assertEquals(1, result.getOrderNumber());
assertEquals(1, result.getCustomNumber());
ArgumentCaptor<Collection<String>> idCaptor = ArgumentCaptor.forClass(Collection.class);
verify(earningsLineMapper).sumAmountByClerkAndOrderIds(eq("c9"), idCaptor.capture(),
eq("2024-08-01 00:00:00"), eq("2024-08-02 23:59:59"));
assertEquals(1, idCaptor.getValue().size());
assertTrue(idCaptor.getValue().contains(completed.getId()));
}
private PlayClerkUserInfoEntity buildClerk(String id, String name, String groupId, String levelId) {
PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
entity.setId(id);
@@ -270,6 +387,7 @@ class PlayClerkPerformanceServiceImplTest {
order.setOrdersExpiredState("1".equals(refundType) ? "1" : "0");
order.setPurchaserTime(purchaserTime);
order.setId(clerkId + "-" + purchaser + "-" + purchaserTime.toString());
order.setOrderStatus(OrderConstant.OrderStatus.COMPLETED.getCode());
return order;
}
@@ -288,4 +406,9 @@ class PlayClerkPerformanceServiceImplTest {
order.setRefundAmount(refundAmount);
return order;
}
private PlayOrderInfoEntity withStatus(PlayOrderInfoEntity order, String status) {
order.setOrderStatus(status);
return order;
}
}