Compare commits
2 Commits
8a9e7dc86f
...
6dba6464f9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dba6464f9 | ||
|
|
1ec92cc2ab |
@@ -158,6 +158,21 @@
|
|||||||
<artifactId>mockito-junit-jupiter</artifactId>
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jayway.jsonpath</groupId>
|
||||||
|
<artifactId>json-path</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
|||||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
||||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
|
||||||
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoQueryVo;
|
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoQueryVo;
|
||||||
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
|
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
|
||||||
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
|
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
|
||||||
@@ -81,6 +85,20 @@ public class PlayClerkPerformanceController {
|
|||||||
return R.ok(voPage);
|
return R.ok(voPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "店员业绩概览", notes = "汇总店员业绩榜单和汇总数据")
|
||||||
|
@PostMapping("/overview")
|
||||||
|
public R overview(
|
||||||
|
@ApiParam(value = "查询条件", required = true) @Validated @RequestBody ClerkPerformanceOverviewQueryVo vo) {
|
||||||
|
return R.ok(playClerkPerformanceService.queryOverview(vo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "店员业绩详情", notes = "查看单个店员的业绩构成和趋势")
|
||||||
|
@PostMapping("/detail")
|
||||||
|
public R detail(
|
||||||
|
@ApiParam(value = "查询条件", required = true) @Validated @RequestBody ClerkPerformanceDetailQueryVo vo) {
|
||||||
|
return R.ok(playClerkPerformanceService.queryDetail(vo));
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation(value = "按日查询业绩", notes = "按日期查询店员业绩统计信息")
|
@ApiOperation(value = "按日查询业绩", notes = "按日期查询店员业绩统计信息")
|
||||||
@ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
|
@ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
|
||||||
@PostMapping("/listByTime")
|
@PostMapping("/listByTime")
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序指标字段。
|
||||||
|
*
|
||||||
|
* @author admin
|
||||||
|
* @since 2024/08/30
|
||||||
|
*/
|
||||||
|
public enum ClerkPerformanceSortField {
|
||||||
|
/**
|
||||||
|
* 根据成交金额(GMV)排序。
|
||||||
|
*/
|
||||||
|
GMV,
|
||||||
|
/**
|
||||||
|
* 根据订单数量排序。
|
||||||
|
*/
|
||||||
|
ORDER_COUNT,
|
||||||
|
/**
|
||||||
|
* 根据续单率排序。
|
||||||
|
*/
|
||||||
|
CONTINUE_RATE,
|
||||||
|
/**
|
||||||
|
* 根据预估收入排序。
|
||||||
|
*/
|
||||||
|
ESTIMATED_REVENUE
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序方向。
|
||||||
|
*/
|
||||||
|
public enum SortDirection {
|
||||||
|
/**
|
||||||
|
* 升序。
|
||||||
|
*/
|
||||||
|
ASC,
|
||||||
|
/**
|
||||||
|
* 降序。
|
||||||
|
*/
|
||||||
|
DESC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认排序方向。
|
||||||
|
*/
|
||||||
|
public static final SortDirection DEFAULT = DESC;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩详情组成。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩详情组成数据")
|
||||||
|
public class ClerkPerformanceDetailCompositionVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("订单构成")
|
||||||
|
private List<CompositionSlice> orderComposition = Collections.emptyList();
|
||||||
|
|
||||||
|
@ApiModelProperty("金额构成")
|
||||||
|
private List<CompositionSlice> amountComposition = Collections.emptyList();
|
||||||
|
|
||||||
|
@ApiModelProperty("顾客构成")
|
||||||
|
private List<CompositionSlice> customerComposition = Collections.emptyList();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ApiModel("业绩组成占比")
|
||||||
|
public static class CompositionSlice {
|
||||||
|
|
||||||
|
@ApiModelProperty("标识")
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
@ApiModelProperty("名称")
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
@ApiModelProperty("数量")
|
||||||
|
private Integer count;
|
||||||
|
|
||||||
|
@ApiModelProperty("数量占比")
|
||||||
|
private BigDecimal countRatio = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("金额")
|
||||||
|
private BigDecimal amount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("金额占比")
|
||||||
|
private BigDecimal amountRatio = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩详情查询参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel("店员业绩详情查询参数")
|
||||||
|
public class ClerkPerformanceDetailQueryVo extends PlayClerkPerformanceInfoQueryVo {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotBlank(message = "店员ID不能为空")
|
||||||
|
@ApiModelProperty(value = "店员ID", required = true)
|
||||||
|
public String getClerkId() {
|
||||||
|
return super.getClerkId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否返回趋势数据")
|
||||||
|
private Boolean includeTrend = Boolean.TRUE;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "趋势天数,默认7")
|
||||||
|
private Integer trendDays = 7;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩详情响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩详情响应")
|
||||||
|
public class ClerkPerformanceDetailResponseVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("基础信息")
|
||||||
|
private ClerkPerformanceProfileVo profile;
|
||||||
|
|
||||||
|
@ApiModelProperty("业绩快照")
|
||||||
|
private ClerkPerformanceSnapshotVo snapshot;
|
||||||
|
|
||||||
|
@ApiModelProperty("业绩组成")
|
||||||
|
private ClerkPerformanceDetailCompositionVo composition;
|
||||||
|
|
||||||
|
@ApiModelProperty("趋势数据")
|
||||||
|
private List<ClerkPerformanceTrendPointVo> trend = Collections.emptyList();
|
||||||
|
|
||||||
|
@ApiModelProperty("趋势维度")
|
||||||
|
private String trendDimension = "DAY";
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import com.starry.admin.modules.statistics.module.enums.ClerkPerformanceSortField;
|
||||||
|
import com.starry.admin.modules.statistics.module.enums.SortDirection;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩概览查询参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel("店员业绩概览查询参数")
|
||||||
|
public class ClerkPerformanceOverviewQueryVo extends PlayClerkPerformanceInfoQueryVo {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序字段", allowableValues = "GMV,ORDER_COUNT,CONTINUE_RATE,ESTIMATED_REVENUE")
|
||||||
|
private ClerkPerformanceSortField sortField = ClerkPerformanceSortField.GMV;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序方向", allowableValues = "ASC,DESC")
|
||||||
|
private SortDirection sortDirection = SortDirection.DEFAULT;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "返回前N名数据,未设置则返回分页数据")
|
||||||
|
private Integer limit;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否包含汇总数据")
|
||||||
|
private Boolean includeSummary = Boolean.TRUE;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否包含排行列表数据")
|
||||||
|
private Boolean includeRankings = Boolean.TRUE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEndOrderTime(List<String> endOrderTime) {
|
||||||
|
super.setEndOrderTime(endOrderTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩概览响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩概览响应")
|
||||||
|
public class ClerkPerformanceOverviewResponseVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("汇总信息")
|
||||||
|
private ClerkPerformanceOverviewSummaryVo summary;
|
||||||
|
|
||||||
|
@ApiModelProperty("业绩排行榜")
|
||||||
|
private List<ClerkPerformanceSnapshotVo> rankings = Collections.emptyList();
|
||||||
|
|
||||||
|
@ApiModelProperty("榜单总人数")
|
||||||
|
private Integer total = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩概览汇总。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩概览汇总")
|
||||||
|
public class ClerkPerformanceOverviewSummaryVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("GMV汇总")
|
||||||
|
private BigDecimal totalGmv = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("订单总数")
|
||||||
|
private Integer totalOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("首单总数")
|
||||||
|
private Integer totalFirstOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("续单总数")
|
||||||
|
private Integer totalContinuedOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("退款总数")
|
||||||
|
private Integer totalRefundOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("续单率(加权)")
|
||||||
|
private BigDecimal continuedRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("续费率(续单金额/GMV)")
|
||||||
|
private BigDecimal continuedAmountRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("退单率")
|
||||||
|
private BigDecimal refundRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("客单价")
|
||||||
|
private BigDecimal averageTicketPrice = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("预估工资汇总")
|
||||||
|
private BigDecimal totalEstimatedRevenue = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("复购用户数")
|
||||||
|
private Integer totalContinuedUserCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("用户总数")
|
||||||
|
private Integer totalUserCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("续客率")
|
||||||
|
private BigDecimal continuedCustomerRate = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员基础信息。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩详情-基础信息")
|
||||||
|
public class ClerkPerformanceProfileVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("店员ID")
|
||||||
|
private String clerkId;
|
||||||
|
|
||||||
|
@ApiModelProperty("店员昵称")
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
@ApiModelProperty("头像地址")
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
@ApiModelProperty("性别")
|
||||||
|
private String sex;
|
||||||
|
|
||||||
|
@ApiModelProperty("所属组名称")
|
||||||
|
private String groupName;
|
||||||
|
|
||||||
|
@ApiModelProperty("等级名称")
|
||||||
|
private String levelName;
|
||||||
|
|
||||||
|
@ApiModelProperty("上架状态")
|
||||||
|
private String listingState;
|
||||||
|
|
||||||
|
@ApiModelProperty("在线状态")
|
||||||
|
private String onlineState;
|
||||||
|
|
||||||
|
@ApiModelProperty("角色")
|
||||||
|
private String role;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个店员的业绩快照。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩快照")
|
||||||
|
public class ClerkPerformanceSnapshotVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("店员ID")
|
||||||
|
private String clerkId;
|
||||||
|
|
||||||
|
@ApiModelProperty("店员昵称")
|
||||||
|
private String clerkNickname;
|
||||||
|
|
||||||
|
@ApiModelProperty("头像地址")
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
@ApiModelProperty("店员性别")
|
||||||
|
private String sex;
|
||||||
|
|
||||||
|
@ApiModelProperty("所属组名称")
|
||||||
|
private String groupName;
|
||||||
|
|
||||||
|
@ApiModelProperty("等级名称")
|
||||||
|
private String levelName;
|
||||||
|
|
||||||
|
@ApiModelProperty("GMV(支付金额)")
|
||||||
|
private BigDecimal gmv = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("首单金额")
|
||||||
|
private BigDecimal firstOrderAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("续单金额")
|
||||||
|
private BigDecimal continuedOrderAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("打赏金额")
|
||||||
|
private BigDecimal rewardAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("退款金额")
|
||||||
|
private BigDecimal refundAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("预估工资")
|
||||||
|
private BigDecimal estimatedRevenue = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("订单数量")
|
||||||
|
private Integer orderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("首单数量")
|
||||||
|
private Integer firstOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("续单数量")
|
||||||
|
private Integer continuedOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("退款订单数量")
|
||||||
|
private Integer refundOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("超时未接单数量")
|
||||||
|
private Integer expiredOrderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("用户数量")
|
||||||
|
private Integer userCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("复购用户数量")
|
||||||
|
private Integer continuedUserCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("续单率")
|
||||||
|
private BigDecimal continuedRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("续费率(续单金额/GMV)")
|
||||||
|
private BigDecimal continuedAmountRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("退单率")
|
||||||
|
private BigDecimal refundRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("续客率")
|
||||||
|
private BigDecimal continuedCustomerRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("客单价")
|
||||||
|
private BigDecimal averageTicketPrice = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("在线时长(单位:秒)")
|
||||||
|
private Long onlineDuration = 0L;
|
||||||
|
|
||||||
|
@ApiModelProperty("排行榜名次")
|
||||||
|
private Integer rank;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.starry.admin.modules.statistics.module.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 店员业绩趋势点。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("店员业绩趋势点")
|
||||||
|
public class ClerkPerformanceTrendPointVo {
|
||||||
|
|
||||||
|
@ApiModelProperty("日期")
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
@ApiModelProperty("GMV")
|
||||||
|
private BigDecimal gmv = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("订单数量")
|
||||||
|
private Integer orderCount = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty("续单率")
|
||||||
|
private BigDecimal continuedRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("续费金额")
|
||||||
|
private BigDecimal continuedAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
@ApiModelProperty("首单金额")
|
||||||
|
private BigDecimal firstAmount = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
@@ -4,6 +4,10 @@ import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
|||||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
|
||||||
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
|
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -29,4 +33,8 @@ public interface IPlayClerkPerformanceService {
|
|||||||
PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo,
|
PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo,
|
||||||
List<PlayOrderInfoEntity> orderInfoEntities, List<PlayClerkLevelInfoEntity> clerkLevelInfoEntity,
|
List<PlayOrderInfoEntity> orderInfoEntities, List<PlayClerkLevelInfoEntity> clerkLevelInfoEntity,
|
||||||
List<PlayPersonnelGroupInfoEntity> groupInfoEntities);
|
List<PlayPersonnelGroupInfoEntity> groupInfoEntities);
|
||||||
|
|
||||||
|
ClerkPerformanceOverviewResponseVo queryOverview(ClerkPerformanceOverviewQueryVo vo);
|
||||||
|
|
||||||
|
ClerkPerformanceDetailResponseVo queryDetail(ClerkPerformanceDetailQueryVo vo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,75 @@
|
|||||||
package com.starry.admin.modules.statistics.service.impl;
|
package com.starry.admin.modules.statistics.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.starry.admin.common.exception.ServiceException;
|
||||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
||||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
||||||
|
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||||
|
import com.starry.admin.modules.statistics.module.enums.ClerkPerformanceSortField;
|
||||||
|
import com.starry.admin.modules.statistics.module.enums.SortDirection;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailCompositionVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewSummaryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceProfileVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceTrendPointVo;
|
||||||
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
|
import com.starry.admin.modules.statistics.module.vo.PlayClerkPerformanceInfoReturnVo;
|
||||||
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
|
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
|
||||||
|
import com.starry.admin.utils.SecurityUtils;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author admin
|
* 店员业绩数据服务。
|
||||||
* @since 2024/6/15 下午3:29
|
*/
|
||||||
**/
|
|
||||||
@Service
|
@Service
|
||||||
public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceService {
|
public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceService {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayOrderInfoService playOrderInfoService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayClerkLevelInfoService playClerkLevelInfoService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayPersonnelGroupInfoService playPersonnelGroupInfoService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo,
|
public PlayClerkPerformanceInfoReturnVo getClerkPerformanceInfo(PlayClerkUserInfoEntity userInfo,
|
||||||
List<PlayOrderInfoEntity> orderInfoEntities, List<PlayClerkLevelInfoEntity> clerkLevelInfoEntities,
|
List<PlayOrderInfoEntity> orderInfoEntities, List<PlayClerkLevelInfoEntity> clerkLevelInfoEntities,
|
||||||
List<PlayPersonnelGroupInfoEntity> groupInfoEntities) {
|
List<PlayPersonnelGroupInfoEntity> groupInfoEntities) {
|
||||||
|
|
||||||
Set<String> customIds = new HashSet<>();
|
Set<String> customIds = new HashSet<>();
|
||||||
int orderContinueNumber = 0;
|
int orderContinueNumber = 0;
|
||||||
int orderRefundNumber = 0;
|
int orderRefundNumber = 0;
|
||||||
@@ -36,35 +82,36 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
|||||||
BigDecimal estimatedRevenue = BigDecimal.ZERO;
|
BigDecimal estimatedRevenue = BigDecimal.ZERO;
|
||||||
for (PlayOrderInfoEntity orderInfoEntity : orderInfoEntities) {
|
for (PlayOrderInfoEntity orderInfoEntity : orderInfoEntities) {
|
||||||
customIds.add(orderInfoEntity.getPurchaserBy());
|
customIds.add(orderInfoEntity.getPurchaserBy());
|
||||||
finalAmount = finalAmount.add(orderInfoEntity.getFinalAmount());
|
finalAmount = finalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||||
if ("1".equals(orderInfoEntity.getFirstOrder())) {
|
if ("1".equals(orderInfoEntity.getFirstOrder())) {
|
||||||
orderFirstAmount = orderFirstAmount.add(orderInfoEntity.getFinalAmount());
|
orderFirstAmount = orderFirstAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||||
} else {
|
} else {
|
||||||
orderContinueNumber++;
|
orderContinueNumber++;
|
||||||
orderTotalAmount = orderTotalAmount.add(orderInfoEntity.getFinalAmount());
|
orderTotalAmount = orderTotalAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||||
}
|
}
|
||||||
if ("2".equals(orderInfoEntity.getPlaceType())) {
|
if ("2".equals(orderInfoEntity.getPlaceType())) {
|
||||||
orderRewardAmount = orderRewardAmount.add(orderInfoEntity.getFinalAmount());
|
orderRewardAmount = orderRewardAmount.add(defaultZero(orderInfoEntity.getFinalAmount()));
|
||||||
}
|
}
|
||||||
if ("1".equals(orderInfoEntity.getRefundType())) {
|
if ("1".equals(orderInfoEntity.getRefundType())) {
|
||||||
orderRefundNumber++;
|
orderRefundNumber++;
|
||||||
orderRefundAmount = orderRefundAmount.add(orderInfoEntity.getRefundAmount());
|
orderRefundAmount = orderRefundAmount.add(defaultZero(orderInfoEntity.getRefundAmount()));
|
||||||
}
|
}
|
||||||
if ("1".equals(orderInfoEntity.getOrdersExpiredState())) {
|
if ("1".equals(orderInfoEntity.getOrdersExpiredState())) {
|
||||||
ordersExpiredNumber++;
|
ordersExpiredNumber++;
|
||||||
}
|
}
|
||||||
|
estimatedRevenue = estimatedRevenue.add(defaultZero(orderInfoEntity.getEstimatedRevenue()));
|
||||||
}
|
}
|
||||||
PlayClerkPerformanceInfoReturnVo returnVo = new PlayClerkPerformanceInfoReturnVo();
|
PlayClerkPerformanceInfoReturnVo returnVo = new PlayClerkPerformanceInfoReturnVo();
|
||||||
returnVo.setClerkId(userInfo.getId());
|
returnVo.setClerkId(userInfo.getId());
|
||||||
returnVo.setClerkNickname(userInfo.getNickname());
|
returnVo.setClerkNickname(userInfo.getNickname());
|
||||||
returnVo.setClerkSex(userInfo.getSex());
|
returnVo.setClerkSex(userInfo.getSex());
|
||||||
for (PlayClerkLevelInfoEntity infoEntity : clerkLevelInfoEntities) {
|
for (PlayClerkLevelInfoEntity infoEntity : clerkLevelInfoEntities) {
|
||||||
if (infoEntity.getId().equals(userInfo.getLevelId())) {
|
if (Objects.equals(infoEntity.getId(), userInfo.getLevelId())) {
|
||||||
returnVo.setLevelName(infoEntity.getName());
|
returnVo.setLevelName(infoEntity.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (PlayPersonnelGroupInfoEntity infoEntity : groupInfoEntities) {
|
for (PlayPersonnelGroupInfoEntity infoEntity : groupInfoEntities) {
|
||||||
if (infoEntity.getId().equals(userInfo.getGroupId())) {
|
if (Objects.equals(infoEntity.getId(), userInfo.getGroupId())) {
|
||||||
returnVo.setGroupName(infoEntity.getGroupName());
|
returnVo.setGroupName(infoEntity.getGroupName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +126,456 @@ public class PlayClerkPerformanceServiceImpl implements IPlayClerkPerformanceSer
|
|||||||
returnVo.setOrderRefundAmount(orderRefundAmount);
|
returnVo.setOrderRefundAmount(orderRefundAmount);
|
||||||
returnVo.setCustomNumber(customIds.size());
|
returnVo.setCustomNumber(customIds.size());
|
||||||
returnVo.setEstimatedRevenue(estimatedRevenue);
|
returnVo.setEstimatedRevenue(estimatedRevenue);
|
||||||
|
|
||||||
return returnVo;
|
return returnVo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClerkPerformanceOverviewResponseVo queryOverview(ClerkPerformanceOverviewQueryVo vo) {
|
||||||
|
DateRange range = resolveDateRange(vo.getEndOrderTime());
|
||||||
|
List<PlayClerkUserInfoEntity> clerks = loadAccessibleClerks(vo);
|
||||||
|
ClerkPerformanceOverviewResponseVo responseVo = new ClerkPerformanceOverviewResponseVo();
|
||||||
|
if (CollectionUtil.isEmpty(clerks)) {
|
||||||
|
responseVo.setSummary(new ClerkPerformanceOverviewSummaryVo());
|
||||||
|
responseVo.setRankings(Collections.emptyList());
|
||||||
|
responseVo.setTotal(0);
|
||||||
|
return responseVo;
|
||||||
|
}
|
||||||
|
Map<String, String> levelNameMap = playClerkLevelInfoService.selectAll().stream().collect(
|
||||||
|
Collectors.toMap(PlayClerkLevelInfoEntity::getId, PlayClerkLevelInfoEntity::getName, (a, b) -> a));
|
||||||
|
Map<String, String> groupNameMap = playPersonnelGroupInfoService.selectAll().stream().collect(
|
||||||
|
Collectors.toMap(PlayPersonnelGroupInfoEntity::getId, PlayPersonnelGroupInfoEntity::getGroupName,
|
||||||
|
(a, b) -> a));
|
||||||
|
List<ClerkPerformanceSnapshotVo> snapshots = new ArrayList<>(clerks.size());
|
||||||
|
for (PlayClerkUserInfoEntity clerk : clerks) {
|
||||||
|
List<PlayOrderInfoEntity> orders = playOrderInfoService.clerkSelectOrderInfoList(clerk.getId(),
|
||||||
|
range.startTime, range.endTime);
|
||||||
|
snapshots.add(buildSnapshot(clerk, orders, levelNameMap, groupNameMap));
|
||||||
|
}
|
||||||
|
int total = snapshots.size();
|
||||||
|
ClerkPerformanceOverviewSummaryVo summary = aggregateSummary(snapshots);
|
||||||
|
List<ClerkPerformanceSnapshotVo> sorted = new ArrayList<>(snapshots);
|
||||||
|
applySortAndRank(sorted, vo.getSortField(), vo.getSortDirection());
|
||||||
|
if (vo.getLimit() != null && vo.getLimit() > 0 && vo.getLimit() < sorted.size()) {
|
||||||
|
sorted = new ArrayList<>(sorted.subList(0, vo.getLimit()));
|
||||||
|
applySequentialRank(sorted);
|
||||||
|
}
|
||||||
|
responseVo.setSummary(Boolean.TRUE.equals(vo.getIncludeSummary()) ? summary : null);
|
||||||
|
responseVo.setRankings(Boolean.TRUE.equals(vo.getIncludeRankings()) ? sorted : Collections.emptyList());
|
||||||
|
responseVo.setTotal(total);
|
||||||
|
return responseVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClerkPerformanceDetailResponseVo queryDetail(ClerkPerformanceDetailQueryVo vo) {
|
||||||
|
DateRange range = resolveDateRange(vo.getEndOrderTime());
|
||||||
|
PlayClerkUserInfoEntity clerk = clerkUserInfoService.getById(vo.getClerkId());
|
||||||
|
if (clerk == null) {
|
||||||
|
throw new ServiceException("店员不存在");
|
||||||
|
}
|
||||||
|
List<String> accessibleIds =
|
||||||
|
playPersonnelGroupInfoService.getValidClerkIdList(SecurityUtils.getLoginUser(), null);
|
||||||
|
if (CollectionUtil.isEmpty(accessibleIds) || !accessibleIds.contains(clerk.getId())) {
|
||||||
|
throw new ServiceException("无权查看该店员业绩");
|
||||||
|
}
|
||||||
|
Map<String, String> levelNameMap = playClerkLevelInfoService.selectAll().stream().collect(
|
||||||
|
Collectors.toMap(PlayClerkLevelInfoEntity::getId, PlayClerkLevelInfoEntity::getName, (a, b) -> a));
|
||||||
|
Map<String, String> groupNameMap = playPersonnelGroupInfoService.selectAll().stream().collect(
|
||||||
|
Collectors.toMap(PlayPersonnelGroupInfoEntity::getId, PlayPersonnelGroupInfoEntity::getGroupName,
|
||||||
|
(a, b) -> a));
|
||||||
|
List<PlayOrderInfoEntity> orders = playOrderInfoService.clerkSelectOrderInfoList(clerk.getId(),
|
||||||
|
range.startTime, range.endTime);
|
||||||
|
ClerkPerformanceSnapshotVo snapshot = buildSnapshot(clerk, orders, levelNameMap, groupNameMap);
|
||||||
|
ClerkPerformanceDetailResponseVo responseVo = new ClerkPerformanceDetailResponseVo();
|
||||||
|
responseVo.setProfile(buildProfile(clerk, levelNameMap, groupNameMap));
|
||||||
|
responseVo.setSnapshot(snapshot);
|
||||||
|
responseVo.setComposition(buildComposition(snapshot));
|
||||||
|
if (Boolean.TRUE.equals(vo.getIncludeTrend())) {
|
||||||
|
responseVo.setTrend(buildTrend(orders, range,
|
||||||
|
vo.getTrendDays() == null || vo.getTrendDays() <= 0 ? 7 : vo.getTrendDays()));
|
||||||
|
responseVo.setTrendDimension("DAY");
|
||||||
|
} else {
|
||||||
|
responseVo.setTrend(Collections.emptyList());
|
||||||
|
}
|
||||||
|
return responseVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClerkPerformanceProfileVo buildProfile(PlayClerkUserInfoEntity clerk, Map<String, String> levelNameMap,
|
||||||
|
Map<String, String> groupNameMap) {
|
||||||
|
ClerkPerformanceProfileVo profile = new ClerkPerformanceProfileVo();
|
||||||
|
profile.setClerkId(clerk.getId());
|
||||||
|
profile.setNickname(clerk.getNickname());
|
||||||
|
profile.setAvatar(clerk.getAvatar());
|
||||||
|
profile.setSex(clerk.getSex());
|
||||||
|
profile.setGroupName(groupNameMap.getOrDefault(clerk.getGroupId(), ""));
|
||||||
|
profile.setLevelName(levelNameMap.getOrDefault(clerk.getLevelId(), ""));
|
||||||
|
profile.setListingState(clerk.getListingState());
|
||||||
|
profile.setOnlineState(clerk.getOnlineState());
|
||||||
|
profile.setRole(null);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ClerkPerformanceTrendPointVo> buildTrend(List<PlayOrderInfoEntity> orders, DateRange range,
|
||||||
|
int trendDays) {
|
||||||
|
if (CollectionUtil.isEmpty(orders)) {
|
||||||
|
return buildEmptyTrend(range, trendDays);
|
||||||
|
}
|
||||||
|
LocalDate end = range.endDate;
|
||||||
|
LocalDate start = end.minusDays(trendDays - 1);
|
||||||
|
if (start.isBefore(range.startDate)) {
|
||||||
|
start = range.startDate;
|
||||||
|
}
|
||||||
|
Map<LocalDate, List<PlayOrderInfoEntity>> grouped = orders.stream()
|
||||||
|
.filter(order -> order.getPurchaserTime() != null)
|
||||||
|
.collect(Collectors.groupingBy(order -> order.getPurchaserTime().toLocalDate()));
|
||||||
|
List<ClerkPerformanceTrendPointVo> points = new ArrayList<>();
|
||||||
|
LocalDate cursor = start;
|
||||||
|
while (!cursor.isAfter(end)) {
|
||||||
|
List<PlayOrderInfoEntity> dayOrders = grouped.getOrDefault(cursor, Collections.emptyList());
|
||||||
|
points.add(buildTrendPoint(cursor, dayOrders));
|
||||||
|
cursor = cursor.plusDays(1);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ClerkPerformanceTrendPointVo> buildEmptyTrend(DateRange range, int trendDays) {
|
||||||
|
List<ClerkPerformanceTrendPointVo> points = new ArrayList<>();
|
||||||
|
LocalDate end = range.endDate;
|
||||||
|
LocalDate start = end.minusDays(trendDays - 1);
|
||||||
|
if (start.isBefore(range.startDate)) {
|
||||||
|
start = range.startDate;
|
||||||
|
}
|
||||||
|
LocalDate cursor = start;
|
||||||
|
while (!cursor.isAfter(end)) {
|
||||||
|
points.add(buildTrendPoint(cursor, Collections.emptyList()));
|
||||||
|
cursor = cursor.plusDays(1);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClerkPerformanceTrendPointVo buildTrendPoint(LocalDate date, List<PlayOrderInfoEntity> orders) {
|
||||||
|
ClerkPerformanceTrendPointVo point = new ClerkPerformanceTrendPointVo();
|
||||||
|
point.setDate(date);
|
||||||
|
BigDecimal gmv = BigDecimal.ZERO;
|
||||||
|
BigDecimal continuedAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal firstAmount = BigDecimal.ZERO;
|
||||||
|
int continuedCount = 0;
|
||||||
|
for (PlayOrderInfoEntity order : orders) {
|
||||||
|
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
|
||||||
|
gmv = gmv.add(finalAmount);
|
||||||
|
if ("1".equals(order.getFirstOrder())) {
|
||||||
|
firstAmount = firstAmount.add(finalAmount);
|
||||||
|
} else {
|
||||||
|
continuedAmount = continuedAmount.add(finalAmount);
|
||||||
|
continuedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
point.setGmv(gmv);
|
||||||
|
point.setOrderCount(orders.size());
|
||||||
|
point.setContinuedAmount(continuedAmount);
|
||||||
|
point.setFirstAmount(firstAmount);
|
||||||
|
point.setContinuedRate(calcPercentage(continuedCount, orders.size()));
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClerkPerformanceDetailCompositionVo buildComposition(ClerkPerformanceSnapshotVo snapshot) {
|
||||||
|
ClerkPerformanceDetailCompositionVo composition = new ClerkPerformanceDetailCompositionVo();
|
||||||
|
int orderTotal = snapshot.getOrderCount();
|
||||||
|
BigDecimal gmv = snapshot.getGmv();
|
||||||
|
List<ClerkPerformanceDetailCompositionVo.CompositionSlice> orderSlices = new ArrayList<>();
|
||||||
|
orderSlices.add(createSlice("FIRST_ORDER", "首单", snapshot.getFirstOrderCount(), orderTotal,
|
||||||
|
snapshot.getFirstOrderAmount(), gmv));
|
||||||
|
orderSlices.add(createSlice("CONTINUED_ORDER", "续单", snapshot.getContinuedOrderCount(), orderTotal,
|
||||||
|
snapshot.getContinuedOrderAmount(), gmv));
|
||||||
|
orderSlices.add(createSlice("REFUND_ORDER", "退款", snapshot.getRefundOrderCount(), orderTotal,
|
||||||
|
snapshot.getRefundAmount(), gmv));
|
||||||
|
orderSlices.add(createSlice("EXPIRED_ORDER", "超时", snapshot.getExpiredOrderCount(), orderTotal,
|
||||||
|
BigDecimal.ZERO, gmv));
|
||||||
|
composition.setOrderComposition(orderSlices);
|
||||||
|
List<ClerkPerformanceDetailCompositionVo.CompositionSlice> amountSlices = new ArrayList<>();
|
||||||
|
amountSlices.add(createSlice("FIRST_AMOUNT", "首单金额", 0, orderTotal, snapshot.getFirstOrderAmount(), gmv));
|
||||||
|
amountSlices.add(
|
||||||
|
createSlice("CONTINUED_AMOUNT", "续单金额", 0, orderTotal, snapshot.getContinuedOrderAmount(), gmv));
|
||||||
|
amountSlices.add(createSlice("REWARD_AMOUNT", "打赏金额", 0, orderTotal, snapshot.getRewardAmount(), gmv));
|
||||||
|
amountSlices.add(createSlice("REFUND_AMOUNT", "退款金额", 0, orderTotal, snapshot.getRefundAmount(), gmv));
|
||||||
|
composition.setAmountComposition(amountSlices);
|
||||||
|
int continuedCustomers = snapshot.getContinuedUserCount();
|
||||||
|
int totalCustomers = snapshot.getUserCount();
|
||||||
|
int newCustomers = Math.max(totalCustomers - continuedCustomers, 0);
|
||||||
|
List<ClerkPerformanceDetailCompositionVo.CompositionSlice> customerSlices = new ArrayList<>();
|
||||||
|
customerSlices.add(createSlice("NEW_CUSTOMER", "新顾客", newCustomers, totalCustomers, BigDecimal.ZERO, gmv));
|
||||||
|
customerSlices
|
||||||
|
.add(createSlice("CONTINUED_CUSTOMER", "复购顾客", continuedCustomers, totalCustomers, BigDecimal.ZERO,
|
||||||
|
gmv));
|
||||||
|
composition.setCustomerComposition(customerSlices);
|
||||||
|
return composition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClerkPerformanceDetailCompositionVo.CompositionSlice createSlice(String key, String label, int count,
|
||||||
|
int totalCount, BigDecimal amount, BigDecimal totalAmount) {
|
||||||
|
ClerkPerformanceDetailCompositionVo.CompositionSlice slice =
|
||||||
|
new ClerkPerformanceDetailCompositionVo.CompositionSlice();
|
||||||
|
slice.setKey(key);
|
||||||
|
slice.setLabel(label);
|
||||||
|
slice.setCount(count);
|
||||||
|
slice.setCountRatio(calcPercentage(count, totalCount));
|
||||||
|
slice.setAmount(amount);
|
||||||
|
slice.setAmountRatio(calcPercentage(amount, totalAmount));
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySortAndRank(List<ClerkPerformanceSnapshotVo> snapshots, ClerkPerformanceSortField sortField,
|
||||||
|
SortDirection direction) {
|
||||||
|
if (CollectionUtil.isEmpty(snapshots)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Comparator<ClerkPerformanceSnapshotVo> comparator;
|
||||||
|
if (sortField == null) {
|
||||||
|
sortField = ClerkPerformanceSortField.GMV;
|
||||||
|
}
|
||||||
|
switch (sortField) {
|
||||||
|
case ORDER_COUNT:
|
||||||
|
comparator = Comparator.comparing(ClerkPerformanceSnapshotVo::getOrderCount, Comparator.nullsLast(Integer::compareTo));
|
||||||
|
break;
|
||||||
|
case CONTINUE_RATE:
|
||||||
|
comparator = Comparator.comparing(ClerkPerformanceSnapshotVo::getContinuedRate, Comparator.nullsLast(BigDecimal::compareTo));
|
||||||
|
break;
|
||||||
|
case ESTIMATED_REVENUE:
|
||||||
|
comparator = Comparator.comparing(ClerkPerformanceSnapshotVo::getEstimatedRevenue, Comparator.nullsLast(BigDecimal::compareTo));
|
||||||
|
break;
|
||||||
|
case GMV:
|
||||||
|
default:
|
||||||
|
comparator = Comparator.comparing(ClerkPerformanceSnapshotVo::getGmv, Comparator.nullsLast(BigDecimal::compareTo));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
comparator = comparator.thenComparing(ClerkPerformanceSnapshotVo::getClerkNickname,
|
||||||
|
Comparator.nullsFirst(String::compareTo));
|
||||||
|
if (direction == null) {
|
||||||
|
direction = SortDirection.DEFAULT;
|
||||||
|
}
|
||||||
|
if (direction == SortDirection.DESC) {
|
||||||
|
comparator = comparator.reversed();
|
||||||
|
}
|
||||||
|
snapshots.sort(comparator);
|
||||||
|
applySequentialRank(snapshots);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySequentialRank(List<ClerkPerformanceSnapshotVo> snapshots) {
|
||||||
|
for (int i = 0; i < snapshots.size(); i++) {
|
||||||
|
snapshots.get(i).setRank(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClerkPerformanceOverviewSummaryVo aggregateSummary(List<ClerkPerformanceSnapshotVo> snapshots) {
|
||||||
|
ClerkPerformanceOverviewSummaryVo summary = new ClerkPerformanceOverviewSummaryVo();
|
||||||
|
if (CollectionUtil.isEmpty(snapshots)) {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
BigDecimal totalGmv = BigDecimal.ZERO;
|
||||||
|
BigDecimal continuedAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal refundAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal estimatedRevenue = BigDecimal.ZERO;
|
||||||
|
int orderTotal = 0;
|
||||||
|
int firstOrderTotal = 0;
|
||||||
|
int continuedOrderTotal = 0;
|
||||||
|
int refundOrderTotal = 0;
|
||||||
|
int userTotal = 0;
|
||||||
|
int continuedUserTotal = 0;
|
||||||
|
for (ClerkPerformanceSnapshotVo snapshot : snapshots) {
|
||||||
|
totalGmv = totalGmv.add(defaultZero(snapshot.getGmv()));
|
||||||
|
continuedAmount = continuedAmount.add(defaultZero(snapshot.getContinuedOrderAmount()));
|
||||||
|
refundAmount = refundAmount.add(defaultZero(snapshot.getRefundAmount()));
|
||||||
|
estimatedRevenue = estimatedRevenue.add(defaultZero(snapshot.getEstimatedRevenue()));
|
||||||
|
orderTotal += snapshot.getOrderCount();
|
||||||
|
firstOrderTotal += snapshot.getFirstOrderCount();
|
||||||
|
continuedOrderTotal += snapshot.getContinuedOrderCount();
|
||||||
|
refundOrderTotal += snapshot.getRefundOrderCount();
|
||||||
|
userTotal += snapshot.getUserCount();
|
||||||
|
continuedUserTotal += snapshot.getContinuedUserCount();
|
||||||
|
}
|
||||||
|
summary.setTotalGmv(totalGmv);
|
||||||
|
summary.setTotalOrderCount(orderTotal);
|
||||||
|
summary.setTotalFirstOrderCount(firstOrderTotal);
|
||||||
|
summary.setTotalContinuedOrderCount(continuedOrderTotal);
|
||||||
|
summary.setTotalRefundOrderCount(refundOrderTotal);
|
||||||
|
summary.setTotalEstimatedRevenue(estimatedRevenue);
|
||||||
|
summary.setTotalUserCount(userTotal);
|
||||||
|
summary.setTotalContinuedUserCount(continuedUserTotal);
|
||||||
|
summary.setContinuedRate(calcPercentage(continuedOrderTotal, orderTotal));
|
||||||
|
summary.setContinuedAmountRate(calcPercentage(continuedAmount, totalGmv));
|
||||||
|
summary.setRefundRate(calcPercentage(refundOrderTotal, orderTotal));
|
||||||
|
summary.setAverageTicketPrice(calcAverage(totalGmv, orderTotal));
|
||||||
|
summary.setContinuedCustomerRate(calcPercentage(continuedUserTotal, userTotal));
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClerkPerformanceSnapshotVo buildSnapshot(PlayClerkUserInfoEntity clerk, List<PlayOrderInfoEntity> orders,
|
||||||
|
Map<String, String> levelNameMap, Map<String, String> groupNameMap) {
|
||||||
|
ClerkPerformanceSnapshotVo snapshot = new ClerkPerformanceSnapshotVo();
|
||||||
|
snapshot.setClerkId(clerk.getId());
|
||||||
|
snapshot.setClerkNickname(clerk.getNickname());
|
||||||
|
snapshot.setAvatar(clerk.getAvatar());
|
||||||
|
snapshot.setSex(clerk.getSex());
|
||||||
|
snapshot.setGroupName(groupNameMap.getOrDefault(clerk.getGroupId(), ""));
|
||||||
|
snapshot.setLevelName(levelNameMap.getOrDefault(clerk.getLevelId(), ""));
|
||||||
|
BigDecimal gmv = BigDecimal.ZERO;
|
||||||
|
BigDecimal firstAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal continuedAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal rewardAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal refundAmount = BigDecimal.ZERO;
|
||||||
|
BigDecimal estimatedRevenue = BigDecimal.ZERO;
|
||||||
|
int firstCount = 0;
|
||||||
|
int continuedCount = 0;
|
||||||
|
int refundCount = 0;
|
||||||
|
int expiredCount = 0;
|
||||||
|
Map<String, Integer> userOrderMap = new HashMap<>();
|
||||||
|
for (PlayOrderInfoEntity order : orders) {
|
||||||
|
BigDecimal finalAmount = defaultZero(order.getFinalAmount());
|
||||||
|
gmv = gmv.add(finalAmount);
|
||||||
|
userOrderMap.merge(order.getPurchaserBy(), 1, Integer::sum);
|
||||||
|
if ("1".equals(order.getFirstOrder())) {
|
||||||
|
firstCount++;
|
||||||
|
firstAmount = firstAmount.add(finalAmount);
|
||||||
|
} else {
|
||||||
|
continuedCount++;
|
||||||
|
continuedAmount = continuedAmount.add(finalAmount);
|
||||||
|
}
|
||||||
|
if ("2".equals(order.getPlaceType())) {
|
||||||
|
rewardAmount = rewardAmount.add(finalAmount);
|
||||||
|
}
|
||||||
|
if ("1".equals(order.getRefundType())) {
|
||||||
|
refundCount++;
|
||||||
|
refundAmount = refundAmount.add(defaultZero(order.getRefundAmount()));
|
||||||
|
}
|
||||||
|
if ("1".equals(order.getOrdersExpiredState())) {
|
||||||
|
expiredCount++;
|
||||||
|
}
|
||||||
|
estimatedRevenue = estimatedRevenue.add(defaultZero(order.getEstimatedRevenue()));
|
||||||
|
}
|
||||||
|
int orderCount = orders.size();
|
||||||
|
int userCount = userOrderMap.size();
|
||||||
|
int continuedUserCount = (int) userOrderMap.values().stream().filter(cnt -> cnt > 1).count();
|
||||||
|
snapshot.setGmv(gmv);
|
||||||
|
snapshot.setFirstOrderAmount(firstAmount);
|
||||||
|
snapshot.setContinuedOrderAmount(continuedAmount);
|
||||||
|
snapshot.setRewardAmount(rewardAmount);
|
||||||
|
snapshot.setRefundAmount(refundAmount);
|
||||||
|
snapshot.setEstimatedRevenue(estimatedRevenue);
|
||||||
|
snapshot.setOrderCount(orderCount);
|
||||||
|
snapshot.setFirstOrderCount(firstCount);
|
||||||
|
snapshot.setContinuedOrderCount(continuedCount);
|
||||||
|
snapshot.setRefundOrderCount(refundCount);
|
||||||
|
snapshot.setExpiredOrderCount(expiredCount);
|
||||||
|
snapshot.setUserCount(userCount);
|
||||||
|
snapshot.setContinuedUserCount(continuedUserCount);
|
||||||
|
snapshot.setContinuedRate(calcPercentage(continuedCount, orderCount));
|
||||||
|
snapshot.setContinuedAmountRate(calcPercentage(continuedAmount, gmv));
|
||||||
|
snapshot.setRefundRate(calcPercentage(refundCount, orderCount));
|
||||||
|
snapshot.setContinuedCustomerRate(calcPercentage(continuedUserCount, userCount));
|
||||||
|
snapshot.setAverageTicketPrice(calcAverage(gmv, orderCount));
|
||||||
|
snapshot.setOnlineDuration(0L);
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PlayClerkUserInfoEntity> loadAccessibleClerks(ClerkPerformanceOverviewQueryVo vo) {
|
||||||
|
List<String> accessibleIds =
|
||||||
|
playPersonnelGroupInfoService.getValidClerkIdList(SecurityUtils.getLoginUser(), null);
|
||||||
|
if (CollectionUtil.isEmpty(accessibleIds)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
LambdaQueryWrapper<PlayClerkUserInfoEntity> wrapper =
|
||||||
|
Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).in(PlayClerkUserInfoEntity::getId, accessibleIds)
|
||||||
|
.eq(PlayClerkUserInfoEntity::getClerkState, "1");
|
||||||
|
if (StrUtil.isNotBlank(vo.getClerkId())) {
|
||||||
|
wrapper.eq(PlayClerkUserInfoEntity::getId, vo.getClerkId());
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(vo.getGroupId())) {
|
||||||
|
wrapper.eq(PlayClerkUserInfoEntity::getGroupId, vo.getGroupId());
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(vo.getSex())) {
|
||||||
|
wrapper.eq(PlayClerkUserInfoEntity::getSex, vo.getSex());
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(vo.getListingState())) {
|
||||||
|
wrapper.eq(PlayClerkUserInfoEntity::getListingState, vo.getListingState());
|
||||||
|
}
|
||||||
|
return clerkUserInfoService.list(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateRange resolveDateRange(List<String> endOrderTime) {
|
||||||
|
String startStr;
|
||||||
|
String endStr;
|
||||||
|
if (CollectionUtil.isNotEmpty(endOrderTime) && endOrderTime.size() >= 2) {
|
||||||
|
startStr = normalizeStart(endOrderTime.get(0));
|
||||||
|
endStr = normalizeEnd(endOrderTime.get(1));
|
||||||
|
} else {
|
||||||
|
LocalDate end = LocalDate.now();
|
||||||
|
LocalDate start = end.minusDays(6);
|
||||||
|
startStr = start.format(DATE_FORMATTER) + " 00:00:00";
|
||||||
|
endStr = end.format(DATE_FORMATTER) + " 23:59:59";
|
||||||
|
}
|
||||||
|
LocalDate startDate = LocalDate.parse(startStr.substring(0, 10), DATE_FORMATTER);
|
||||||
|
LocalDate endDate = LocalDate.parse(endStr.substring(0, 10), DATE_FORMATTER);
|
||||||
|
return new DateRange(startStr, endStr, startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeStart(String raw) {
|
||||||
|
if (StrUtil.isBlank(raw)) {
|
||||||
|
return LocalDate.now().minusDays(6).format(DATE_FORMATTER) + " 00:00:00";
|
||||||
|
}
|
||||||
|
if (raw.length() > 10) {
|
||||||
|
LocalDateTime.parse(raw, DATE_TIME_FORMATTER);
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw + " 00:00:00";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeEnd(String raw) {
|
||||||
|
if (StrUtil.isBlank(raw)) {
|
||||||
|
return LocalDate.now().format(DATE_FORMATTER) + " 23:59:59";
|
||||||
|
}
|
||||||
|
if (raw.length() > 10) {
|
||||||
|
LocalDateTime.parse(raw, DATE_TIME_FORMATTER);
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw + " 23:59:59";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calcPercentage(int numerator, int denominator) {
|
||||||
|
if (denominator <= 0) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
return BigDecimal.valueOf(numerator).multiply(BigDecimal.valueOf(100))
|
||||||
|
.divide(BigDecimal.valueOf(denominator), 2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calcPercentage(BigDecimal numerator, BigDecimal denominator) {
|
||||||
|
if (denominator == null || BigDecimal.ZERO.compareTo(denominator) == 0 || numerator == null) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
return numerator.multiply(BigDecimal.valueOf(100)).divide(denominator, 2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calcAverage(BigDecimal numerator, int denominator) {
|
||||||
|
if (denominator <= 0 || numerator == null) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
return numerator.divide(BigDecimal.valueOf(denominator), 2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal defaultZero(BigDecimal value) {
|
||||||
|
return value == null ? BigDecimal.ZERO : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class DateRange {
|
||||||
|
private final String startTime;
|
||||||
|
private final String endTime;
|
||||||
|
private final LocalDate startDate;
|
||||||
|
private final LocalDate endDate;
|
||||||
|
|
||||||
|
private DateRange(String startTime, String endTime, LocalDate startDate, LocalDate endDate) {
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.endTime = endTime;
|
||||||
|
this.startDate = startDate;
|
||||||
|
this.endDate = endDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
package com.starry.admin.modules.statistics.controller;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailCompositionVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewSummaryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceProfileVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceTrendPointVo;
|
||||||
|
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
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;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MockMvc tests for {@link PlayClerkPerformanceController} verifying new overview/detail endpoints.
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PlayClerkPerformanceControllerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayClerkPerformanceService playClerkPerformanceService;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PlayClerkPerformanceController controller;
|
||||||
|
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.findAndRegisterModules();
|
||||||
|
mockMvc = MockMvcBuilders.standaloneSetup(controller)
|
||||||
|
.setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("POST /statistics/performance/overview should delegate and wrap service response")
|
||||||
|
void overviewEndpointReturnsAggregatedData() throws Exception {
|
||||||
|
ClerkPerformanceSnapshotVo snapshot = new ClerkPerformanceSnapshotVo();
|
||||||
|
snapshot.setClerkId("c1");
|
||||||
|
snapshot.setClerkNickname("Alice");
|
||||||
|
snapshot.setGmv(new BigDecimal("300.00"));
|
||||||
|
snapshot.setOrderCount(5);
|
||||||
|
snapshot.setContinuedRate(new BigDecimal("60.00"));
|
||||||
|
|
||||||
|
ClerkPerformanceOverviewSummaryVo summary = new ClerkPerformanceOverviewSummaryVo();
|
||||||
|
summary.setTotalGmv(new BigDecimal("300.00"));
|
||||||
|
summary.setTotalOrderCount(5);
|
||||||
|
summary.setContinuedRate(new BigDecimal("60.00"));
|
||||||
|
|
||||||
|
ClerkPerformanceOverviewResponseVo responseVo = new ClerkPerformanceOverviewResponseVo();
|
||||||
|
responseVo.setSummary(summary);
|
||||||
|
responseVo.setRankings(Collections.singletonList(snapshot));
|
||||||
|
responseVo.setTotal(1);
|
||||||
|
|
||||||
|
when(playClerkPerformanceService.queryOverview(any())).thenReturn(responseVo);
|
||||||
|
|
||||||
|
String payload = "{\"endOrderTime\":[\"2024-08-01 00:00:00\",\"2024-08-07 23:59:59\"],\"limit\":3}";
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post("/statistics/performance/overview")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.success").value(true))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.rankings", hasSize(1)))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.rankings[0].clerkId").value("c1"))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.summary.totalGmv").value(300.00))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.total").value(1));
|
||||||
|
|
||||||
|
ArgumentCaptor<ClerkPerformanceOverviewQueryVo> captor = ArgumentCaptor
|
||||||
|
.forClass(ClerkPerformanceOverviewQueryVo.class);
|
||||||
|
verify(playClerkPerformanceService).queryOverview(captor.capture());
|
||||||
|
ClerkPerformanceOverviewQueryVo captured = captor.getValue();
|
||||||
|
assertEquals(Integer.valueOf(3), captured.getLimit());
|
||||||
|
assertEquals(Arrays.asList("2024-08-01 00:00:00", "2024-08-07 23:59:59"), captured.getEndOrderTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("POST /statistics/performance/detail should return snapshot and trend data")
|
||||||
|
void detailEndpointReturnsSnapshot() throws Exception {
|
||||||
|
ClerkPerformanceProfileVo profile = new ClerkPerformanceProfileVo();
|
||||||
|
profile.setClerkId("c1");
|
||||||
|
profile.setNickname("Alice");
|
||||||
|
profile.setGroupName("一组");
|
||||||
|
|
||||||
|
ClerkPerformanceSnapshotVo snapshotVo = new ClerkPerformanceSnapshotVo();
|
||||||
|
snapshotVo.setClerkId("c1");
|
||||||
|
snapshotVo.setGmv(new BigDecimal("260.00"));
|
||||||
|
snapshotVo.setOrderCount(3);
|
||||||
|
snapshotVo.setContinuedRate(new BigDecimal("66.67"));
|
||||||
|
|
||||||
|
ClerkPerformanceDetailCompositionVo composition = new ClerkPerformanceDetailCompositionVo();
|
||||||
|
ClerkPerformanceDetailCompositionVo.CompositionSlice slice = new ClerkPerformanceDetailCompositionVo.CompositionSlice();
|
||||||
|
slice.setKey("FIRST_ORDER");
|
||||||
|
slice.setLabel("首单");
|
||||||
|
slice.setCount(1);
|
||||||
|
slice.setCountRatio(new BigDecimal("33.33"));
|
||||||
|
composition.setOrderComposition(Collections.singletonList(slice));
|
||||||
|
|
||||||
|
ClerkPerformanceTrendPointVo trendPoint = new ClerkPerformanceTrendPointVo();
|
||||||
|
trendPoint.setDate(LocalDate.of(2024, 8, 1));
|
||||||
|
trendPoint.setGmv(new BigDecimal("120.00"));
|
||||||
|
|
||||||
|
ClerkPerformanceDetailResponseVo detailResponse = new ClerkPerformanceDetailResponseVo();
|
||||||
|
detailResponse.setProfile(profile);
|
||||||
|
detailResponse.setSnapshot(snapshotVo);
|
||||||
|
detailResponse.setComposition(composition);
|
||||||
|
detailResponse.setTrend(Collections.singletonList(trendPoint));
|
||||||
|
detailResponse.setTrendDimension("DAY");
|
||||||
|
|
||||||
|
when(playClerkPerformanceService.queryDetail(any())).thenReturn(detailResponse);
|
||||||
|
|
||||||
|
String payload = "{\"clerkId\":\"c1\",\"endOrderTime\":[\"2024-08-01 00:00:00\",\"2024-08-03 23:59:59\"],\"includeTrend\":true}";
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post("/statistics/performance/detail")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.success").value(true))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.profile.clerkId").value("c1"))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.snapshot.gmv").value(260.00))
|
||||||
|
.andExpect(MockMvcResultMatchers.jsonPath("$.data.trend[0].gmv").value(120.00));
|
||||||
|
|
||||||
|
ArgumentCaptor<ClerkPerformanceDetailQueryVo> captor = ArgumentCaptor
|
||||||
|
.forClass(ClerkPerformanceDetailQueryVo.class);
|
||||||
|
verify(playClerkPerformanceService).queryDetail(captor.capture());
|
||||||
|
ClerkPerformanceDetailQueryVo captured = captor.getValue();
|
||||||
|
assertTrue(captured.getIncludeTrend());
|
||||||
|
assertEquals("c1", captured.getClerkId());
|
||||||
|
assertEquals(Arrays.asList("2024-08-01 00:00:00", "2024-08-03 23:59:59"), captured.getEndOrderTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
package com.starry.admin.modules.statistics.service;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import com.starry.admin.common.domain.LoginUser;
|
||||||
|
import com.starry.admin.common.exception.ServiceException;
|
||||||
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
||||||
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||||
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
|
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
||||||
|
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQueryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewSummaryVo;
|
||||||
|
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
|
||||||
|
import com.starry.admin.modules.statistics.service.impl.PlayClerkPerformanceServiceImpl;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.Month;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link PlayClerkPerformanceServiceImpl} covering aggregation, ranking and detail logic.
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PlayClerkPerformanceServiceImplTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayOrderInfoService playOrderInfoService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayClerkLevelInfoService playClerkLevelInfoService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayPersonnelGroupInfoService playPersonnelGroupInfoService;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PlayClerkPerformanceServiceImpl service;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("queryOverview should aggregate metrics and sort clerks by GMV")
|
||||||
|
void queryOverviewAggregatesAndSorts() {
|
||||||
|
ClerkPerformanceOverviewQueryVo vo = new ClerkPerformanceOverviewQueryVo();
|
||||||
|
vo.setEndOrderTime(Arrays.asList("2024-08-01 00:00:00", "2024-08-07 23:59:59"));
|
||||||
|
vo.setIncludeSummary(true);
|
||||||
|
vo.setIncludeRankings(true);
|
||||||
|
|
||||||
|
PlayClerkUserInfoEntity clerkA = buildClerk("c1", "Alice", "g1", "l1");
|
||||||
|
PlayClerkUserInfoEntity clerkB = buildClerk("c2", "Bob", "g2", "l2");
|
||||||
|
|
||||||
|
when(playPersonnelGroupInfoService.getValidClerkIdList(any(), any())).thenReturn(Arrays.asList("c1", "c2"));
|
||||||
|
when(clerkUserInfoService.list((Wrapper<PlayClerkUserInfoEntity>) any())).thenReturn(Arrays.asList(clerkA, clerkB));
|
||||||
|
when(playClerkLevelInfoService.selectAll()).thenReturn(Arrays.asList(level("l1", "白银"), level("l2", "黄金")));
|
||||||
|
when(playPersonnelGroupInfoService.selectAll())
|
||||||
|
.thenReturn(Arrays.asList(group("g1", "一组"), group("g2", "二组")));
|
||||||
|
|
||||||
|
List<PlayOrderInfoEntity> ordersA = Arrays.asList(
|
||||||
|
order("c1", "userA", "1", "0", "0", new BigDecimal("100.00"), new BigDecimal("60.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 1, 10, 0)),
|
||||||
|
order("c1", "userA", "0", "2", "0", new BigDecimal("150.00"), new BigDecimal("90.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 2, 14, 0)),
|
||||||
|
withRefund(order("c1", "userB", "0", "0", "1", new BigDecimal("50.00"), new BigDecimal("30.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 3, 9, 0)), new BigDecimal("30.00")));
|
||||||
|
|
||||||
|
List<PlayOrderInfoEntity> ordersB = Collections.singletonList(
|
||||||
|
order("c2", "userC", "1", "0", "0", new BigDecimal("80.00"), new BigDecimal("50.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 1, 12, 0)));
|
||||||
|
|
||||||
|
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c1"), anyString(), anyString())).thenReturn(ordersA);
|
||||||
|
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c2"), anyString(), anyString())).thenReturn(ordersB);
|
||||||
|
|
||||||
|
setAuthentication();
|
||||||
|
try {
|
||||||
|
ClerkPerformanceOverviewResponseVo response = service.queryOverview(vo);
|
||||||
|
|
||||||
|
assertNotNull(response.getSummary(), "summary should be present when includeSummary=true");
|
||||||
|
assertEquals(2, response.getTotal());
|
||||||
|
assertEquals(2, response.getRankings().size());
|
||||||
|
|
||||||
|
ClerkPerformanceSnapshotVo top = response.getRankings().get(0);
|
||||||
|
assertEquals("c1", top.getClerkId(), "Highest GMV clerk should rank first");
|
||||||
|
assertEquals(new BigDecimal("300.00"), top.getGmv());
|
||||||
|
assertEquals(new BigDecimal("100.00"), top.getFirstOrderAmount());
|
||||||
|
assertEquals(new BigDecimal("200.00"), top.getContinuedOrderAmount());
|
||||||
|
assertEquals(new BigDecimal("150.00"), top.getRewardAmount());
|
||||||
|
assertEquals(new BigDecimal("30.00"), top.getRefundAmount());
|
||||||
|
assertEquals(3, top.getOrderCount());
|
||||||
|
assertEquals(2, top.getContinuedOrderCount());
|
||||||
|
assertEquals(new BigDecimal("66.67"), top.getContinuedRate());
|
||||||
|
assertEquals(new BigDecimal("100.00"), top.getAverageTicketPrice());
|
||||||
|
|
||||||
|
ClerkPerformanceOverviewSummaryVo summary = response.getSummary();
|
||||||
|
assertEquals(new BigDecimal("380.00"), summary.getTotalGmv());
|
||||||
|
assertEquals(4, summary.getTotalOrderCount());
|
||||||
|
assertEquals(2, summary.getTotalFirstOrderCount());
|
||||||
|
assertEquals(2, summary.getTotalContinuedOrderCount());
|
||||||
|
assertEquals(new BigDecimal("50.00"), summary.getContinuedRate());
|
||||||
|
assertEquals(new BigDecimal("95.00"), summary.getAverageTicketPrice());
|
||||||
|
} finally {
|
||||||
|
clearAuthentication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("queryDetail should build snapshot, composition and trend for accessible clerk")
|
||||||
|
void queryDetailBuildsSnapshotCompositionAndTrend() {
|
||||||
|
ClerkPerformanceDetailQueryVo vo = new ClerkPerformanceDetailQueryVo();
|
||||||
|
vo.setClerkId("c1");
|
||||||
|
vo.setEndOrderTime(Arrays.asList("2024-08-01 00:00:00", "2024-08-03 23:59:59"));
|
||||||
|
vo.setIncludeTrend(true);
|
||||||
|
vo.setTrendDays(3);
|
||||||
|
|
||||||
|
PlayClerkUserInfoEntity clerk = buildClerk("c1", "Alice", "g1", "l1");
|
||||||
|
|
||||||
|
when(playPersonnelGroupInfoService.getValidClerkIdList(any(), any())).thenReturn(Collections.singletonList("c1"));
|
||||||
|
when(clerkUserInfoService.getById("c1")).thenReturn(clerk);
|
||||||
|
when(playClerkLevelInfoService.selectAll()).thenReturn(Collections.singletonList(level("l1", "白银")));
|
||||||
|
when(playPersonnelGroupInfoService.selectAll()).thenReturn(Collections.singletonList(group("g1", "一组")));
|
||||||
|
|
||||||
|
List<PlayOrderInfoEntity> orders = Arrays.asList(
|
||||||
|
order("c1", "userA", "1", "0", "0", new BigDecimal("120.00"), new BigDecimal("70.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 1, 9, 0)),
|
||||||
|
order("c1", "userA", "0", "0", "0", new BigDecimal("80.00"), new BigDecimal("40.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 2, 10, 0)),
|
||||||
|
withRefund(order("c1", "userB", "0", "2", "1", new BigDecimal("60.00"), new BigDecimal("30.00"),
|
||||||
|
LocalDateTime.of(2024, Month.AUGUST, 2, 18, 0)), new BigDecimal("20.00")));
|
||||||
|
when(playOrderInfoService.clerkSelectOrderInfoList(eq("c1"), anyString(), anyString())).thenReturn(orders);
|
||||||
|
|
||||||
|
setAuthentication();
|
||||||
|
try {
|
||||||
|
ClerkPerformanceDetailResponseVo response = service.queryDetail(vo);
|
||||||
|
assertNotNull(response.getProfile());
|
||||||
|
assertEquals("Alice", response.getProfile().getNickname());
|
||||||
|
assertEquals("一组", response.getProfile().getGroupName());
|
||||||
|
assertNotNull(response.getSnapshot());
|
||||||
|
assertEquals(new BigDecimal("260.00"), response.getSnapshot().getGmv());
|
||||||
|
assertEquals(3, response.getSnapshot().getOrderCount());
|
||||||
|
assertEquals(new BigDecimal("66.67"), response.getSnapshot().getContinuedRate());
|
||||||
|
assertEquals(new BigDecimal("86.67"), response.getSnapshot().getAverageTicketPrice());
|
||||||
|
|
||||||
|
assertNotNull(response.getComposition());
|
||||||
|
assertEquals(4, response.getComposition().getOrderComposition().size());
|
||||||
|
assertEquals(new BigDecimal("33.33"), response.getComposition().getOrderComposition().get(0).getCountRatio());
|
||||||
|
|
||||||
|
assertEquals(3, response.getTrend().size());
|
||||||
|
assertEquals("DAY", response.getTrendDimension());
|
||||||
|
assertEquals(new BigDecimal("120.00"), response.getTrend().get(0).getGmv());
|
||||||
|
assertEquals(new BigDecimal("140.00"), response.getTrend().get(1).getGmv());
|
||||||
|
} finally {
|
||||||
|
clearAuthentication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("queryDetail should reject access when clerk not under current user")
|
||||||
|
void queryDetailShouldEnforcePermissions() {
|
||||||
|
ClerkPerformanceDetailQueryVo vo = new ClerkPerformanceDetailQueryVo();
|
||||||
|
vo.setClerkId("c99");
|
||||||
|
vo.setEndOrderTime(Arrays.asList("2024-08-01 00:00:00", "2024-08-02 23:59:59"));
|
||||||
|
|
||||||
|
when(playPersonnelGroupInfoService.getValidClerkIdList(any(), any())).thenReturn(Collections.singletonList("c1"));
|
||||||
|
when(clerkUserInfoService.getById("c99")).thenReturn(buildClerk("c99", "Ghost", "g1", "l1"));
|
||||||
|
|
||||||
|
setAuthentication();
|
||||||
|
try {
|
||||||
|
ServiceException ex = assertThrows(ServiceException.class, () -> service.queryDetail(vo));
|
||||||
|
assertTrue(ex.getMessage().contains("无权"));
|
||||||
|
} finally {
|
||||||
|
clearAuthentication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayClerkUserInfoEntity buildClerk(String id, String name, String groupId, String levelId) {
|
||||||
|
PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
|
||||||
|
entity.setId(id);
|
||||||
|
entity.setNickname(name);
|
||||||
|
entity.setGroupId(groupId);
|
||||||
|
entity.setLevelId(levelId);
|
||||||
|
entity.setSex("1");
|
||||||
|
entity.setListingState("1");
|
||||||
|
entity.setOnlineState("1");
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayClerkLevelInfoEntity level(String id, String name) {
|
||||||
|
PlayClerkLevelInfoEntity level = new PlayClerkLevelInfoEntity();
|
||||||
|
level.setId(id);
|
||||||
|
level.setName(name);
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayPersonnelGroupInfoEntity group(String id, String name) {
|
||||||
|
PlayPersonnelGroupInfoEntity entity = new PlayPersonnelGroupInfoEntity();
|
||||||
|
entity.setId(id);
|
||||||
|
entity.setGroupName(name);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayOrderInfoEntity order(String clerkId, String purchaser, String firstOrder, String placeType,
|
||||||
|
String refundType, BigDecimal finalAmount, BigDecimal estimatedRevenue, LocalDateTime purchaserTime) {
|
||||||
|
PlayOrderInfoEntity order = new PlayOrderInfoEntity();
|
||||||
|
order.setAcceptBy(clerkId);
|
||||||
|
order.setPurchaserBy(purchaser);
|
||||||
|
order.setFirstOrder(firstOrder);
|
||||||
|
order.setPlaceType(placeType);
|
||||||
|
order.setRefundType(refundType);
|
||||||
|
order.setFinalAmount(finalAmount);
|
||||||
|
order.setEstimatedRevenue(estimatedRevenue);
|
||||||
|
order.setOrdersExpiredState("1".equals(refundType) ? "1" : "0");
|
||||||
|
order.setPurchaserTime(purchaserTime);
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAuthentication() {
|
||||||
|
LoginUser loginUser = new LoginUser();
|
||||||
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
|
new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAuthentication() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayOrderInfoEntity withRefund(PlayOrderInfoEntity order, BigDecimal refundAmount) {
|
||||||
|
order.setRefundAmount(refundAmount);
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user