diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java index 9464fb6..5ace627 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java @@ -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 clerkSelectOrderInfoList(String clerkId, String startTime, String endTime) { LambdaQueryWrapper 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); } diff --git a/play-admin/src/main/java/com/starry/admin/modules/statistics/controller/PlayClerkPerformanceController.java b/play-admin/src/main/java/com/starry/admin/modules/statistics/controller/PlayClerkPerformanceController.java index 0449cc5..e578243 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/statistics/controller/PlayClerkPerformanceController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/statistics/controller/PlayClerkPerformanceController.java @@ -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 page = clerkUserInfoService.selectByPage(vo); - IPage voPage = page.convert(u -> { + IPage voPage = page.convert(user -> { List clerkLevelInfoEntity = playClerkLevelInfoService.selectAll(); - String startTime = vo.getEndOrderTime() != null ? vo.getEndOrderTime().get(0) : ""; - String endTime = vo.getEndOrderTime() != null ? vo.getEndOrderTime().get(1) : ""; - List 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 orderInfoEntities = playOrderInfoService.clerkSelectOrderInfoList(user.getId(), startTime, endTime); List 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); } diff --git a/play-admin/src/main/java/com/starry/admin/modules/statistics/service/IPlayClerkPerformanceService.java b/play-admin/src/main/java/com/starry/admin/modules/statistics/service/IPlayClerkPerformanceService.java index 43b3854..69dade5 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/statistics/service/IPlayClerkPerformanceService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/statistics/service/IPlayClerkPerformanceService.java @@ -32,7 +32,7 @@ public interface IPlayClerkPerformanceService { */ PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo, List orderInfoEntities, List clerkLevelInfoEntity, - List groupInfoEntities); + List groupInfoEntities, String startTime, String endTime); ClerkPerformanceOverviewResponseVo queryOverview(ClerkPerformanceOverviewQueryVo vo); diff --git a/play-admin/src/main/java/com/starry/admin/modules/statistics/service/impl/PlayClerkPerformanceServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/statistics/service/impl/PlayClerkPerformanceServiceImpl.java index 4c89dcf..5b38ec8 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/statistics/service/impl/PlayClerkPerformanceServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/statistics/service/impl/PlayClerkPerformanceServiceImpl.java @@ -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 orderInfoEntities, List clerkLevelInfoEntities, - List groupInfoEntities) { + List groupInfoEntities, String startTime, String endTime) { + Set 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 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 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 orders, String startTime, + String endTime) { + if (StrUtil.isBlank(clerkId) || CollectionUtil.isEmpty(orders)) { + return BigDecimal.ZERO; + } + List 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 orders, - Map levelNameMap, Map groupNameMap) { + Map levelNameMap, Map 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; diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java index ec24786..04cc910 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java @@ -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 clerkLevelInfoEntity = playClerkLevelInfoService.selectAll(); + String startTime = DateRangeUtils.normalizeStartOptional(vo.getStartTime()); + String endTime = DateRangeUtils.normalizeEndOptional(vo.getEndTime()); List orderInfoEntities = playOrderInfoService.clerkSelectOrderInfoList(entity.getId(), - vo.getStartTime(), vo.getEndTime()); + startTime, endTime); List groupInfoEntities = playPersonnelGroupInfoService.selectAll(); return R.ok(playClerkPerformanceService.getClerkPerformanceInfo(entity, orderInfoEntities, clerkLevelInfoEntity, - groupInfoEntities)); + groupInfoEntities, startTime, endTime)); } /** diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java index 0a65f27..a123fee 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java @@ -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); } } diff --git a/play-admin/src/main/java/com/starry/admin/modules/withdraw/mapper/EarningsLineMapper.java b/play-admin/src/main/java/com/starry/admin/modules/withdraw/mapper/EarningsLineMapper.java index c298824..4373534 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/withdraw/mapper/EarningsLineMapper.java +++ b/play-admin/src/main/java/com/starry/admin/modules/withdraw/mapper/EarningsLineMapper.java @@ -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 { " AND (status = 'available' OR (status = 'frozen' AND unlock_time <= #{now})) " + "ORDER BY unlock_time ASC") List selectWithdrawableLines(@Param("clerkId") String clerkId, @Param("now") LocalDateTime now); + + @Select("") + BigDecimal sumAmountByClerkAndOrderIds(@Param("clerkId") String clerkId, + @Param("orderIds") Collection orderIds, + @Param("startTime") String startTime, + @Param("endTime") String endTime); } diff --git a/play-admin/src/main/java/com/starry/admin/utils/DateRangeUtils.java b/play-admin/src/main/java/com/starry/admin/utils/DateRangeUtils.java new file mode 100644 index 0000000..b5203f5 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/utils/DateRangeUtils.java @@ -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; + } + } +} diff --git a/play-admin/src/test/java/com/starry/admin/modules/statistics/service/PlayClerkPerformanceServiceImplTest.java b/play-admin/src/test/java/com/starry/admin/modules/statistics/service/PlayClerkPerformanceServiceImplTest.java index 8442a45..adc4033 100644 --- a/play-admin/src/test/java/com/starry/admin/modules/statistics/service/PlayClerkPerformanceServiceImplTest.java +++ b/play-admin/src/test/java/com/starry/admin/modules/statistics/service/PlayClerkPerformanceServiceImplTest.java @@ -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 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> orderIdsCaptor = ArgumentCaptor.forClass(Collection.class); + ArgumentCaptor startCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor 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; }