fix: fix performance salary calculation
Some checks failed
Build and Push Backend / docker (push) Failing after 6s

This commit is contained in:
irving
2025-10-31 21:31:56 -04:00
parent e7ccadaea0
commit 8f89955405
9 changed files with 227 additions and 61 deletions

View File

@@ -45,6 +45,7 @@ import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
import com.starry.admin.modules.weichat.entity.order.*;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.admin.modules.withdraw.service.IEarningsService;
import com.starry.admin.utils.DateRangeUtils;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.utils.ConvertUtil;
import com.starry.common.utils.IdUtils;
@@ -456,8 +457,17 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
public List<PlayOrderInfoEntity> clerkSelectOrderInfoList(String clerkId, String startTime, String endTime) {
LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
lambdaQueryWrapper.between(PlayOrderInfoEntity::getPurchaserTime, startTime, endTime);
String normalizedStart = DateRangeUtils.normalizeStartOptional(startTime);
String normalizedEnd = DateRangeUtils.normalizeEndOptional(endTime);
if (StrUtil.isNotBlank(normalizedStart) && StrUtil.isNotBlank(normalizedEnd)) {
lambdaQueryWrapper.between(PlayOrderInfoEntity::getPurchaserTime, normalizedStart, normalizedEnd);
} else {
if (StrUtil.isNotBlank(normalizedStart)) {
lambdaQueryWrapper.ge(PlayOrderInfoEntity::getPurchaserTime, normalizedStart);
}
if (StrUtil.isNotBlank(normalizedEnd)) {
lambdaQueryWrapper.le(PlayOrderInfoEntity::getPurchaserTime, normalizedEnd);
}
}
return this.baseMapper.selectList(lambdaQueryWrapper);
}

View File

@@ -16,6 +16,7 @@ import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewRes
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoQueryVo;
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
import com.starry.admin.utils.DateRangeUtils;
import com.starry.common.result.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -69,19 +70,27 @@ public class PlayClerkPerformanceController {
public R listByPage(
@ApiParam(value = "查询条件", required = true) @Validated @RequestBody PlayClerkPerformanceInfoQueryVo vo) {
IPage<PlayClerkUserInfoEntity> page = clerkUserInfoService.selectByPage(vo);
IPage<PlayClerkPerformanceInfoReturnVo> voPage = page.convert(u -> {
IPage<PlayClerkPerformanceInfoReturnVo> voPage = page.convert(user -> {
List<PlayClerkLevelInfoEntity> clerkLevelInfoEntity = playClerkLevelInfoService.selectAll();
String startTime = vo.getEndOrderTime() != null ? vo.getEndOrderTime().get(0) : "";
String endTime = vo.getEndOrderTime() != null ? vo.getEndOrderTime().get(1) : "";
List<PlayOrderInfoEntity> orderInfoEntities = playOrderInfoService.clerkSelectOrderInfoList(u.getId(),
String rawStart = vo.getEndOrderTime() != null && vo.getEndOrderTime().size() > 0
? vo.getEndOrderTime().get(0)
: null;
String rawEnd = vo.getEndOrderTime() != null && vo.getEndOrderTime().size() > 1
? vo.getEndOrderTime().get(1)
: null;
String startTime = DateRangeUtils.normalizeStartOptional(rawStart);
String endTime = DateRangeUtils.normalizeEndOptional(rawEnd);
List<PlayOrderInfoEntity> orderInfoEntities = playOrderInfoService.clerkSelectOrderInfoList(user.getId(),
startTime, endTime);
List<PlayPersonnelGroupInfoEntity> groupInfoEntities = playPersonnelGroupInfoService.selectAll();
return playClerkPerformanceService.getClerkPerformanceInfo(u, orderInfoEntities, clerkLevelInfoEntity,
groupInfoEntities);
return playClerkPerformanceService.getClerkPerformanceInfo(user, orderInfoEntities, clerkLevelInfoEntity,
groupInfoEntities, startTime, endTime);
});
voPage.setRecords(voPage.getRecords().stream()
.sorted(Comparator.comparing(PlayClerkPerformanceInfoReturnVo::getOrderNumber).reversed())
.collect(Collectors.toList()));
return R.ok(voPage);
}

View File

@@ -32,7 +32,7 @@ public interface IPlayClerkPerformanceService {
*/
PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo,
List<PlayOrderInfoEntity> orderInfoEntities, List<PlayClerkLevelInfoEntity> clerkLevelInfoEntity,
List<PlayPersonnelGroupInfoEntity> groupInfoEntities);
List<PlayPersonnelGroupInfoEntity> groupInfoEntities, String startTime, String endTime);
ClerkPerformanceOverviewResponseVo queryOverview(ClerkPerformanceOverviewQueryVo vo);

View File

@@ -9,6 +9,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;
@@ -26,12 +27,13 @@ import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceTrendPointVo;
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
import com.starry.admin.modules.withdraw.mapper.EarningsLineMapper;
import com.starry.admin.utils.DateRangeUtils;
import com.starry.admin.utils.SecurityUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -51,9 +53,6 @@ import org.springframework.stereotype.Service;
@Service
public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceService {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Resource
private IPlayClerkUserInfoService clerkUserInfoService;
@@ -66,10 +65,14 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
@Resource
private IPlayPersonnelGroupInfoService playPersonnelGroupInfoService;
@Resource
private EarningsLineMapper earningsLineMapper;
@Override
public PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo,
List<PlayOrderInfoEntity> orderInfoEntities, List<PlayClerkLevelInfoEntity> clerkLevelInfoEntities,
List<PlayPersonnelGroupInfoEntity> groupInfoEntities) {
List<PlayPersonnelGroupInfoEntity> groupInfoEntities, String startTime, String endTime) {
Set<String> customIds = new HashSet<>();
int orderContinueNumber = 0;
int orderRefundNumber = 0;
@@ -79,28 +82,31 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
BigDecimal orderTotalAmount = BigDecimal.ZERO;
BigDecimal orderRewardAmount = BigDecimal.ZERO;
BigDecimal orderRefundAmount = BigDecimal.ZERO;
BigDecimal estimatedRevenue = BigDecimal.ZERO;
for (PlayOrderInfoEntity orderInfoEntity : orderInfoEntities) {
customIds.add(orderInfoEntity.getPurchaserBy());
finalAmount = finalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
if ("1".equals(orderInfoEntity.getFirstOrder())) {
if (OrderConstant.YesNoFlag.YES.getCode().equals(orderInfoEntity.getFirstOrder())) {
orderFirstAmount = orderFirstAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
} else {
orderContinueNumber++;
orderTotalAmount = orderTotalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
}
if ("2".equals(orderInfoEntity.getPlaceType())) {
if (OrderConstant.PlaceType.REWARD.getCode().equals(orderInfoEntity.getPlaceType())) {
orderRewardAmount = orderRewardAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
}
if ("1".equals(orderInfoEntity.getRefundType())) {
if (OrderConstant.OrderRefundFlag.REFUNDED.getCode().equals(orderInfoEntity.getRefundType())) {
orderRefundNumber++;
orderRefundAmount = orderRefundAmount.add(defaultZero(orderInfoEntity.getRefundAmount()));
}
if ("1".equals(orderInfoEntity.getOrdersExpiredState())) {
if (OrderConstant.OrdersExpiredState.EXPIRED.getCode().equals(orderInfoEntity.getOrdersExpiredState())) {
ordersExpiredNumber++;
}
estimatedRevenue = estimatedRevenue.add(defaultZero(orderInfoEntity.getEstimatedRevenue()));
}
BigDecimal estimatedRevenue =
calculateEarningsAmount(userInfo.getId(), orderInfoEntities, startTime, endTime);
PlayClerkPerformanceInfoReturnVo returnVo = new PlayClerkPerformanceInfoReturnVo();
returnVo.setClerkId(userInfo.getId());
returnVo.setClerkNickname(userInfo.getNickname());
@@ -149,7 +155,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
for (PlayClerkUserInfoEntity clerk : clerks) {
List<PlayOrderInfoEntity> orders = playOrderInfoService.clerkSelectOrderInfoList(clerk.getId(),
range.startTime, range.endTime);
snapshots.add(buildSnapshot(clerk, orders, levelNameMap, groupNameMap));
snapshots.add(buildSnapshot(clerk, orders, levelNameMap, groupNameMap, range.startTime, range.endTime));
}
int total = snapshots.size();
ClerkPerformanceOverviewSummaryVo summary = aggregateSummary(snapshots);
@@ -184,7 +190,8 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
(a, b) -> a));
List<PlayOrderInfoEntity> orders = playOrderInfoService.clerkSelectOrderInfoList(clerk.getId(),
range.startTime, range.endTime);
ClerkPerformanceSnapshotVo snapshot = buildSnapshot(clerk, orders, levelNameMap, groupNameMap);
ClerkPerformanceSnapshotVo snapshot =
buildSnapshot(clerk, orders, levelNameMap, groupNameMap, range.startTime, range.endTime);
ClerkPerformanceDetailResponseVo responseVo = new ClerkPerformanceDetailResponseVo();
responseVo.setProfile(buildProfile(clerk, levelNameMap, groupNameMap));
responseVo.setSnapshot(snapshot);
@@ -262,7 +269,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
for (PlayOrderInfoEntity order : orders) {
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
gmv = gmv.add(finalAmount);
if ("1".equals(order.getFirstOrder())) {
if (OrderConstant.YesNoFlag.YES.getCode().equals(order.getFirstOrder())) {
firstAmount = firstAmount.add(finalAmount);
} else {
continuedAmount = continuedAmount.add(finalAmount);
@@ -408,8 +415,27 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
return summary;
}
private BigDecimal calculateEarningsAmount(String clerkId, List<PlayOrderInfoEntity> orders, String startTime,
String endTime) {
if (StrUtil.isBlank(clerkId) || CollectionUtil.isEmpty(orders)) {
return BigDecimal.ZERO;
}
List<String> orderIds = orders.stream()
.map(PlayOrderInfoEntity::getId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(orderIds)) {
return BigDecimal.ZERO;
}
String normalizedStart = DateRangeUtils.normalizeStartOptional(startTime);
String normalizedEnd = DateRangeUtils.normalizeEndOptional(endTime);
BigDecimal sum = earningsLineMapper.sumAmountByClerkAndOrderIds(clerkId, orderIds, normalizedStart,
normalizedEnd);
return defaultZero(sum);
}
private ClerkPerformanceSnapshotVo buildSnapshot(PlayClerkUserInfoEntity clerk, List<PlayOrderInfoEntity> orders,
Map<String, String> levelNameMap, Map<String, String> groupNameMap) {
Map<String, String> levelNameMap, Map<String, String> groupNameMap, String startTime, String endTime) {
ClerkPerformanceSnapshotVo snapshot = new ClerkPerformanceSnapshotVo();
snapshot.setClerkId(clerk.getId());
snapshot.setClerkNickname(clerk.getNickname());
@@ -422,7 +448,6 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
BigDecimal continuedAmount = BigDecimal.ZERO;
BigDecimal rewardAmount = BigDecimal.ZERO;
BigDecimal refundAmount = BigDecimal.ZERO;
BigDecimal estimatedRevenue = BigDecimal.ZERO;
int firstCount = 0;
int continuedCount = 0;
int refundCount = 0;
@@ -432,28 +457,28 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
gmv = gmv.add(finalAmount);
userOrderMap.merge(order.getPurchaserBy(), 1, Integer::sum);
if ("1".equals(order.getFirstOrder())) {
if (OrderConstant.YesNoFlag.YES.getCode().equals(order.getFirstOrder())) {
firstCount++;
firstAmount = firstAmount.add(finalAmount);
} else {
continuedCount++;
continuedAmount = continuedAmount.add(finalAmount);
}
if ("2".equals(order.getPlaceType())) {
if (OrderConstant.PlaceType.REWARD.getCode().equals(order.getPlaceType())) {
rewardAmount = rewardAmount.add(finalAmount);
}
if ("1".equals(order.getRefundType())) {
if (OrderConstant.OrderRefundFlag.REFUNDED.getCode().equals(order.getRefundType())) {
refundCount++;
refundAmount = refundAmount.add(defaultZero(order.getRefundAmount()));
}
if ("1".equals(order.getOrdersExpiredState())) {
if (OrderConstant.OrdersExpiredState.EXPIRED.getCode().equals(order.getOrdersExpiredState())) {
expiredCount++;
}
estimatedRevenue = estimatedRevenue.add(defaultZero(order.getEstimatedRevenue()));
}
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);
snapshot.setGmv(gmv);
snapshot.setFirstOrderAmount(firstAmount);
snapshot.setContinuedOrderAmount(continuedAmount);
@@ -504,41 +529,19 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
String startStr;
String endStr;
if (CollectionUtil.isNotEmpty(endOrderTime) && endOrderTime.size() >= 2) {
startStr = normalizeStart(endOrderTime.get(0));
endStr = normalizeEnd(endOrderTime.get(1));
startStr = DateRangeUtils.normalizeStart(endOrderTime.get(0));
endStr = DateRangeUtils.normalizeEnd(endOrderTime.get(1));
} else {
LocalDate end = LocalDate.now();
LocalDate start = end.minusDays(6);
startStr = start.format(DATE_FORMATTER) + " 00:00:00";
endStr = end.format(DATE_FORMATTER) + " 23:59:59";
startStr = start.format(DateRangeUtils.DATE_FORMATTER) + " 00:00:00";
endStr = end.format(DateRangeUtils.DATE_FORMATTER) + " 23:59:59";
}
LocalDate startDate = LocalDate.parse(startStr.substring(0, 10), DATE_FORMATTER);
LocalDate endDate = LocalDate.parse(endStr.substring(0, 10), DATE_FORMATTER);
LocalDate startDate = LocalDate.parse(startStr.substring(0, 10), DateRangeUtils.DATE_FORMATTER);
LocalDate endDate = LocalDate.parse(endStr.substring(0, 10), DateRangeUtils.DATE_FORMATTER);
return new DateRange(startStr, endStr, startDate, endDate);
}
private String normalizeStart(String raw) {
if (StrUtil.isBlank(raw)) {
return LocalDate.now().minusDays(6).format(DATE_FORMATTER) + " 00:00:00";
}
if (raw.length() > 10) {
LocalDateTime.parse(raw, DATE_TIME_FORMATTER);
return raw;
}
return raw + " 00:00:00";
}
private String normalizeEnd(String raw) {
if (StrUtil.isBlank(raw)) {
return LocalDate.now().format(DATE_FORMATTER) + " 23:59:59";
}
if (raw.length() > 10) {
LocalDateTime.parse(raw, DATE_TIME_FORMATTER);
return raw;
}
return raw + " 23:59:59";
}
private BigDecimal calcPercentage(int numerator, int denominator) {
if (denominator <= 0) {
return BigDecimal.ZERO;

View File

@@ -33,6 +33,7 @@ import com.starry.admin.modules.weichat.entity.order.PlayClerkOrderInfoQueryVo;
import com.starry.admin.modules.weichat.entity.order.PlayClerkOrderListReturnVo;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.admin.modules.weichat.service.WxCustomUserService;
import com.starry.admin.utils.DateRangeUtils;
import com.starry.admin.utils.SecurityUtils;
import com.starry.admin.utils.SmsUtils;
import com.starry.common.redis.RedisCache;
@@ -126,11 +127,13 @@ public class WxClerkController {
PlayClerkUserInfoEntity entity = clerkUserInfoService
.selectById(ThreadLocalRequestDetail.getClerkUserInfo().getId());
List<PlayClerkLevelInfoEntity> clerkLevelInfoEntity = playClerkLevelInfoService.selectAll();
String startTime = DateRangeUtils.normalizeStartOptional(vo.getStartTime());
String endTime = DateRangeUtils.normalizeEndOptional(vo.getEndTime());
List<PlayOrderInfoEntity> orderInfoEntities = playOrderInfoService.clerkSelectOrderInfoList(entity.getId(),
vo.getStartTime(), vo.getEndTime());
startTime, endTime);
List<PlayPersonnelGroupInfoEntity> groupInfoEntities = playPersonnelGroupInfoService.selectAll();
return R.ok(playClerkPerformanceService.getClerkPerformanceInfo(entity, orderInfoEntities, clerkLevelInfoEntity,
groupInfoEntities));
groupInfoEntities, startTime, endTime));
}
/**

View File

@@ -178,10 +178,16 @@ public class WxCustomMpService {
data.add(new WxMpTemplateData("thing11", commodityName));
data.add(new WxMpTemplateData("amount8", money));
templateMessage.setData(data);
PlayClerkUserInfoEntity clerkUserInfo =
StringUtils.isBlank(openId) ? null : clerkUserInfoService.selectByOpenid(openId);
String clerkId = clerkUserInfo == null ? null : clerkUserInfo.getId();
String clerkNickname = clerkUserInfo == null ? null : clerkUserInfo.getNickname();
try {
proxyWxMpService(tenantId).getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
log.error("接单成功发送消息异常", e);
log.error(
"接单成功发送消息异常, tenantId={}, tenantName={}, orderId={}, orderNo={}, recipientType=clerk, clerkId={}, openId={}, nickname={}",
tenantId, tenant.getTenantName(), orderId, orderNo, clerkId, openId, clerkNickname, e);
}
}

View File

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -34,4 +35,28 @@ public interface EarningsLineMapper extends BaseMapper<EarningsLineEntity> {
" AND (status = 'available' OR (status = 'frozen' AND unlock_time <= #{now})) " +
"ORDER BY unlock_time ASC")
List<EarningsLineEntity> selectWithdrawableLines(@Param("clerkId") String clerkId, @Param("now") LocalDateTime now);
@Select("<script>" +
"SELECT COALESCE(SUM(el.amount), 0) " +
"FROM play_earnings_line el " +
"JOIN play_order_info oi ON oi.id = el.order_id " +
"WHERE el.deleted = 0 " +
" AND oi.deleted = 0 " +
" AND el.clerk_id = #{clerkId} " +
" AND el.status &lt;&gt; 'reversed' " +
"<if test='orderIds != null and orderIds.size &gt; 0'>" +
" AND el.order_id IN " +
" <foreach item='id' collection='orderIds' open='(' separator=',' close=')'>#{id}</foreach>" +
"</if>" +
"<if test='startTime != null'>" +
" AND oi.purchaser_time &gt;= #{startTime}" +
"</if>" +
"<if test='endTime != null'>" +
" AND oi.purchaser_time &lt;= #{endTime}" +
"</if>" +
"</script>")
BigDecimal sumAmountByClerkAndOrderIds(@Param("clerkId") String clerkId,
@Param("orderIds") Collection<String> orderIds,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@@ -0,0 +1,69 @@
package com.starry.admin.utils;
import cn.hutool.core.util.StrUtil;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
/**
* Utilities for normalizing date/time range inputs.
*/
public final class DateRangeUtils {
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private DateRangeUtils() {}
public static String normalizeStart(String raw) {
if (StrUtil.isBlank(raw)) {
return LocalDate.now().minusDays(6).format(DATE_FORMATTER) + " 00:00:00";
}
if (raw.length() > 10) {
LocalDateTime.parse(raw, DATE_TIME_FORMATTER);
return raw;
}
return raw + " 00:00:00";
}
public static String normalizeEnd(String raw) {
if (StrUtil.isBlank(raw)) {
return LocalDate.now().format(DATE_FORMATTER) + " 23:59:59";
}
if (raw.length() > 10) {
LocalDateTime.parse(raw, DATE_TIME_FORMATTER);
return raw;
}
return raw + " 23:59:59";
}
public static String normalizeStartOptional(String raw) {
if (StrUtil.isBlank(raw)) {
return null;
}
return normalizeFlexible(raw, false);
}
public static String normalizeEndOptional(String raw) {
if (StrUtil.isBlank(raw)) {
return null;
}
return normalizeFlexible(raw, true);
}
private static String normalizeFlexible(String raw, boolean isEnd) {
String candidate = raw.trim().replace('T', ' ').replace("Z", "");
if (candidate.length() <= 10) {
candidate = candidate + (isEnd ? " 23:59:59" : " 00:00:00");
} else if (candidate.length() > 19) {
candidate = candidate.substring(0, 19);
}
try {
LocalDateTime.parse(candidate, DATE_TIME_FORMATTER);
return candidate;
} catch (DateTimeParseException ex) {
return candidate;
}
}
}

View File

@@ -22,15 +22,18 @@ import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewRes
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewSummaryVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
import com.starry.admin.modules.statistics.service.impl.PlayClerkPerformanceServiceImpl;
import com.starry.admin.modules.withdraw.mapper.EarningsLineMapper;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -55,6 +58,9 @@ class PlayClerkPerformanceServiceImplTest {
@Mock
private IPlayPersonnelGroupInfoService playPersonnelGroupInfoService;
@Mock
private EarningsLineMapper earningsLineMapper;
@InjectMocks
private PlayClerkPerformanceServiceImpl service;
@@ -89,6 +95,10 @@ class PlayClerkPerformanceServiceImplTest {
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c1"), anyString(), anyString())).thenReturn(ordersA);
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c2"), anyString(), anyString())).thenReturn(ordersB);
when(earningsLineMapper.sumAmountByClerkAndOrderIds(eq("c1"), anyCollection(), any(), any()))
.thenReturn(new BigDecimal("170.00"));
when(earningsLineMapper.sumAmountByClerkAndOrderIds(eq("c2"), anyCollection(), any(), any()))
.thenReturn(new BigDecimal("55.00"));
setAuthentication();
try {
@@ -109,6 +119,8 @@ class PlayClerkPerformanceServiceImplTest {
assertEquals(2, top.getContinuedOrderCount());
assertEquals(new BigDecimal("66.67"), top.getContinuedRate());
assertEquals(new BigDecimal("100.00"), top.getAverageTicketPrice());
assertEquals(new BigDecimal("170.00"), top.getEstimatedRevenue());
assertEquals(new BigDecimal("55.00"), response.getRankings().get(1).getEstimatedRevenue());
ClerkPerformanceOverviewSummaryVo summary = response.getSummary();
assertEquals(new BigDecimal("380.00"), summary.getTotalGmv());
@@ -117,6 +129,7 @@ class PlayClerkPerformanceServiceImplTest {
assertEquals(2, summary.getTotalContinuedOrderCount());
assertEquals(new BigDecimal("50.00"), summary.getContinuedRate());
assertEquals(new BigDecimal("95.00"), summary.getAverageTicketPrice());
assertEquals(new BigDecimal("225.00"), summary.getTotalEstimatedRevenue());
} finally {
clearAuthentication();
}
@@ -146,6 +159,8 @@ class PlayClerkPerformanceServiceImplTest {
withRefund(order("c1", "userB", "0", "2", "1", new BigDecimal("60.00"), new BigDecimal("30.00"),
LocalDateTime.of(2024, Month.AUGUST, 2, 18, 0)), new BigDecimal("20.00")));
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c1"), anyString(), anyString())).thenReturn(orders);
when(earningsLineMapper.sumAmountByClerkAndOrderIds(eq("c1"), anyCollection(), any(), any()))
.thenReturn(new BigDecimal("110.00"));
setAuthentication();
try {
@@ -158,6 +173,7 @@ class PlayClerkPerformanceServiceImplTest {
assertEquals(3, response.getSnapshot().getOrderCount());
assertEquals(new BigDecimal("66.67"), response.getSnapshot().getContinuedRate());
assertEquals(new BigDecimal("86.67"), response.getSnapshot().getAverageTicketPrice());
assertEquals(new BigDecimal("110.00"), response.getSnapshot().getEstimatedRevenue());
assertNotNull(response.getComposition());
assertEquals(4, response.getComposition().getOrderComposition().size());
@@ -191,6 +207,30 @@ class PlayClerkPerformanceServiceImplTest {
}
}
@Test
@DisplayName("getClerkPerformanceInfo aligns earnings lookup with purchaser time")
void getClerkPerformanceInfoAlignsEarningsLookupWithPurchaserTime() {
PlayClerkUserInfoEntity clerk = buildClerk("c3", "Carol", "g3", "l3");
List<PlayOrderInfoEntity> orders = Collections.singletonList(
order("c3", "userX", "1", "0", "0", new BigDecimal("99.00"), new BigDecimal("50.00"),
LocalDateTime.of(2024, Month.AUGUST, 1, 15, 30)));
when(earningsLineMapper.sumAmountByClerkAndOrderIds(eq("c3"), anyCollection(), any(), any()))
.thenReturn(new BigDecimal("80.00"));
service.getClerkPerformanceInfo(clerk, orders, Collections.singletonList(level("l3", "钻石")),
Collections.singletonList(group("g3", "三组")), "2024-08-01", "2024-08-02");
ArgumentCaptor<Collection<String>> orderIdsCaptor = ArgumentCaptor.forClass(Collection.class);
ArgumentCaptor<String> startCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> endCaptor = ArgumentCaptor.forClass(String.class);
verify(earningsLineMapper).sumAmountByClerkAndOrderIds(eq("c3"), orderIdsCaptor.capture(),
startCaptor.capture(), endCaptor.capture());
assertTrue(orderIdsCaptor.getValue().contains(orders.get(0).getId()));
assertEquals("2024-08-01 00:00:00", startCaptor.getValue());
assertEquals("2024-08-02 23:59:59", endCaptor.getValue());
}
private PlayClerkUserInfoEntity buildClerk(String id, String name, String groupId, String levelId) {
PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
entity.setId(id);
@@ -229,6 +269,7 @@ class PlayClerkPerformanceServiceImplTest {
order.setEstimatedRevenue(estimatedRevenue);
order.setOrdersExpiredState("1".equals(refundType) ? "1" : "0");
order.setPurchaserTime(purchaserTime);
order.setId(clerkId + "-" + purchaser + "-" + purchaserTime.toString());
return order;
}