feat: implement order relation type tracking
- Add OrderRelationType enum (UNASSIGNED, LEGACY, FIRST, CONTINUED, NEUTRAL) - Create play_clerk_customer_relation table to track first-completed history - Add order_relation_type column to play_order_info - Migrate existing orders to set relation types based on completion history - Update order services to determine relation type on creation - Update order VOs and controllers to expose relation type in API responses - Update clerk performance calculations to account for relation types - Update revenue calculations to distinguish between first and continued orders - Add comprehensive API and unit tests for order relation functionality
This commit is contained in:
@@ -1,24 +1,25 @@
|
||||
package com.starry.admin.common.task;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkWagesDetailsInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkWagesInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkWagesDetailsInfoService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkWagesInfoService;
|
||||
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.system.module.entity.SysTenantEntity;
|
||||
import com.starry.admin.modules.system.service.ISysTenantService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -128,14 +129,13 @@ public class ClerkWagesSettlementTask {
|
||||
finalAmount = finalAmount.add(orderInfo.getFinalAmount());
|
||||
orderNumber++;
|
||||
estimatedRevenue = estimatedRevenue.add(orderInfo.getEstimatedRevenue());
|
||||
if ("0".equals(orderInfo.getFirstOrder())) {
|
||||
orderContinueNumber++;
|
||||
orderContinueMoney = orderContinueMoney.add(orderInfo.getFinalAmount());
|
||||
}
|
||||
if ("1".equals(orderInfo.getOrdersExpiredState())) {
|
||||
ordersExpiredNumber++;
|
||||
}
|
||||
}
|
||||
ContinuationMetrics continuationMetrics = calculateContinuationMetrics(orderInfoEntities);
|
||||
orderContinueNumber = continuationMetrics.count;
|
||||
orderContinueMoney = continuationMetrics.money;
|
||||
|
||||
PlayClerkWagesInfoEntity wagesInfo = new PlayClerkWagesInfoEntity();
|
||||
wagesInfo.setId(wagesId);
|
||||
@@ -158,4 +158,51 @@ public class ClerkWagesSettlementTask {
|
||||
|
||||
playClerkWagesInfoService.saveOrUpdate(wagesInfo);
|
||||
}
|
||||
|
||||
private ContinuationMetrics calculateContinuationMetrics(List<PlayOrderInfoEntity> orders) {
|
||||
List<PlayOrderInfoEntity> completedOrders = orders.stream()
|
||||
.filter(order -> OrderConstant.OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus()))
|
||||
.collect(Collectors.toList());
|
||||
if (completedOrders.isEmpty()) {
|
||||
return new ContinuationMetrics(0, BigDecimal.ZERO);
|
||||
}
|
||||
int continuedCount = 0;
|
||||
BigDecimal continuedMoney = BigDecimal.ZERO;
|
||||
for (PlayOrderInfoEntity order : completedOrders) {
|
||||
String customerId = order.getPurchaserBy();
|
||||
if (StrUtil.isBlank(customerId)) {
|
||||
throw new CustomException("订单缺少顾客信息,无法统计续单");
|
||||
}
|
||||
OrderConstant.OrderRelationType relationType = normalizeRelationType(order);
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
continuedCount++;
|
||||
continuedMoney = continuedMoney.add(order.getFinalAmount());
|
||||
}
|
||||
}
|
||||
return new ContinuationMetrics(continuedCount, continuedMoney);
|
||||
}
|
||||
|
||||
private OrderConstant.OrderRelationType normalizeRelationType(PlayOrderInfoEntity order) {
|
||||
OrderConstant.OrderRelationType relationType = order.getOrderRelationType();
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(order.getPlaceType())) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.NEUTRAL) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
return relationType;
|
||||
}
|
||||
|
||||
private static final class ContinuationMetrics {
|
||||
private final int count;
|
||||
private final BigDecimal money;
|
||||
|
||||
private ContinuationMetrics(int count, BigDecimal money) {
|
||||
this.count = count;
|
||||
this.money = money;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.starry.admin.common.task;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkRankingInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkRankingInfoService;
|
||||
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.system.module.entity.SysTenantEntity;
|
||||
@@ -20,6 +23,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -134,14 +138,13 @@ public class OrderRankingSettlementTask {
|
||||
for (PlayOrderInfoEntity orderInfoEntity : orderInfoEntities) {
|
||||
customIds.add(orderInfoEntity.getAcceptBy());
|
||||
orderMoney = orderMoney.add(orderInfoEntity.getOrderMoney());
|
||||
if ("0".equals(orderInfoEntity.getFirstOrder())) {
|
||||
orderContinueNumber++;
|
||||
orderContinueMoney = orderContinueMoney.add(orderInfoEntity.getOrderMoney());
|
||||
}
|
||||
if ("1".equals(orderInfoEntity.getOrdersExpiredState())) {
|
||||
ordersExpiredNumber++;
|
||||
}
|
||||
}
|
||||
ContinuationMetrics continuationMetrics = calculateContinuationMetrics(orderInfoEntities);
|
||||
orderContinueNumber = continuationMetrics.count;
|
||||
orderContinueMoney = continuationMetrics.money;
|
||||
BigDecimal orderContinueProportion = orderNumber == 0 ? BigDecimal.ZERO : new BigDecimal(ordersExpiredNumber).divide(new BigDecimal(orderNumber), 4, RoundingMode.HALF_UP).add(new BigDecimal(100));
|
||||
BigDecimal averageUnitPrice = customIds.isEmpty() ? BigDecimal.ZERO : orderMoney.divide(new BigDecimal(customIds.size()), 4, RoundingMode.HALF_UP);
|
||||
PlayClerkRankingInfoEntity rankingInfo = new PlayClerkRankingInfoEntity();
|
||||
@@ -170,4 +173,51 @@ public class OrderRankingSettlementTask {
|
||||
return rankingInfo;
|
||||
}
|
||||
|
||||
private ContinuationMetrics calculateContinuationMetrics(List<PlayOrderInfoEntity> orders) {
|
||||
List<PlayOrderInfoEntity> completedOrders = orders.stream()
|
||||
.filter(order -> OrderConstant.OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus()))
|
||||
.collect(Collectors.toList());
|
||||
if (completedOrders.isEmpty()) {
|
||||
return new ContinuationMetrics(0, BigDecimal.ZERO);
|
||||
}
|
||||
int continuedCount = 0;
|
||||
BigDecimal continuedMoney = BigDecimal.ZERO;
|
||||
for (PlayOrderInfoEntity order : completedOrders) {
|
||||
String customerId = order.getPurchaserBy();
|
||||
if (StrUtil.isBlank(customerId)) {
|
||||
throw new CustomException("订单缺少顾客信息,无法统计续单");
|
||||
}
|
||||
OrderConstant.OrderRelationType relationType = normalizeRelationType(order);
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
continuedCount++;
|
||||
continuedMoney = continuedMoney.add(order.getOrderMoney());
|
||||
}
|
||||
}
|
||||
return new ContinuationMetrics(continuedCount, continuedMoney);
|
||||
}
|
||||
|
||||
private OrderConstant.OrderRelationType normalizeRelationType(PlayOrderInfoEntity order) {
|
||||
OrderConstant.OrderRelationType relationType = order.getOrderRelationType();
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(order.getPlaceType())) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.NEUTRAL) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
return relationType;
|
||||
}
|
||||
|
||||
private static final class ContinuationMetrics {
|
||||
private final int count;
|
||||
private final BigDecimal money;
|
||||
|
||||
private ContinuationMetrics(int count, BigDecimal money) {
|
||||
this.count = count;
|
||||
this.money = money;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public class BlindBoxDispatchService {
|
||||
.orderType(OrderConstant.OrderType.GIFT)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(OrderConstant.RewardType.GIFT)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.paymentSource(OrderConstant.PaymentSource.BLIND_BOX)
|
||||
.sourceRewardId(reward.getId())
|
||||
.paymentInfo(paymentInfo)
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.starry.admin.modules.custom.service.IPlayCustomFollowInfoService;
|
||||
import com.starry.admin.modules.media.entity.PlayMediaEntity;
|
||||
import com.starry.admin.modules.media.enums.MediaStatus;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
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.PlayPersonnelAdminInfoEntity;
|
||||
@@ -510,7 +511,17 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl<PlayClerkUserInfoM
|
||||
int orderContinueNumber = 0;
|
||||
int orderNumber = 0;
|
||||
for (PlayOrderInfoEntity orderInfo : playOrderInfoService.queryBySettlementOrder(record.getId(), "")) {
|
||||
if ("0".equals(orderInfo.getFirstOrder())) {
|
||||
OrderConstant.OrderRelationType relationType = orderInfo.getOrderRelationType();
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
|
||||
relationType = OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.NEUTRAL) {
|
||||
relationType = OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
orderContinueNumber++;
|
||||
}
|
||||
orderNumber++;
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.starry.admin.modules.custom.module.vo.PlayCustomUserQueryVo;
|
||||
import com.starry.admin.modules.custom.module.vo.PlayCustomUserReturnVo;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomLevelInfoService;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
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.impl.PlayOrderInfoServiceImpl;
|
||||
import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService;
|
||||
@@ -169,7 +170,17 @@ public class PlayCustomUserInfoServiceImpl extends ServiceImpl<PlayCustomUserInf
|
||||
if (orderInfo.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
if ("0".equals(orderInfo.getFirstOrder())) {
|
||||
OrderConstant.OrderRelationType relationType = orderInfo.getOrderRelationType();
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
|
||||
relationType = OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.NEUTRAL) {
|
||||
relationType = OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
orderContinueNumber++;
|
||||
}
|
||||
orderNumber++;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.starry.admin.modules.order.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.starry.admin.modules.order.module.entity.PlayClerkCustomerRelationEntity;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* 客户-店员关系Mapper接口
|
||||
*/
|
||||
public interface PlayClerkCustomerRelationMapper extends MPJBaseMapper<PlayClerkCustomerRelationEntity> {
|
||||
|
||||
@Select("SELECT * FROM play_clerk_customer_relation "
|
||||
+ "WHERE tenant_id = #{tenantId} AND purchaser_by = #{purchaserBy} AND accept_by = #{acceptBy} "
|
||||
+ "AND deleted = 0 FOR UPDATE")
|
||||
PlayClerkCustomerRelationEntity selectForUpdate(@Param("tenantId") String tenantId,
|
||||
@Param("purchaserBy") String purchaserBy,
|
||||
@Param("acceptBy") String acceptBy);
|
||||
|
||||
@Update("UPDATE play_clerk_customer_relation "
|
||||
+ "SET deleted = 0, has_completed = '0', first_completed_order_id = NULL, first_completed_time = NULL "
|
||||
+ "WHERE tenant_id = #{tenantId} AND purchaser_by = #{purchaserBy} AND accept_by = #{acceptBy}")
|
||||
int restoreRelation(@Param("tenantId") String tenantId,
|
||||
@Param("purchaserBy") String purchaserBy,
|
||||
@Param("acceptBy") String acceptBy);
|
||||
|
||||
@Delete("DELETE FROM play_clerk_customer_relation WHERE tenant_id = #{tenantId}")
|
||||
int hardDeleteByTenant(@Param("tenantId") String tenantId);
|
||||
}
|
||||
@@ -123,6 +123,37 @@ public class OrderConstant {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单关系类型
|
||||
*/
|
||||
@Getter
|
||||
public enum OrderRelationType {
|
||||
UNASSIGNED("UNASSIGNED", "未分配"),
|
||||
LEGACY("LEGACY", "历史存量"),
|
||||
FIRST("FIRST", "首单"),
|
||||
CONTINUED("CONTINUED", "续单"),
|
||||
NEUTRAL("NEUTRAL", "中性");
|
||||
|
||||
@com.baomidou.mybatisplus.annotation.EnumValue
|
||||
@com.fasterxml.jackson.annotation.JsonValue
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
OrderRelationType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static OrderRelationType fromCode(String code) {
|
||||
for (OrderRelationType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown order relation type code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 性别枚举
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,8 @@ public class OrderCreationContext {
|
||||
|
||||
private OrderConstant.RewardType rewardType;
|
||||
|
||||
private boolean isFirstOrder;
|
||||
@NotNull(message = "订单关系类型不能为空")
|
||||
private OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
private OrderConstant.PaymentSource paymentSource;
|
||||
|
||||
@@ -64,10 +65,6 @@ public class OrderCreationContext {
|
||||
@Nullable
|
||||
private String creatorId;
|
||||
|
||||
public String getFirstOrderString() {
|
||||
return isFirstOrder ? "1" : "0";
|
||||
}
|
||||
|
||||
public boolean isValidForRandomOrder() {
|
||||
return placeType == OrderConstant.PlaceType.RANDOM && randomOrderRequirements != null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.starry.admin.modules.order.module.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.starry.common.domain.BaseEntity;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 客户-店员关系(完成历史快照)
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("play_clerk_customer_relation")
|
||||
public class PlayClerkCustomerRelationEntity extends BaseEntity<PlayClerkCustomerRelationEntity> {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 顾客ID
|
||||
*/
|
||||
@TableField("purchaser_by")
|
||||
private String purchaserBy;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@TableField("tenant_id")
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 店员ID
|
||||
*/
|
||||
@TableField("accept_by")
|
||||
private String acceptBy;
|
||||
|
||||
/**
|
||||
* 是否有已完成历史
|
||||
*/
|
||||
@TableField("has_completed")
|
||||
private String hasCompleted;
|
||||
|
||||
/**
|
||||
* 首次完成订单ID
|
||||
*/
|
||||
@TableField("first_completed_order_id")
|
||||
private String firstCompletedOrderId;
|
||||
|
||||
/**
|
||||
* 首次完成时间
|
||||
*/
|
||||
@TableField("first_completed_time")
|
||||
private LocalDateTime firstCompletedTime;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.starry.admin.common.conf.StringTypeHandler;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||
import com.starry.admin.modules.order.service.impl.OrderLifecycleServiceImpl;
|
||||
import com.starry.common.domain.BaseEntity;
|
||||
@@ -64,8 +65,15 @@ public class PlayOrderInfoEntity extends BaseEntity<PlayOrderInfoEntity> {
|
||||
/**
|
||||
* 是否是首单【0:不是,1:是】
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String firstOrder;
|
||||
|
||||
/**
|
||||
* 订单关系类型(UNASSIGNED/FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
@TableField("order_relation_type")
|
||||
private OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 退款类型【0:未退款,1:已退款】
|
||||
*/
|
||||
@@ -368,4 +376,14 @@ public class PlayOrderInfoEntity extends BaseEntity<PlayOrderInfoEntity> {
|
||||
}
|
||||
this.orderEndTime = orderEndTime;
|
||||
}
|
||||
|
||||
public String getFirstOrder() {
|
||||
if (orderRelationType == null) {
|
||||
throw new IllegalStateException("订单关系类型不能为空");
|
||||
}
|
||||
if (orderRelationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
return OrderConstant.YesNoFlag.NO.getCode();
|
||||
}
|
||||
return OrderConstant.YesNoFlag.YES.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,11 @@ public class PlayOrderDetailsReturnVo {
|
||||
*/
|
||||
private String placeType;
|
||||
|
||||
/**
|
||||
* 订单关系类型(FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 要求店员性别(0:未知;1:男;2:女)- 仅随机单有效
|
||||
*/
|
||||
|
||||
@@ -140,6 +140,11 @@ public class PlayOrderInfoReturnVo {
|
||||
*/
|
||||
private String firstOrder;
|
||||
|
||||
/**
|
||||
* 订单关系类型(UNASSIGNED/FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 订单最终金额(支付金额)
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.starry.admin.modules.order.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.dto.*;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.module.vo.*;
|
||||
@@ -51,39 +52,40 @@ public interface IPlayOrderInfoService extends IService<PlayOrderInfoEntity> {
|
||||
* @param clerkId 店员ID
|
||||
* @param croupIds 优惠券ID列表
|
||||
* @param placeType 下单类型(-1:其他类型;0:指定单;1:随机单;2:打赏单)
|
||||
* @param firstOrder 是否是首单【0:不是,1:是】
|
||||
* @param relationType 订单关系类型
|
||||
* @param orderAmount 订单金额
|
||||
* @return com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo
|
||||
* @author admin
|
||||
* @since 2024/7/18 16:39
|
||||
**/
|
||||
ClerkEstimatedRevenueVo getClerkEstimatedRevenue(String clerkId, List<String> croupIds, String placeType,
|
||||
String firstOrder, BigDecimal orderAmount);
|
||||
OrderConstant.OrderRelationType relationType, BigDecimal orderAmount);
|
||||
|
||||
/**
|
||||
* 根据店员等级和订单金额,获取店员预计收入
|
||||
*
|
||||
* @param clerkId 店员ID
|
||||
* @param placeType 下单类型(-1:其他类型;0:指定单;1:随机单;2:打赏单)
|
||||
* @param firstOrder 是否是首单【0:不是,1:是】
|
||||
* @param relationType 订单关系类型
|
||||
* @param orderAmount 订单金额
|
||||
* @return math.BigDecimal 店员预计收入
|
||||
* @author admin
|
||||
* @since 2024/6/3 11:12
|
||||
**/
|
||||
BigDecimal getEstimatedRevenue(String clerkId, String placeType, String firstOrder, BigDecimal orderAmount);
|
||||
BigDecimal getEstimatedRevenue(String clerkId, String placeType, OrderConstant.OrderRelationType relationType,
|
||||
BigDecimal orderAmount);
|
||||
|
||||
/**
|
||||
* 根据店员等级,获取店员提成比例
|
||||
*
|
||||
* @param clerkId 店员ID
|
||||
* @param placeType 下单类型(-1:其他类型;0:指定单;1:随机单;2:打赏单)
|
||||
* @param firstOrder 是否是首单【0:不是,1:是】
|
||||
* @param relationType 订单关系类型
|
||||
* @return math.BigDecimal 店员预计收入
|
||||
* @author admin
|
||||
* @since 2024/6/3 11:12
|
||||
**/
|
||||
Integer getEstimatedRevenueRatio(String clerkId, String placeType, String firstOrder);
|
||||
Integer getEstimatedRevenueRatio(String clerkId, String placeType, OrderConstant.OrderRelationType relationType);
|
||||
|
||||
/**
|
||||
* 根据订单结算状态查询订单
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.mapper.PlayClerkCustomerRelationMapper;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderLogInfoMapper;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
@@ -37,6 +38,7 @@ import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderRevocationContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
import com.starry.admin.modules.order.module.entity.PlayClerkCustomerRelationEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderLogInfoEntity;
|
||||
import com.starry.admin.modules.order.module.event.OrderRevocationEvent;
|
||||
@@ -68,6 +70,7 @@ import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -112,6 +115,9 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
@Resource
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
@Resource
|
||||
private PlayClerkCustomerRelationMapper clerkCustomerRelationMapper;
|
||||
|
||||
@Resource
|
||||
private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService;
|
||||
|
||||
@@ -463,6 +469,9 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
if (latest == null) {
|
||||
throw new CustomException("订单不存在");
|
||||
}
|
||||
if (OrderStatus.COMPLETED.getCode().equals(latest.getOrderStatus())) {
|
||||
updateRelationOnCompletion(latest);
|
||||
}
|
||||
|
||||
boolean forceNotify = context != null && context.isForceNotify();
|
||||
boolean earningsCreated = false;
|
||||
@@ -765,7 +774,16 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
entity.setOrderType(context.getOrderType().getCode());
|
||||
entity.setPlaceType(context.getPlaceType().getCode());
|
||||
entity.setRewardType(context.getRewardType().getCode());
|
||||
entity.setFirstOrder(resolveFirstOrderFlag(context));
|
||||
OrderConstant.OrderRelationType relationType = context.getOrderRelationType();
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (context.getPlaceType() == OrderConstant.PlaceType.RANDOM
|
||||
&& relationType != OrderConstant.OrderRelationType.FIRST) {
|
||||
throw new CustomException("随机单必须为首单关系");
|
||||
}
|
||||
entity.setOrderRelationType(relationType);
|
||||
// entity.setFirstOrder(resolveFirstOrderFlag(context));
|
||||
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
entity.setBackendEntry(YesNoFlag.NO.getCode());
|
||||
entity.setOrderSettlementState(OrderSettlementState.NOT_SETTLED.getCode());
|
||||
@@ -812,11 +830,17 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
return;
|
||||
}
|
||||
entity.setAcceptBy(context.getAcceptBy());
|
||||
OrderConstant.OrderRelationType relationType = resolveOrderRelationType(
|
||||
context.getPurchaserBy(),
|
||||
context.getAcceptBy(),
|
||||
context.getOrderId(),
|
||||
context.getPlaceType());
|
||||
entity.setOrderRelationType(relationType);
|
||||
ClerkEstimatedRevenueVo estimatedRevenue = clerkRevenueCalculator.calculateEstimatedRevenue(
|
||||
context.getAcceptBy(),
|
||||
context.getPaymentInfo().getCouponIds(),
|
||||
context.getPlaceType().getCode(),
|
||||
entity.getFirstOrder(),
|
||||
relationType,
|
||||
context.getPaymentInfo().getOrderMoney());
|
||||
entity.setEstimatedRevenue(estimatedRevenue.getRevenueAmount());
|
||||
entity.setEstimatedRevenueRatio(estimatedRevenue.getRevenueRatio());
|
||||
@@ -855,21 +879,80 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveFirstOrderFlag(OrderCreationContext context) {
|
||||
if (StrUtil.isBlank(context.getAcceptBy()) || StrUtil.isBlank(context.getPurchaserBy())) {
|
||||
return context.getFirstOrderString();
|
||||
private OrderConstant.OrderRelationType resolveOrderRelationType(String customerId, String clerkId, String orderId,
|
||||
OrderConstant.PlaceType placeType) {
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
throw new CustomException("订单关系计算缺少订单ID");
|
||||
}
|
||||
return isFirstOrder(context.getPurchaserBy(), context.getAcceptBy())
|
||||
? YesNoFlag.YES.getCode()
|
||||
: YesNoFlag.NO.getCode();
|
||||
if (placeType == OrderConstant.PlaceType.RANDOM) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (StrUtil.isBlank(customerId) || StrUtil.isBlank(clerkId)) {
|
||||
return OrderConstant.OrderRelationType.UNASSIGNED;
|
||||
}
|
||||
PlayClerkCustomerRelationEntity relation = ensureRelationForUpdate(customerId, clerkId);
|
||||
if (relation == null) {
|
||||
throw new CustomException("订单关系初始化失败");
|
||||
}
|
||||
if (StrUtil.isBlank(relation.getHasCompleted())) {
|
||||
throw new CustomException("订单关系缺少完成标记");
|
||||
}
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(relation.getHasCompleted())) {
|
||||
return OrderConstant.OrderRelationType.CONTINUED;
|
||||
}
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
|
||||
private boolean isFirstOrder(String customerId, String clerkId) {
|
||||
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, customerId)
|
||||
.eq(PlayOrderInfoEntity::getAcceptBy, clerkId)
|
||||
.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||
return orderInfoMapper.selectCount(wrapper) == 0;
|
||||
private PlayClerkCustomerRelationEntity ensureRelationForUpdate(String customerId, String clerkId) {
|
||||
String tenantId = SecurityUtils.getTenantId();
|
||||
if (StrUtil.isBlank(tenantId)) {
|
||||
throw new CustomException("租户信息不能为空");
|
||||
}
|
||||
PlayClerkCustomerRelationEntity relation = clerkCustomerRelationMapper.selectForUpdate(tenantId, customerId, clerkId);
|
||||
if (relation != null) {
|
||||
return relation;
|
||||
}
|
||||
PlayClerkCustomerRelationEntity toCreate = new PlayClerkCustomerRelationEntity();
|
||||
toCreate.setId(IdUtils.getUuid());
|
||||
toCreate.setPurchaserBy(customerId);
|
||||
toCreate.setTenantId(tenantId);
|
||||
toCreate.setAcceptBy(clerkId);
|
||||
toCreate.setHasCompleted(OrderConstant.YesNoFlag.NO.getCode());
|
||||
toCreate.setDeleted(Boolean.FALSE);
|
||||
try {
|
||||
clerkCustomerRelationMapper.insert(toCreate);
|
||||
} catch (DuplicateKeyException ex) {
|
||||
log.debug("Relation already exists for customer {} and clerk {}", customerId, clerkId);
|
||||
clerkCustomerRelationMapper.restoreRelation(tenantId, customerId, clerkId);
|
||||
}
|
||||
return clerkCustomerRelationMapper.selectForUpdate(tenantId, customerId, clerkId);
|
||||
}
|
||||
|
||||
private void updateRelationOnCompletion(PlayOrderInfoEntity order) {
|
||||
if (order == null) {
|
||||
throw new CustomException("订单信息不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(order.getPurchaserBy()) || StrUtil.isBlank(order.getAcceptBy())) {
|
||||
return;
|
||||
}
|
||||
PlayClerkCustomerRelationEntity relation = ensureRelationForUpdate(order.getPurchaserBy(), order.getAcceptBy());
|
||||
if (relation == null) {
|
||||
throw new CustomException("订单关系初始化失败");
|
||||
}
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(relation.getHasCompleted())
|
||||
&& relation.getFirstCompletedTime() != null
|
||||
&& StrUtil.isNotBlank(relation.getFirstCompletedOrderId())) {
|
||||
return;
|
||||
}
|
||||
relation.setHasCompleted(OrderConstant.YesNoFlag.YES.getCode());
|
||||
if (relation.getFirstCompletedTime() == null) {
|
||||
LocalDateTime completionTime = order.getOrderEndTime() != null ? order.getOrderEndTime() : LocalDateTime.now();
|
||||
relation.setFirstCompletedTime(completionTime);
|
||||
}
|
||||
if (StrUtil.isBlank(relation.getFirstCompletedOrderId())) {
|
||||
relation.setFirstCompletedOrderId(order.getId());
|
||||
}
|
||||
clerkCustomerRelationMapper.updateById(relation);
|
||||
}
|
||||
|
||||
private boolean ensureEarnings(PlayOrderInfoEntity order, OrderTriggerSource source) {
|
||||
|
||||
@@ -75,9 +75,9 @@ public class PlayOrderContinueInfoServiceImpl
|
||||
entity.setReviewedTime(LocalDateTime.now());
|
||||
this.baseMapper.updateById(entity);
|
||||
// 添加订单信息
|
||||
if ("1".equals(vo.getReviewState())) {
|
||||
|
||||
}
|
||||
// if ("1".equals(vo.getReviewState())) {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomLevelInfoEntity;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.mapper.PlayClerkCustomerRelationMapper;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
|
||||
@@ -30,6 +31,7 @@ import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||
import com.starry.admin.modules.order.module.dto.*;
|
||||
import com.starry.admin.modules.order.module.entity.PlayClerkCustomerRelationEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderEvaluateInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
@@ -63,6 +65,7 @@ import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -79,6 +82,9 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
IPlayOrderInfoService {
|
||||
@Resource
|
||||
private PlayOrderInfoMapper orderInfoMapper;
|
||||
|
||||
@Resource
|
||||
private PlayClerkCustomerRelationMapper clerkCustomerRelationMapper;
|
||||
@Resource
|
||||
private IPlayClerkUserInfoService playClerkUserInfoService;
|
||||
@Resource
|
||||
@@ -119,8 +125,9 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
|
||||
@Override
|
||||
public ClerkEstimatedRevenueVo getClerkEstimatedRevenue(String clerkId, List<String> croupIds, String placeType,
|
||||
String firstOrder, BigDecimal orderAmount) {
|
||||
return clerkRevenueCalculator.calculateEstimatedRevenue(clerkId, croupIds, placeType, firstOrder, orderAmount);
|
||||
OrderConstant.OrderRelationType relationType,
|
||||
BigDecimal orderAmount) {
|
||||
return clerkRevenueCalculator.calculateEstimatedRevenue(clerkId, croupIds, placeType, relationType, orderAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,22 +142,22 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
* @since 2024/6/3 11:12
|
||||
**/
|
||||
@Override
|
||||
public BigDecimal getEstimatedRevenue(String clerkId, String placeType, String firstOrder, BigDecimal orderAmount) {
|
||||
public BigDecimal getEstimatedRevenue(String clerkId, String placeType,
|
||||
OrderConstant.OrderRelationType relationType, BigDecimal orderAmount) {
|
||||
PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
boolean isFirst = OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder);
|
||||
boolean continued = ensureRelationTypeForRevenue(relationType);
|
||||
BigDecimal safeOrderAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount;
|
||||
try {
|
||||
OrderConstant.PlaceType place = OrderConstant.PlaceType.fromCode(placeType);
|
||||
switch (place) {
|
||||
case SPECIFIED:
|
||||
return calculateRevenue(safeOrderAmount,
|
||||
isFirst ? entity.getFirstRegularRatio() : entity.getNotFirstRegularRatio());
|
||||
continued ? entity.getNotFirstRegularRatio() : entity.getFirstRegularRatio());
|
||||
case RANDOM:
|
||||
return calculateRevenue(safeOrderAmount,
|
||||
isFirst ? entity.getFirstRandomRadio() : entity.getNotFirstRandomRadio());
|
||||
return calculateRevenue(safeOrderAmount, entity.getFirstRandomRadio());
|
||||
case REWARD:
|
||||
return calculateRevenue(safeOrderAmount,
|
||||
isFirst ? entity.getFirstRewardRatio() : entity.getNotFirstRewardRatio());
|
||||
continued ? entity.getNotFirstRewardRatio() : entity.getFirstRewardRatio());
|
||||
case OTHER:
|
||||
default:
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
@@ -163,18 +170,19 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getEstimatedRevenueRatio(String clerkId, String placeType, String firstOrder) {
|
||||
public Integer getEstimatedRevenueRatio(String clerkId, String placeType,
|
||||
OrderConstant.OrderRelationType relationType) {
|
||||
PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
boolean isFirst = OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder);
|
||||
boolean continued = ensureRelationTypeForRevenue(relationType);
|
||||
try {
|
||||
OrderConstant.PlaceType place = OrderConstant.PlaceType.fromCode(placeType);
|
||||
switch (place) {
|
||||
case SPECIFIED:
|
||||
return isFirst ? entity.getFirstRegularRatio() : entity.getNotFirstRegularRatio();
|
||||
return continued ? entity.getNotFirstRegularRatio() : entity.getFirstRegularRatio();
|
||||
case RANDOM:
|
||||
return isFirst ? entity.getFirstRandomRadio() : entity.getNotFirstRandomRadio();
|
||||
return entity.getFirstRandomRadio();
|
||||
case REWARD:
|
||||
return isFirst ? entity.getFirstRewardRatio() : entity.getNotFirstRewardRatio();
|
||||
return continued ? entity.getNotFirstRewardRatio() : entity.getFirstRewardRatio();
|
||||
case OTHER:
|
||||
default:
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
@@ -191,6 +199,19 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private boolean ensureRelationTypeForRevenue(OrderConstant.OrderRelationType relationType) {
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.UNASSIGNED) {
|
||||
throw new CustomException("未分配订单不可计算预计收益");
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.LEGACY) {
|
||||
return false;
|
||||
}
|
||||
return relationType == OrderConstant.OrderRelationType.CONTINUED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增充值订单
|
||||
*
|
||||
@@ -338,11 +359,94 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
|
||||
@Override
|
||||
public Boolean checkFirstOrderFlag(String customId, String clerkId) {
|
||||
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, customId)
|
||||
.eq(PlayOrderInfoEntity::getAcceptBy, clerkId)
|
||||
.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||
return this.baseMapper.selectCount(wrapper) == 0;
|
||||
if (StringUtils.isBlank(customId) || StringUtils.isBlank(clerkId)) {
|
||||
throw new CustomException("用户或店员信息不能为空");
|
||||
}
|
||||
PlayClerkCustomerRelationEntity relation = clerkCustomerRelationMapper.selectOne(
|
||||
Wrappers.lambdaQuery(PlayClerkCustomerRelationEntity.class)
|
||||
.eq(PlayClerkCustomerRelationEntity::getPurchaserBy, customId)
|
||||
.eq(PlayClerkCustomerRelationEntity::getAcceptBy, clerkId)
|
||||
.eq(PlayClerkCustomerRelationEntity::getDeleted, Boolean.FALSE));
|
||||
if (relation == null) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if (StringUtils.isBlank(relation.getHasCompleted())) {
|
||||
throw new CustomException("订单关系缺少完成标记");
|
||||
}
|
||||
return !OrderConstant.YesNoFlag.YES.getCode().equals(relation.getHasCompleted());
|
||||
}
|
||||
|
||||
private OrderConstant.OrderRelationType resolveOrderRelationType(String customId, String clerkId, String orderId,
|
||||
String placeType) {
|
||||
if (StringUtils.isBlank(orderId)) {
|
||||
throw new CustomException("订单关系计算缺少订单ID");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(placeType)) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (StringUtils.isBlank(customId) || StringUtils.isBlank(clerkId)) {
|
||||
return OrderConstant.OrderRelationType.UNASSIGNED;
|
||||
}
|
||||
PlayClerkCustomerRelationEntity relation = ensureRelationForUpdate(customId, clerkId);
|
||||
if (relation == null) {
|
||||
throw new CustomException("订单关系初始化失败");
|
||||
}
|
||||
if (StringUtils.isBlank(relation.getHasCompleted())) {
|
||||
throw new CustomException("订单关系缺少完成标记");
|
||||
}
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(relation.getHasCompleted())) {
|
||||
return OrderConstant.OrderRelationType.CONTINUED;
|
||||
}
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
|
||||
private PlayClerkCustomerRelationEntity ensureRelationForUpdate(String customerId, String clerkId) {
|
||||
String tenantId = SecurityUtils.getTenantId();
|
||||
if (StringUtils.isBlank(tenantId)) {
|
||||
throw new CustomException("租户信息不能为空");
|
||||
}
|
||||
PlayClerkCustomerRelationEntity relation = clerkCustomerRelationMapper.selectForUpdate(tenantId, customerId, clerkId);
|
||||
if (relation != null) {
|
||||
return relation;
|
||||
}
|
||||
PlayClerkCustomerRelationEntity toCreate = new PlayClerkCustomerRelationEntity();
|
||||
toCreate.setId(IdUtils.getUuid());
|
||||
toCreate.setPurchaserBy(customerId);
|
||||
toCreate.setTenantId(tenantId);
|
||||
toCreate.setAcceptBy(clerkId);
|
||||
toCreate.setHasCompleted(OrderConstant.YesNoFlag.NO.getCode());
|
||||
toCreate.setDeleted(Boolean.FALSE);
|
||||
try {
|
||||
clerkCustomerRelationMapper.insert(toCreate);
|
||||
} catch (DuplicateKeyException ex) {
|
||||
log.debug("Relation already exists for customer {} and clerk {}", customerId, clerkId);
|
||||
clerkCustomerRelationMapper.restoreRelation(tenantId, customerId, clerkId);
|
||||
}
|
||||
return clerkCustomerRelationMapper.selectForUpdate(tenantId, customerId, clerkId);
|
||||
}
|
||||
|
||||
private String mapFirstOrder(OrderConstant.OrderRelationType relationType) {
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
return OrderConstant.YesNoFlag.NO.getCode();
|
||||
}
|
||||
return OrderConstant.YesNoFlag.YES.getCode();
|
||||
}
|
||||
|
||||
private OrderConstant.OrderRelationType normalizeRelationType(OrderConstant.OrderRelationType relationType,
|
||||
String placeType) {
|
||||
if (relationType == null) {
|
||||
throw new CustomException("订单关系类型不能为空");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(placeType)) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.NEUTRAL) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
return relationType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,6 +484,12 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getId, orderId);
|
||||
lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
|
||||
PlayOrderDetailsReturnVo vo = this.baseMapper.selectJoinOne(PlayOrderDetailsReturnVo.class, lambdaQueryWrapper);
|
||||
if (vo != null) {
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
normalizeRelationType(vo.getOrderRelationType(), vo.getPlaceType());
|
||||
vo.setOrderRelationType(relationType);
|
||||
vo.setFirstOrder(mapFirstOrder(relationType));
|
||||
}
|
||||
|
||||
// Privacy protection: Hide customer info for pending random orders
|
||||
if (vo != null && OrderConstant.PlaceType.RANDOM.getCode().equals(vo.getPlaceType()) && OrderStatus.PENDING.getCode().equals(vo.getOrderStatus())) {
|
||||
@@ -434,7 +544,14 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getBackendEntry, vo.getBackendEntry());
|
||||
}
|
||||
if (StringUtils.isNotBlank(vo.getFirstOrder())) {
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getFirstOrder, vo.getFirstOrder());
|
||||
String firstOrder = vo.getFirstOrder();
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder)) {
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderRelationType, OrderConstant.OrderRelationType.FIRST);
|
||||
} else if (OrderConstant.YesNoFlag.NO.getCode().equals(firstOrder)) {
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderRelationType, OrderConstant.OrderRelationType.CONTINUED);
|
||||
} else {
|
||||
throw new CustomException("首单筛选条件不合法");
|
||||
}
|
||||
}
|
||||
applyRangeFilter(lambdaQueryWrapper, vo.getPurchaserTime(), PlayOrderInfoEntity::getPurchaserTime);
|
||||
applyRangeFilter(lambdaQueryWrapper, vo.getAcceptTime(), PlayOrderInfoEntity::getAcceptTime);
|
||||
@@ -445,8 +562,17 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
vo.getClerkNickName());
|
||||
lambdaQueryWrapper.and(
|
||||
i -> i.isNull(PlayOrderInfoEntity::getAcceptBy).or().in(PlayOrderInfoEntity::getAcceptBy, clerkIdList));
|
||||
return this.baseMapper.selectJoinPage(new Page<>(vo.getPageNum(), vo.getPageSize()),
|
||||
PlayOrderInfoReturnVo.class, lambdaQueryWrapper);
|
||||
IPage<PlayOrderInfoReturnVo> page = this.baseMapper.selectJoinPage(
|
||||
new Page<>(vo.getPageNum(), vo.getPageSize()),
|
||||
PlayOrderInfoReturnVo.class,
|
||||
lambdaQueryWrapper);
|
||||
for (PlayOrderInfoReturnVo record : page.getRecords()) {
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
normalizeRelationType(record.getOrderRelationType(), record.getPlaceType());
|
||||
record.setOrderRelationType(relationType);
|
||||
record.setFirstOrder(mapFirstOrder(relationType));
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -464,6 +590,10 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
if (returnVo == null) {
|
||||
throw new CustomException("订单不存在或已失效");
|
||||
}
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
normalizeRelationType(returnVo.getOrderRelationType(), returnVo.getPlaceType());
|
||||
returnVo.setOrderRelationType(relationType);
|
||||
returnVo.setFirstOrder(mapFirstOrder(relationType));
|
||||
// 如果订单状态为退款,查询订单退款原因
|
||||
if (OrderStatus.CANCELLED.getCode().equals(returnVo.getOrderStatus())) {
|
||||
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
||||
@@ -540,6 +670,10 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
lambdaQueryWrapper);
|
||||
|
||||
for (PlayClerkOrderListReturnVo record : page.getRecords()) {
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
normalizeRelationType(record.getOrderRelationType(), record.getPlaceType());
|
||||
record.setOrderRelationType(relationType);
|
||||
record.setFirstOrder(mapFirstOrder(relationType));
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(record.getPlaceType())
|
||||
&& OrderStatus.PENDING.getCode().equals(record.getOrderStatus())) {
|
||||
record.setCustomNickname("匿名用户");
|
||||
@@ -559,6 +693,12 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = getCommonOrderQueryVo(entity, null);
|
||||
PlayCustomOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayCustomOrderDetailsReturnVo.class,
|
||||
lambdaQueryWrapper);
|
||||
if (returnVo != null) {
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
normalizeRelationType(returnVo.getOrderRelationType(), returnVo.getPlaceType());
|
||||
returnVo.setOrderRelationType(relationType);
|
||||
returnVo.setFirstOrder(mapFirstOrder(relationType));
|
||||
}
|
||||
// 如果订单状态为退款,查询订单退款原因
|
||||
if (returnVo.getOrderStatus().equals(OrderStatus.CANCELLED.getCode())) {
|
||||
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
||||
@@ -598,6 +738,10 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
PlayOrderEvaluateInfoEntity::getOrderId));
|
||||
for (PlayCustomOrderListReturnVo record : page.getRecords()) {
|
||||
record.setEvaluate(evaluateInfos.containsKey(record.getId()) ? "1" : "0");
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
normalizeRelationType(record.getOrderRelationType(), record.getPlaceType());
|
||||
record.setOrderRelationType(relationType);
|
||||
record.setFirstOrder(mapFirstOrder(relationType));
|
||||
}
|
||||
// 获取当前顾客所有订单投诉信息,将订单评价信息转化为 map<订单ID,订单ID>的结构
|
||||
PlayOrderComplaintInfoEntity orderComplaintInfoEntity = new PlayOrderComplaintInfoEntity();
|
||||
@@ -627,6 +771,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
* @param orderId 订单Id
|
||||
**/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStateTo1(String operatorByType, String operatorBy, String acceptBy, String orderId) {
|
||||
boolean isClerkOperator = OrderConstant.OperatorType.CLERK.getCode().equals(operatorByType);
|
||||
boolean isAdminOperator = OrderConstant.OperatorType.ADMIN.getCode().equals(operatorByType);
|
||||
@@ -658,18 +803,19 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
if (isClerkOperator && OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
|
||||
validateClerkQualificationForRandomOrder(orderInfo, clerkUserInfoEntity, acceptBy);
|
||||
}
|
||||
String firstOrderFlag = resolveFirstOrderFlag(orderInfo.getPurchaserBy(), acceptBy);
|
||||
OrderConstant.OrderRelationType relationType =
|
||||
resolveOrderRelationType(orderInfo.getPurchaserBy(), acceptBy, orderId, orderInfo.getPlaceType());
|
||||
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
|
||||
LocalDateTime acceptTime = LocalDateTime.now();
|
||||
entity.setAcceptBy(acceptBy);
|
||||
entity.setAcceptTime(acceptTime);
|
||||
entity.setFirstOrder(firstOrderFlag);
|
||||
entity.setOrderRelationType(relationType);
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(
|
||||
acceptBy,
|
||||
orderInfo.getCouponIds(),
|
||||
orderInfo.getPlaceType(),
|
||||
firstOrderFlag,
|
||||
relationType,
|
||||
orderInfo.getOrderMoney());
|
||||
BigDecimal revenueAmount = estimatedRevenueVo.getRevenueAmount();
|
||||
entity.setEstimatedRevenue(revenueAmount);
|
||||
@@ -703,7 +849,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
orderInfo.setOrderStatus(OrderStatus.ACCEPTED.getCode());
|
||||
orderInfo.setEstimatedRevenue(revenueAmount);
|
||||
orderInfo.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
|
||||
orderInfo.setFirstOrder(firstOrderFlag);
|
||||
orderInfo.setOrderRelationType(relationType);
|
||||
log.info("Order accepted successfully. orderId={}, orderNo={}, acceptBy={}, operatorByType={}",
|
||||
orderId, orderInfo.getOrderNo(), acceptBy, operatorByType);
|
||||
notificationSender.sendOrderMessageAsync(orderInfo);
|
||||
@@ -729,18 +875,18 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveFirstOrderFlag(String purchaserId, String clerkId) {
|
||||
if (StrUtil.isBlank(purchaserId) || StrUtil.isBlank(clerkId)) {
|
||||
return OrderConstant.YesNoFlag.NO.getCode();
|
||||
}
|
||||
Long completedCount = orderInfoMapper.selectCount(Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, purchaserId)
|
||||
.eq(PlayOrderInfoEntity::getAcceptBy, clerkId));
|
||||
|
||||
return (completedCount == null || completedCount == 0)
|
||||
? OrderConstant.YesNoFlag.YES.getCode()
|
||||
: OrderConstant.YesNoFlag.NO.getCode();
|
||||
}
|
||||
// private String resolveFirstOrderFlag(String purchaserId, String clerkId) {
|
||||
// if (StrUtil.isBlank(purchaserId) || StrUtil.isBlank(clerkId)) {
|
||||
// return OrderConstant.YesNoFlag.NO.getCode();
|
||||
// }
|
||||
// Long completedCount = orderInfoMapper.selectCount(Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
// .eq(PlayOrderInfoEntity::getPurchaserBy, purchaserId)
|
||||
// .eq(PlayOrderInfoEntity::getAcceptBy, clerkId));
|
||||
//
|
||||
// return (completedCount == null || completedCount == 0)
|
||||
// ? OrderConstant.YesNoFlag.YES.getCode()
|
||||
// : OrderConstant.YesNoFlag.NO.getCode();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取通用的订单查询对象 订单作为主表 连接顾客用户表、店员用户表、商品表
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ClerkRevenueCalculator {
|
||||
String clerkId,
|
||||
List<String> couponIds,
|
||||
String placeType,
|
||||
String firstOrder,
|
||||
OrderConstant.OrderRelationType relationType,
|
||||
BigDecimal orderAmount) {
|
||||
PlayClerkLevelInfoEntity levelInfo = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
BigDecimal baseAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount;
|
||||
@@ -56,13 +56,13 @@ public class ClerkRevenueCalculator {
|
||||
|
||||
switch (placeTypeEnum) {
|
||||
case SPECIFIED: // 指定单
|
||||
fillRegularOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
fillRegularOrderRevenue(clerkId, relationType, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case RANDOM: // 随机单
|
||||
fillRandomOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
fillRandomOrderRevenue(clerkId, relationType, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case REWARD: // 打赏单
|
||||
fillRewardOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
fillRewardOrderRevenue(clerkId, relationType, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case OTHER:
|
||||
default:
|
||||
@@ -78,9 +78,11 @@ public class ClerkRevenueCalculator {
|
||||
return estimatedRevenueVo;
|
||||
}
|
||||
|
||||
private void fillRegularOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||
private void fillRegularOrderRevenue(String clerkId, OrderConstant.OrderRelationType relationType,
|
||||
BigDecimal orderAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
boolean continued = validateRelationType(relationType);
|
||||
if (!continued) {
|
||||
int ratio = safeRatio(levelInfo.getFirstRegularRatio(), "firstRegularRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
@@ -91,22 +93,19 @@ public class ClerkRevenueCalculator {
|
||||
}
|
||||
}
|
||||
|
||||
private void fillRandomOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||
private void fillRandomOrderRevenue(String clerkId, OrderConstant.OrderRelationType relationType,
|
||||
BigDecimal orderAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
int ratio = safeRatio(levelInfo.getFirstRandomRadio(), "firstRandomRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
} else {
|
||||
int ratio = safeRatio(levelInfo.getNotFirstRandomRadio(), "notFirstRandomRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
}
|
||||
int ratio = safeRatio(levelInfo.getFirstRandomRadio(), "firstRandomRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
}
|
||||
|
||||
private void fillRewardOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||
private void fillRewardOrderRevenue(String clerkId, OrderConstant.OrderRelationType relationType,
|
||||
BigDecimal orderAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
boolean continued = validateRelationType(relationType);
|
||||
if (!continued) {
|
||||
int ratio = safeRatio(levelInfo.getFirstRewardRatio(), "firstRewardRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
@@ -125,6 +124,19 @@ public class ClerkRevenueCalculator {
|
||||
return ratio;
|
||||
}
|
||||
|
||||
private boolean validateRelationType(OrderConstant.OrderRelationType relationType) {
|
||||
if (relationType == null) {
|
||||
throw new IllegalArgumentException("订单关系类型不能为空");
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.UNASSIGNED) {
|
||||
throw new IllegalArgumentException("未分配订单不可计算预计收益");
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.LEGACY) {
|
||||
return false;
|
||||
}
|
||||
return relationType == OrderConstant.OrderRelationType.CONTINUED;
|
||||
}
|
||||
|
||||
private BigDecimal scaleAmount(BigDecimal baseAmount, int ratio) {
|
||||
return baseAmount
|
||||
.multiply(BigDecimal.valueOf(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
|
||||
@@ -226,12 +226,12 @@ public class PlayClerkPerformanceController {
|
||||
customIds.add(orderInfoEntity.getPurchaserBy());
|
||||
finalAmount = finalAmount.add(orderInfoEntity.getFinalAmount());
|
||||
orderMoney = orderMoney.add(orderInfoEntity.getOrderMoney());
|
||||
if ("1".equals(orderInfoEntity.getFirstOrder())) {
|
||||
orderFirstAmount = orderFirstAmount.add(orderInfoEntity.getFinalAmount());
|
||||
} else {
|
||||
orderContinueNumber++;
|
||||
orderTotalAmount = orderTotalAmount.add(orderInfoEntity.getFinalAmount());
|
||||
}
|
||||
// if ("1".equals(orderInfoEntity.getFirstOrder())) {
|
||||
// orderFirstAmount = orderFirstAmount.add(orderInfoEntity.getFinalAmount());
|
||||
// } else {
|
||||
// orderContinueNumber++;
|
||||
// orderTotalAmount = orderTotalAmount.add(orderInfoEntity.getFinalAmount());
|
||||
// }
|
||||
if ("2".equals(orderInfoEntity.getPlaceType())) {
|
||||
orderRewardAmount = orderRewardAmount.add(orderInfoEntity.getFinalAmount());
|
||||
}
|
||||
@@ -303,12 +303,12 @@ public class PlayClerkPerformanceController {
|
||||
customIds.add(orderInfoEntity.getPurchaserBy());
|
||||
finalAmount = finalAmount.add(orderInfoEntity.getFinalAmount());
|
||||
orderMoney = orderMoney.add(orderInfoEntity.getOrderMoney());
|
||||
if ("1".equals(orderInfoEntity.getFirstOrder())) {
|
||||
orderFirstAmount = orderFirstAmount.add(orderInfoEntity.getFinalAmount());
|
||||
} else {
|
||||
orderContinueNumber++;
|
||||
orderTotalAmount = orderTotalAmount.add(orderInfoEntity.getFinalAmount());
|
||||
}
|
||||
// if ("1".equals(orderInfoEntity.getFirstOrder())) {
|
||||
// orderFirstAmount = orderFirstAmount.add(orderInfoEntity.getFinalAmount());
|
||||
// } else {
|
||||
// orderContinueNumber++;
|
||||
// orderTotalAmount = orderTotalAmount.add(orderInfoEntity.getFinalAmount());
|
||||
// }
|
||||
if ("2".equals(orderInfoEntity.getPlaceType())) {
|
||||
orderRewardAmount = orderRewardAmount.add(orderInfoEntity.getFinalAmount());
|
||||
}
|
||||
|
||||
@@ -33,11 +33,9 @@ import com.starry.admin.utils.SecurityUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -91,12 +89,12 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
completedOrderCount++;
|
||||
customIds.add(orderInfoEntity.getPurchaserBy());
|
||||
finalAmount = finalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(orderInfoEntity.getFirstOrder())) {
|
||||
orderFirstAmount = orderFirstAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||
} else {
|
||||
orderContinueNumber++;
|
||||
orderTotalAmount = orderTotalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||
}
|
||||
// if (OrderConstant.YesNoFlag.YES.getCode().equals(orderInfoEntity.getFirstOrder())) {
|
||||
// orderFirstAmount = orderFirstAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||
// } else {
|
||||
// orderContinueNumber++;
|
||||
// orderTotalAmount = orderTotalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||
// }
|
||||
if (OrderConstant.PlaceType.REWARD.getCode().equals(orderInfoEntity.getPlaceType())) {
|
||||
orderRewardAmount = orderRewardAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||
}
|
||||
@@ -202,7 +200,7 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
responseVo.setSnapshot(snapshot);
|
||||
responseVo.setComposition(buildComposition(snapshot));
|
||||
if (Boolean.TRUE.equals(vo.getIncludeTrend())) {
|
||||
responseVo.setTrend(buildTrend(orders, range,
|
||||
responseVo.setTrend(buildTrend(clerk.getId(), orders, range,
|
||||
vo.getTrendDays() == null || vo.getTrendDays() <= 0 ? 7 : vo.getTrendDays()));
|
||||
responseVo.setTrendDimension("DAY");
|
||||
} else {
|
||||
@@ -226,8 +224,8 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
return profile;
|
||||
}
|
||||
|
||||
private List<ClerkPerformanceTrendPointVo> buildTrend(List<PlayOrderInfoEntity> orders, DateRange range,
|
||||
int trendDays) {
|
||||
private List<ClerkPerformanceTrendPointVo> buildTrend(String clerkId, List<PlayOrderInfoEntity> orders,
|
||||
DateRange range, int trendDays) {
|
||||
List<PlayOrderInfoEntity> completedOrders = orders.stream()
|
||||
.filter(this::isCompletedOrder)
|
||||
.collect(Collectors.toList());
|
||||
@@ -277,11 +275,12 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
for (PlayOrderInfoEntity order : orders) {
|
||||
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
|
||||
gmv = gmv.add(finalAmount);
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(order.getFirstOrder())) {
|
||||
firstAmount = firstAmount.add(finalAmount);
|
||||
} else {
|
||||
OrderConstant.OrderRelationType relationType = normalizeRelationType(order);
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
continuedAmount = continuedAmount.add(finalAmount);
|
||||
continuedCount++;
|
||||
} else {
|
||||
firstAmount = firstAmount.add(finalAmount);
|
||||
}
|
||||
}
|
||||
point.setGmv(gmv);
|
||||
@@ -443,6 +442,23 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
return defaultZero(sum);
|
||||
}
|
||||
|
||||
private OrderConstant.OrderRelationType normalizeRelationType(PlayOrderInfoEntity order) {
|
||||
if (order == null) {
|
||||
throw new ServiceException("订单不能为空,无法统计首单/续单");
|
||||
}
|
||||
OrderConstant.OrderRelationType relationType = order.getOrderRelationType();
|
||||
if (relationType == null) {
|
||||
throw new ServiceException("订单关系类型不能为空,无法统计首单/续单");
|
||||
}
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(order.getPlaceType())) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (relationType == OrderConstant.OrderRelationType.NEUTRAL) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
return relationType;
|
||||
}
|
||||
|
||||
private ClerkPerformanceSnapshotVo buildSnapshot(PlayClerkUserInfoEntity clerk, List<PlayOrderInfoEntity> orders,
|
||||
Map<String, String> levelNameMap, Map<String, String> groupNameMap, String startTime, String endTime) {
|
||||
ClerkPerformanceSnapshotVo snapshot = new ClerkPerformanceSnapshotVo();
|
||||
@@ -461,23 +477,20 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
int continuedCount = 0;
|
||||
int refundCount = 0;
|
||||
int expiredCount = 0;
|
||||
Map<String, Integer> userOrderMap = new HashMap<>();
|
||||
int orderCount = 0;
|
||||
for (PlayOrderInfoEntity order : orders) {
|
||||
if (!isCompletedOrder(order)) {
|
||||
continue;
|
||||
}
|
||||
orderCount++;
|
||||
List<PlayOrderInfoEntity> completedOrders = orders.stream()
|
||||
.filter(this::isCompletedOrder)
|
||||
.collect(Collectors.toList());
|
||||
int orderCount = completedOrders.size();
|
||||
Set<String> userIds = new HashSet<>();
|
||||
Set<String> continuedUserIds = new HashSet<>();
|
||||
for (PlayOrderInfoEntity order : completedOrders) {
|
||||
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
|
||||
gmv = gmv.add(finalAmount);
|
||||
userOrderMap.merge(order.getPurchaserBy(), 1, Integer::sum);
|
||||
if (OrderConstant.YesNoFlag.YES.getCode().equals(order.getFirstOrder())) {
|
||||
firstCount++;
|
||||
firstAmount = firstAmount.add(finalAmount);
|
||||
} else {
|
||||
continuedCount++;
|
||||
continuedAmount = continuedAmount.add(finalAmount);
|
||||
String customerId = order.getPurchaserBy();
|
||||
if (StrUtil.isBlank(customerId)) {
|
||||
throw new ServiceException("订单缺少顾客信息,无法统计首单/续单");
|
||||
}
|
||||
userIds.add(customerId);
|
||||
if (OrderConstant.PlaceType.REWARD.getCode().equals(order.getPlaceType())) {
|
||||
rewardAmount = rewardAmount.add(finalAmount);
|
||||
}
|
||||
@@ -488,9 +501,18 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
||||
if (OrderConstant.OrdersExpiredState.EXPIRED.getCode().equals(order.getOrdersExpiredState())) {
|
||||
expiredCount++;
|
||||
}
|
||||
OrderConstant.OrderRelationType relationType = normalizeRelationType(order);
|
||||
if (relationType == OrderConstant.OrderRelationType.CONTINUED) {
|
||||
continuedCount++;
|
||||
continuedAmount = continuedAmount.add(finalAmount);
|
||||
continuedUserIds.add(customerId);
|
||||
} else {
|
||||
firstCount++;
|
||||
firstAmount = firstAmount.add(finalAmount);
|
||||
}
|
||||
}
|
||||
int userCount = userOrderMap.size();
|
||||
int continuedUserCount = (int) userOrderMap.values().stream().filter(cnt -> cnt > 1).count();
|
||||
int userCount = userIds.size();
|
||||
int continuedUserCount = continuedUserIds.size();
|
||||
BigDecimal estimatedRevenue = calculateEarningsAmount(clerk.getId(), orders, startTime, endTime);
|
||||
snapshot.setGmv(gmv);
|
||||
snapshot.setFirstOrderAmount(firstAmount);
|
||||
|
||||
@@ -235,7 +235,7 @@ public class WxCustomController {
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(RewardType.BALANCE)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(userId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
@@ -304,7 +304,7 @@ public class WxCustomController {
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(customId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
@@ -376,7 +376,7 @@ public class WxCustomController {
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.FIRST)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(customId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
|
||||
@@ -34,6 +34,16 @@ public class PlayClerkOrderDetailsReturnVo {
|
||||
*/
|
||||
private String placeType;
|
||||
|
||||
/**
|
||||
* 订单关系类型(FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 是否是首单【0:不是,1:是】
|
||||
*/
|
||||
private String firstOrder;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,11 @@ public class PlayClerkOrderListReturnVo {
|
||||
*/
|
||||
private String placeType;
|
||||
|
||||
/**
|
||||
* 订单关系类型(FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 是否是首单【0:不是,1:是】
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,16 @@ public class PlayCustomOrderDetailsReturnVo {
|
||||
*/
|
||||
private String placeType;
|
||||
|
||||
/**
|
||||
* 订单关系类型(FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 是否是首单【0:不是,1:是】
|
||||
*/
|
||||
private String firstOrder;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,16 @@ public class PlayCustomOrderListReturnVo {
|
||||
*/
|
||||
private String placeType;
|
||||
|
||||
/**
|
||||
* 订单关系类型(FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 是否是首单【0:不是,1:是】
|
||||
*/
|
||||
private String firstOrder;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,11 @@ public class PlayRandomOrderInfoReturnVo {
|
||||
*/
|
||||
private String placeType;
|
||||
|
||||
/**
|
||||
* 订单关系类型(FIRST/CONTINUED/NEUTRAL)
|
||||
*/
|
||||
private com.starry.admin.modules.order.module.constant.OrderConstant.OrderRelationType orderRelationType;
|
||||
|
||||
/**
|
||||
* 是否是首单【0:不是,1:是】
|
||||
*/
|
||||
|
||||
@@ -179,7 +179,7 @@ public class WxBlindBoxOrderService {
|
||||
.orderType(OrderConstant.OrderType.BLIND_BOX_PURCHASE)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(RewardType.GIFT)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(customer.getId())
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
|
||||
@@ -108,7 +108,7 @@ public class WxGiftOrderService {
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(RewardType.GIFT)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(purchaserId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
CREATE TABLE play_clerk_customer_relation (
|
||||
id VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
purchaser_by VARCHAR(64) NOT NULL COMMENT '顾客ID',
|
||||
tenant_id VARCHAR(32) DEFAULT NULL COMMENT '租户ID',
|
||||
accept_by VARCHAR(64) NOT NULL COMMENT '店员ID',
|
||||
has_completed VARCHAR(1) NOT NULL DEFAULT '0' COMMENT '是否有已完成历史 0否 1是',
|
||||
first_completed_order_id VARCHAR(64) DEFAULT NULL COMMENT '首次完成订单ID',
|
||||
first_completed_time DATETIME DEFAULT NULL COMMENT '首次完成时间',
|
||||
created_by VARCHAR(64) DEFAULT NULL COMMENT '创建人',
|
||||
created_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
updated_by VARCHAR(64) DEFAULT NULL COMMENT '更新人',
|
||||
updated_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
deleted TINYINT(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1已删除 0未删除',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_clerk_customer_relation (tenant_id, purchaser_by, accept_by)
|
||||
) COMMENT '客户-店员关系(完成历史)';
|
||||
|
||||
INSERT INTO play_clerk_customer_relation (
|
||||
id,
|
||||
purchaser_by,
|
||||
tenant_id,
|
||||
accept_by,
|
||||
has_completed,
|
||||
first_completed_time,
|
||||
created_time,
|
||||
updated_time,
|
||||
deleted
|
||||
)
|
||||
SELECT
|
||||
REPLACE(UUID(), '-', ''),
|
||||
purchaser_by,
|
||||
tenant_id,
|
||||
accept_by,
|
||||
'1',
|
||||
MIN(COALESCE(order_end_time, purchaser_time)),
|
||||
NOW(),
|
||||
NOW(),
|
||||
0
|
||||
FROM play_order_info
|
||||
WHERE order_status = '3'
|
||||
AND purchaser_by IS NOT NULL
|
||||
AND purchaser_by <> ''
|
||||
AND tenant_id IS NOT NULL
|
||||
AND tenant_id <> ''
|
||||
AND accept_by IS NOT NULL
|
||||
AND accept_by <> ''
|
||||
GROUP BY tenant_id, purchaser_by, accept_by;
|
||||
|
||||
ALTER TABLE play_order_info
|
||||
ADD COLUMN order_relation_type VARCHAR(32) NOT NULL DEFAULT 'LEGACY';
|
||||
|
||||
UPDATE play_order_info o
|
||||
LEFT JOIN (
|
||||
SELECT purchaser_by, accept_by, MIN(purchaser_time) AS first_completed_time
|
||||
FROM play_order_info
|
||||
WHERE order_status = '3'
|
||||
GROUP BY purchaser_by, accept_by
|
||||
) h ON o.purchaser_by = h.purchaser_by AND o.accept_by = h.accept_by
|
||||
SET o.order_relation_type = CASE
|
||||
WHEN o.place_type = '1' THEN 'FIRST'
|
||||
WHEN o.accept_by IS NULL OR o.accept_by = '' THEN 'LEGACY'
|
||||
WHEN h.first_completed_time IS NOT NULL AND o.purchaser_time > h.first_completed_time THEN 'CONTINUED'
|
||||
ELSE 'FIRST'
|
||||
END
|
||||
WHERE o.order_relation_type IS NULL
|
||||
OR o.order_relation_type = ''
|
||||
OR o.order_relation_type = 'LEGACY';
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.starry.admin.api;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.constant.Constants;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
class LegacyOrderIntegrationTest extends WxCustomOrderApiTestSupport {
|
||||
|
||||
private String orderIdToCleanup;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (orderIdToCleanup != null) {
|
||||
ensureTenantContext();
|
||||
playOrderInfoService.removeById(orderIdToCleanup);
|
||||
orderIdToCleanup = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLegacyOrderDetails_Admin_ShouldReturnLegacyType() throws Exception {
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity legacyOrder = createLegacyOrder();
|
||||
|
||||
mockMvc.perform(get("/order/order/" + legacyOrder.getId())
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.orderRelationType").value("LEGACY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listByPage_Admin_ShouldReturnLegacyType() throws Exception {
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity legacyOrder = createLegacyOrder();
|
||||
|
||||
ObjectNode payload = objectMapper.createObjectNode();
|
||||
payload.put("pageNum", 1);
|
||||
payload.put("pageSize", 10);
|
||||
payload.put("orderNo", legacyOrder.getOrderNo());
|
||||
|
||||
String response = mockMvc.perform(post("/order/order/listByPage")
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
|
||||
JsonNode root = objectMapper.readTree(response);
|
||||
JsonNode data = root.path("data");
|
||||
JsonNode records = data.isArray() ? data : data.path("records");
|
||||
String type = records.get(0).path("orderRelationType").asText();
|
||||
assertThat(type).isEqualTo("LEGACY");
|
||||
}
|
||||
private String ensureCustomerToken() {
|
||||
resetCustomerBalance();
|
||||
String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||
return customerToken;
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryById_WxCustom_ShouldReturnLegacyType() throws Exception {
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity legacyOrder = createLegacyOrder();
|
||||
String customerToken = ensureCustomerToken();
|
||||
|
||||
mockMvc.perform(get("/wx/custom/order/queryById")
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||
.param("id", legacyOrder.getId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.orderRelationType").value("LEGACY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryByPage_WxCustom_ShouldReturnLegacyType() throws Exception {
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity legacyOrder = createLegacyOrder();
|
||||
String customerToken = ensureCustomerToken();
|
||||
|
||||
ObjectNode payload = objectMapper.createObjectNode();
|
||||
payload.put("pageNum", 1);
|
||||
payload.put("pageSize", 20);
|
||||
|
||||
String response = mockMvc.perform(post("/wx/custom/order/queryByPage")
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
|
||||
JsonNode root = objectMapper.readTree(response);
|
||||
JsonNode records = root.path("data").isArray() ? root.path("data") : root.path("data").path("records");
|
||||
boolean found = false;
|
||||
for (JsonNode node : records) {
|
||||
if (legacyOrder.getId().equals(node.path("id").asText())) {
|
||||
assertThat(node.path("orderRelationType").asText()).isEqualTo("LEGACY");
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(found).withFailMessage("Legacy order not found in customer list").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateEstimatedRevenue_ForLegacyOrder_ShouldNotFail() throws Exception {
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity legacyOrder = createLegacyOrder();
|
||||
|
||||
BigDecimal revenue = playOrderInfoService.getEstimatedRevenue(
|
||||
legacyOrder.getAcceptBy(),
|
||||
legacyOrder.getPlaceType(),
|
||||
legacyOrder.getOrderRelationType(),
|
||||
legacyOrder.getOrderMoney()
|
||||
);
|
||||
|
||||
assertThat(revenue).isNotNull();
|
||||
assertThat(revenue).isGreaterThanOrEqualTo(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateEstimatedRevenue_ForUnassignedOrder_ShouldThrowException() {
|
||||
ensureTenantContext();
|
||||
try {
|
||||
playOrderInfoService.getEstimatedRevenue(
|
||||
ApiTestDataSeeder.DEFAULT_CLERK_ID,
|
||||
"0", // Specified
|
||||
OrderConstant.OrderRelationType.UNASSIGNED,
|
||||
new BigDecimal("100.00")
|
||||
);
|
||||
assertThat(true).withFailMessage("Should have thrown exception for UNASSIGNED relation type").isFalse();
|
||||
} catch (Exception e) {
|
||||
assertThat(e).isInstanceOf(RuntimeException.class);
|
||||
assertThat(e.getMessage()).contains("未分配订单不可计算预计收益");
|
||||
}
|
||||
}
|
||||
|
||||
private PlayOrderInfoEntity createLegacyOrder() {
|
||||
PlayOrderInfoEntity order = new PlayOrderInfoEntity();
|
||||
String id = "legacy-" + IdUtils.getUuid();
|
||||
order.setId(id);
|
||||
order.setOrderNo("LEGACY" + IdUtils.getUuid().substring(0, 6));
|
||||
order.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
order.setOrderStatus("3"); // Completed
|
||||
order.setOrderType("2"); // Normal
|
||||
order.setPlaceType("0"); // Specified
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.LEGACY);
|
||||
order.setOrderMoney(new BigDecimal("100.00"));
|
||||
order.setFinalAmount(new BigDecimal("100.00"));
|
||||
order.setAcceptBy(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||
order.setPurchaserBy(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||
order.setPurchaserTime(LocalDateTime.now().minusDays(100));
|
||||
order.setCreatedTime(new Date());
|
||||
order.setUpdatedTime(new Date());
|
||||
order.setDeleted(false);
|
||||
order.setPayMethod("2");
|
||||
order.setUseCoupon("0");
|
||||
order.setBackendEntry("0");
|
||||
order.setSex("2");
|
||||
order.setCommodityType("1");
|
||||
order.setCommodityId(ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
|
||||
order.setCommodityName("Legacy Service");
|
||||
order.setCommodityPrice(new BigDecimal("100.00"));
|
||||
order.setCommodityNumber("1");
|
||||
order.setRefundType("0");
|
||||
order.setRefundAmount(BigDecimal.ZERO);
|
||||
|
||||
playOrderInfoService.save(order);
|
||||
orderIdToCleanup = id;
|
||||
return order;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,32 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final BigDecimal DEFAULT_AMOUNT = new BigDecimal("188.00");
|
||||
private static final int ID_SUFFIX_LENGTH = 6;
|
||||
private static final String FIRST_FLAG = OrderConstant.YesNoFlag.YES.getCode();
|
||||
private static final String CONTINUED_FLAG = OrderConstant.YesNoFlag.NO.getCode();
|
||||
private static final String RELATION_FIRST = OrderConstant.OrderRelationType.FIRST.getCode();
|
||||
private static final String RELATION_CONTINUED = OrderConstant.OrderRelationType.CONTINUED.getCode();
|
||||
private static final String RELATION_UNASSIGNED = OrderConstant.OrderRelationType.UNASSIGNED.getCode();
|
||||
private static final String RELATION_NEUTRAL = OrderConstant.OrderRelationType.NEUTRAL.getCode();
|
||||
private static final String RELATION_LEGACY = OrderConstant.OrderRelationType.LEGACY.getCode();
|
||||
private static final String FIELD_ORDER_RELATION_TYPE = "orderRelationType";
|
||||
private static final String FIELD_FIRST_ORDER = "firstOrder";
|
||||
private static final String PREFIX_FIRST_RELATION = "FR";
|
||||
private static final String PREFIX_CONTINUED_RELATION = "CR";
|
||||
private static final String PREFIX_UNASSIGNED_RELATION = "UR";
|
||||
private static final String PREFIX_NEUTRAL_RELATION = "NR";
|
||||
private static final String PREFIX_RANDOM_RELATION = "RR";
|
||||
private static final String PREFIX_FIRST_FILTER_YES = "FY";
|
||||
private static final String PREFIX_FIRST_FILTER_NO = "FN";
|
||||
private static final String PREFIX_BACKWARD_COMPAT = "BC";
|
||||
private static final String TOKEN_LEGACY_CLIENT = "legacy-client";
|
||||
private static final String TOKEN_LEGACY_CONTINUED = "legacy-continued";
|
||||
private static final long HOURS_OFFSET_ONE = 1L;
|
||||
private static final long HOURS_OFFSET_TWO = 2L;
|
||||
private static final long HOURS_OFFSET_THREE = 3L;
|
||||
private static final long HOURS_OFFSET_FOUR = 4L;
|
||||
private static final long HOURS_OFFSET_FIVE = 5L;
|
||||
private static final long MINUTES_OFFSET_TEN = 10L;
|
||||
|
||||
@Autowired
|
||||
private IPlayOrderInfoService orderInfoService;
|
||||
@@ -87,20 +113,20 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
order.setPayMethod("2");
|
||||
order.setUseCoupon("1");
|
||||
order.setBackendEntry("1");
|
||||
order.setFirstOrder("0");
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
order.setGroupId(ApiTestDataSeeder.DEFAULT_GROUP_ID);
|
||||
order.setSex("2");
|
||||
order.setAcceptBy(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||
order.setPurchaserBy(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||
});
|
||||
|
||||
persistOrder(marker, "noise", reference.minusDays(3), order -> {
|
||||
PlayOrderInfoEntity noiseOrder = persistOrder(marker, "noise", reference.minusDays(3), order -> {
|
||||
order.setOrderStatus("0");
|
||||
order.setPlaceType("0");
|
||||
order.setPayMethod("0");
|
||||
order.setUseCoupon("0");
|
||||
order.setBackendEntry("0");
|
||||
order.setFirstOrder("1");
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
order.setGroupId(marker + "-grp");
|
||||
order.setSex("1");
|
||||
order.setAcceptBy(null);
|
||||
@@ -149,9 +175,13 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
|
||||
// firstOrder filter
|
||||
ObjectNode firstOrderPayload = queryWithMarker(marker);
|
||||
firstOrderPayload.put("firstOrder", matching.getFirstOrder());
|
||||
firstOrderPayload.put("firstOrder", OrderConstant.YesNoFlag.NO.getCode());
|
||||
assertFilterMatches(firstOrderPayload, matching.getId());
|
||||
|
||||
ObjectNode firstOrderYesPayload = queryWithMarker(marker);
|
||||
firstOrderYesPayload.put("firstOrder", OrderConstant.YesNoFlag.YES.getCode());
|
||||
assertFilterMatches(firstOrderYesPayload, noiseOrder.getId());
|
||||
|
||||
// groupId filter
|
||||
ObjectNode groupPayload = queryWithMarker(marker);
|
||||
groupPayload.put("groupId", matching.getGroupId());
|
||||
@@ -192,7 +222,7 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
combinedPayload.put("payMethod", matching.getPayMethod());
|
||||
combinedPayload.put("useCoupon", matching.getUseCoupon());
|
||||
combinedPayload.put("backendEntry", matching.getBackendEntry());
|
||||
combinedPayload.put("firstOrder", matching.getFirstOrder());
|
||||
combinedPayload.put("firstOrder", OrderConstant.YesNoFlag.NO.getCode());
|
||||
combinedPayload.put("groupId", matching.getGroupId());
|
||||
combinedPayload.put("sex", matching.getSex());
|
||||
combinedPayload.set("purchaserTime", range(
|
||||
@@ -207,6 +237,191 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
assertFilterMatches(combinedPayload, matching.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证首单关系在列表中映射为 firstOrder=1。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_mapsFirstOrderFlagForFirstRelationType() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_FIRST_RELATION + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_FOUR).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, "first", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_FIRST, FIRST_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证续单关系在列表中映射为 firstOrder=0。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_mapsFirstOrderFlagForContinuedRelationType() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_CONTINUED_RELATION + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_THREE).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, "continued", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_CONTINUED, CONTINUED_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证未分配订单在列表中映射为 firstOrder=1。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_mapsFirstOrderFlagForUnassignedRelationType() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_UNASSIGNED_RELATION + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_TWO).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, "unassigned", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.UNASSIGNED);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
entity.setAcceptBy(null);
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_UNASSIGNED, FIRST_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证历史存量关系在列表中映射为首单。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_mapsFirstOrderFlagForLegacyRelationType() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (RELATION_LEGACY + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_TWO).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, "legacy", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.LEGACY);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_LEGACY, FIRST_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证中性关系在列表展示中被视为首单。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_normalizesNeutralRelationTypeToFirst() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_NEUTRAL_RELATION + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(2).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, "neutral", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.NEUTRAL);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_FIRST, FIRST_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证随机单强制展示为首单关系。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_normalizesRandomRelationTypeToFirst() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_RANDOM_RELATION + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_ONE).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, "random", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.RANDOM.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_FIRST, FIRST_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证首单筛选仅返回首单关系订单。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_filtersFirstOrderYesMatchesFirstRelationType() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_FIRST_FILTER_YES + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_FIVE).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity firstOrder = persistOrder(marker, "first", reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
});
|
||||
persistOrder(marker, "continued", reference.plusMinutes(MINUTES_OFFSET_TEN), entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
});
|
||||
|
||||
ObjectNode payload = queryWithMarker(marker);
|
||||
payload.put("firstOrder", FIRST_FLAG);
|
||||
assertFilterMatches(payload, firstOrder.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证续单筛选仅返回续单关系订单。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_filtersFirstOrderNoMatchesContinuedRelationType() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_FIRST_FILTER_NO + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_FIVE).withNano(0);
|
||||
|
||||
persistOrder(marker, "first", reference, entity -> entity.setOrderRelationType(OrderConstant.OrderRelationType.FIRST));
|
||||
PlayOrderInfoEntity continuedOrder = persistOrder(marker, "continued", reference.plusMinutes(MINUTES_OFFSET_TEN), entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
});
|
||||
|
||||
ObjectNode payload = queryWithMarker(marker);
|
||||
payload.put("firstOrder", CONTINUED_FLAG);
|
||||
assertFilterMatches(payload, continuedOrder.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证未传首单筛选时,列表仍返回首单字段(向后兼容)。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_includesFirstOrderFieldWhenFilterNotProvided() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_BACKWARD_COMPAT + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_TWO).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, TOKEN_LEGACY_CLIENT, reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_FIRST, FIRST_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证续单关系在旧字段中仍保持兼容输出。
|
||||
*/
|
||||
@Test
|
||||
void listByPage_includesFirstOrderFieldForContinuedRelation() throws Exception {
|
||||
ensureTenantContext();
|
||||
String marker = (PREFIX_BACKWARD_COMPAT + IdUtils.getUuid().replace("-", "").substring(0, ID_SUFFIX_LENGTH)).toUpperCase();
|
||||
LocalDateTime reference = LocalDateTime.now().minusHours(HOURS_OFFSET_THREE).withNano(0);
|
||||
|
||||
PlayOrderInfoEntity order = persistOrder(marker, TOKEN_LEGACY_CONTINUED, reference, entity -> {
|
||||
entity.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
entity.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
});
|
||||
|
||||
JsonNode record = fetchSingleRecord(queryWithMarker(marker), order.getId());
|
||||
assertRelationFields(record, RELATION_CONTINUED, CONTINUED_FLAG);
|
||||
}
|
||||
|
||||
@Test
|
||||
void listByPage_keywordFiltersByOrderNoOrClerkName() throws Exception {
|
||||
ensureTenantContext();
|
||||
@@ -736,7 +951,7 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
order.setOrderType("2");
|
||||
order.setPlaceType("1");
|
||||
order.setRewardType("0");
|
||||
order.setFirstOrder("0");
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
order.setRefundType("0");
|
||||
order.setRefundAmount(BigDecimal.ZERO);
|
||||
order.setRefundReason(null);
|
||||
@@ -846,6 +1061,25 @@ class PlayOrderInfoControllerApiTest extends AbstractApiTest {
|
||||
.isEqualTo(expectedOrderId);
|
||||
}
|
||||
|
||||
private JsonNode fetchSingleRecord(ObjectNode payload, String expectedOrderId) throws Exception {
|
||||
RecordsResponse response = executeList(payload);
|
||||
JsonNode records = response.records;
|
||||
assertThat(records.isArray())
|
||||
.withFailMessage("Records payload is not an array for body=%s | response=%s", payload, response.rawResponse)
|
||||
.isTrue();
|
||||
for (JsonNode record : records) {
|
||||
if (expectedOrderId.equals(record.path("id").asText())) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("未找到订单 " + expectedOrderId);
|
||||
}
|
||||
|
||||
private void assertRelationFields(JsonNode record, String relationCode, String firstOrderFlag) {
|
||||
assertThat(record.path(FIELD_ORDER_RELATION_TYPE).asText()).isEqualTo(relationCode);
|
||||
assertThat(record.path(FIELD_FIRST_ORDER).asText()).isEqualTo(firstOrderFlag);
|
||||
}
|
||||
|
||||
private RecordsResponse executeList(ObjectNode payload) throws Exception {
|
||||
MvcResult result = mockMvc.perform(post("/order/order/listByPage")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
|
||||
@@ -362,7 +362,7 @@ class WxCustomOrderQueryApiTest extends WxCustomOrderApiTestSupport {
|
||||
order.setOrderNo("FOCUS-" + IdUtils.getUuid().substring(0, 4));
|
||||
order.setUseCoupon("1");
|
||||
order.setBackendEntry("1");
|
||||
order.setFirstOrder("1");
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
order.setGroupId("group-focus");
|
||||
order.setSex("1");
|
||||
order.setPurchaserTime(base.plusMinutes(5));
|
||||
@@ -374,7 +374,7 @@ class WxCustomOrderQueryApiTest extends WxCustomOrderApiTestSupport {
|
||||
order.setPlaceType(OrderConstant.PlaceType.RANDOM.getCode());
|
||||
order.setUseCoupon("0");
|
||||
order.setBackendEntry("0");
|
||||
order.setFirstOrder("0");
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.CONTINUED);
|
||||
order.setGroupId("group-noise");
|
||||
order.setSex("2");
|
||||
});
|
||||
@@ -384,7 +384,7 @@ class WxCustomOrderQueryApiTest extends WxCustomOrderApiTestSupport {
|
||||
payload.put("orderNo", target.getOrderNo().substring(0, 6));
|
||||
payload.put("useCoupon", "1");
|
||||
payload.put("backendEntry", "1");
|
||||
payload.put("firstOrder", "1");
|
||||
payload.put("firstOrder", OrderConstant.YesNoFlag.YES.getCode());
|
||||
payload.put("groupId", "group-focus");
|
||||
payload.put("sex", "1");
|
||||
payload.set("purchaserTime", range(target.getPurchaserTime().minusMinutes(1), target.getPurchaserTime().plusMinutes(1)));
|
||||
@@ -455,7 +455,7 @@ class WxCustomOrderQueryApiTest extends WxCustomOrderApiTestSupport {
|
||||
order.setOrderType(OrderConstant.OrderType.NORMAL.getCode());
|
||||
order.setPlaceType(OrderConstant.PlaceType.SPECIFIED.getCode());
|
||||
order.setRewardType("0");
|
||||
order.setFirstOrder("0");
|
||||
order.setOrderRelationType(OrderConstant.OrderRelationType.FIRST);
|
||||
order.setRefundType("0");
|
||||
order.setRefundAmount(BigDecimal.ZERO);
|
||||
order.setOrderMoney(new BigDecimal("99.00"));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ class OrderCreationContextTest {
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(OrderConstant.RewardType.BALANCE)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.commodityInfo(commodityInfo)
|
||||
.paymentInfo(paymentInfo)
|
||||
.purchaserBy("customer_001")
|
||||
@@ -64,8 +64,7 @@ class OrderCreationContextTest {
|
||||
assertEquals(OrderConstant.OrderStatus.PENDING, request.getOrderStatus());
|
||||
assertEquals(OrderConstant.OrderType.NORMAL, request.getOrderType());
|
||||
assertEquals(OrderConstant.PlaceType.SPECIFIED, request.getPlaceType());
|
||||
assertTrue(request.isFirstOrder());
|
||||
assertEquals("1", request.getFirstOrderString());
|
||||
assertEquals(OrderConstant.OrderRelationType.UNASSIGNED, request.getOrderRelationType());
|
||||
|
||||
// 验证商品信息
|
||||
assertNotNull(request.getCommodityInfo());
|
||||
@@ -88,7 +87,7 @@ class OrderCreationContextTest {
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
@@ -105,7 +104,7 @@ class OrderCreationContextTest {
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.FIRST)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
@@ -127,7 +126,7 @@ class OrderCreationContextTest {
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
@@ -139,37 +138,35 @@ class OrderCreationContextTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试首单标识转换")
|
||||
void testFirstOrderStringConversion() {
|
||||
// 测试首单
|
||||
@DisplayName("测试订单关系类型设置")
|
||||
void testRelationTypeAssignment() {
|
||||
OrderCreationContext firstOrder = OrderCreationContext.builder()
|
||||
.orderId("order_001")
|
||||
.orderNo("ORD001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.FIRST)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
.build();
|
||||
|
||||
assertEquals("1", firstOrder.getFirstOrderString());
|
||||
assertEquals(OrderConstant.OrderRelationType.FIRST, firstOrder.getOrderRelationType());
|
||||
|
||||
// 测试非首单
|
||||
OrderCreationContext notFirstOrder = OrderCreationContext.builder()
|
||||
OrderCreationContext continuedOrder = OrderCreationContext.builder()
|
||||
.orderId("order_002")
|
||||
.orderNo("ORD002")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.CONTINUED)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
.build();
|
||||
|
||||
assertEquals("0", notFirstOrder.getFirstOrderString());
|
||||
assertEquals(OrderConstant.OrderRelationType.CONTINUED, continuedOrder.getOrderRelationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -182,7 +179,7 @@ class OrderCreationContextTest {
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.FIRST)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
@@ -200,7 +197,7 @@ class OrderCreationContextTest {
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.FIRST)
|
||||
.commodityInfo(CommodityInfo.builder().commodityId("test").build())
|
||||
.paymentInfo(PaymentInfo.builder().orderMoney(BigDecimal.ZERO).build())
|
||||
.purchaserBy("customer")
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.mapper.PlayClerkCustomerRelationMapper;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderLogInfoMapper;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
@@ -46,6 +47,7 @@ import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderRevocationContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
import com.starry.admin.modules.order.module.entity.PlayClerkCustomerRelationEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.module.event.OrderRevocationEvent;
|
||||
import com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo;
|
||||
@@ -60,6 +62,7 @@ import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
|
||||
import com.starry.admin.modules.weichat.service.NotificationSender;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
@@ -110,6 +113,9 @@ class OrderLifecycleServiceImplTest {
|
||||
@Mock
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
@Mock
|
||||
private PlayClerkCustomerRelationMapper clerkCustomerRelationMapper;
|
||||
|
||||
@Mock
|
||||
private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService;
|
||||
|
||||
@@ -118,6 +124,12 @@ class OrderLifecycleServiceImplTest {
|
||||
|
||||
@BeforeEach
|
||||
void initStrategies() {
|
||||
SecurityUtils.setTenantId("tenant-test");
|
||||
PlayClerkCustomerRelationEntity relation = new PlayClerkCustomerRelationEntity();
|
||||
relation.setHasCompleted(YesNoFlag.NO.getCode());
|
||||
relation.setDeleted(Boolean.FALSE);
|
||||
lenient().when(clerkCustomerRelationMapper.selectForUpdate(anyString(), anyString(), anyString()))
|
||||
.thenReturn(relation);
|
||||
lifecycleService.initPlacementStrategies();
|
||||
}
|
||||
|
||||
@@ -302,7 +314,7 @@ class OrderLifecycleServiceImplTest {
|
||||
.orderType(OrderType.NORMAL)
|
||||
.placeType(PlaceType.OTHER)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(false)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity")
|
||||
.commodityType(CommodityType.SERVICE)
|
||||
@@ -332,7 +344,7 @@ class OrderLifecycleServiceImplTest {
|
||||
.orderType(OrderType.NORMAL)
|
||||
.placeType(PlaceType.SPECIFIED)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity-01")
|
||||
.commodityType(CommodityType.SERVICE)
|
||||
@@ -370,10 +382,10 @@ class OrderLifecycleServiceImplTest {
|
||||
revenueVo.setRevenueAmount(BigDecimal.valueOf(89.5));
|
||||
revenueVo.setRevenueRatio(50);
|
||||
|
||||
when(orderInfoMapper.selectCount(any())).thenReturn(0L);
|
||||
when(orderInfoMapper.insert(any())).thenReturn(1);
|
||||
when(clerkRevenueCalculator.calculateEstimatedRevenue(
|
||||
anyString(), anyList(), anyString(), anyString(), any())).thenReturn(revenueVo);
|
||||
anyString(), anyList(), anyString(), any(OrderConstant.OrderRelationType.class), any()))
|
||||
.thenReturn(revenueVo);
|
||||
doNothing().when(customUserInfoService).saveOrderInfo(any());
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), anyString());
|
||||
|
||||
@@ -403,10 +415,10 @@ class OrderLifecycleServiceImplTest {
|
||||
request.getAcceptBy(),
|
||||
request.getPaymentInfo().getCouponIds(),
|
||||
request.getPlaceType().getCode(),
|
||||
YesNoFlag.YES.getCode(),
|
||||
OrderConstant.OrderRelationType.FIRST,
|
||||
request.getPaymentInfo().getOrderMoney());
|
||||
|
||||
assertEquals(YesNoFlag.YES.getCode(), created.getFirstOrder());
|
||||
assertEquals(OrderConstant.OrderRelationType.FIRST, created.getOrderRelationType());
|
||||
assertEquals(revenueVo.getRevenueAmount(), created.getEstimatedRevenue());
|
||||
assertEquals(revenueVo.getRevenueRatio(), created.getEstimatedRevenueRatio());
|
||||
assertEquals(PayMethod.WECHAT.getCode(), created.getPayMethod());
|
||||
@@ -1411,7 +1423,8 @@ class OrderLifecycleServiceImplTest {
|
||||
revenueVo.setRevenueAmount(BigDecimal.ZERO);
|
||||
revenueVo.setRevenueRatio(0);
|
||||
lenient().when(clerkRevenueCalculator.calculateEstimatedRevenue(
|
||||
anyString(), anyList(), anyString(), anyString(), any())).thenReturn(revenueVo);
|
||||
anyString(), anyList(), anyString(), any(OrderConstant.OrderRelationType.class), any()))
|
||||
.thenReturn(revenueVo);
|
||||
}
|
||||
|
||||
private PaymentInfo payment(BigDecimal gross, BigDecimal net, BigDecimal discount, List<String> couponIds) {
|
||||
@@ -1432,7 +1445,9 @@ class OrderLifecycleServiceImplTest {
|
||||
.orderType(OrderType.NORMAL)
|
||||
.placeType(placeType)
|
||||
.rewardType(rewardType)
|
||||
.isFirstOrder(true)
|
||||
.orderRelationType(placeType == PlaceType.RANDOM
|
||||
? OrderConstant.OrderRelationType.FIRST
|
||||
: OrderConstant.OrderRelationType.UNASSIGNED)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity-" + placeType.getCode())
|
||||
.commodityType(CommodityType.SERVICE)
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -70,6 +71,11 @@ class PlayClerkPerformanceServiceImplTest {
|
||||
@InjectMocks
|
||||
private PlayClerkPerformanceServiceImpl service;
|
||||
|
||||
@BeforeEach
|
||||
void setupDefaultMocks() {
|
||||
lenient().when(playOrderInfoService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryOverview should aggregate metrics and sort clerks by GMV")
|
||||
void queryOverviewAggregatesAndSorts() {
|
||||
@@ -499,7 +505,7 @@ class PlayClerkPerformanceServiceImplTest {
|
||||
PlayOrderInfoEntity order = new PlayOrderInfoEntity();
|
||||
order.setAcceptBy(clerkId);
|
||||
order.setPurchaserBy(purchaser);
|
||||
order.setFirstOrder(firstOrder);
|
||||
order.setOrderRelationType(resolveRelationType(firstOrder, placeType));
|
||||
order.setPlaceType(placeType);
|
||||
order.setRefundType(refundType);
|
||||
order.setFinalAmount(finalAmount);
|
||||
@@ -511,6 +517,16 @@ class PlayClerkPerformanceServiceImplTest {
|
||||
return order;
|
||||
}
|
||||
|
||||
private OrderConstant.OrderRelationType resolveRelationType(String firstOrder, String placeType) {
|
||||
if (OrderConstant.PlaceType.RANDOM.getCode().equals(placeType)) {
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
if (OrderConstant.YesNoFlag.NO.getCode().equals(firstOrder)) {
|
||||
return OrderConstant.OrderRelationType.CONTINUED;
|
||||
}
|
||||
return OrderConstant.OrderRelationType.FIRST;
|
||||
}
|
||||
|
||||
private void setAuthentication() {
|
||||
LoginUser loginUser = new LoginUser();
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
|
||||
Reference in New Issue
Block a user