Commit 1a232f80ec97caf4e845f82135d0d6b32101d4f6

Authored by YunaiV
2 parents b3296b4e 86ff0347

Merge remote-tracking branch 'origin/master-jdk17' into master-jdk17

Showing 58 changed files with 2269 additions and 76 deletions
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/performance/CrmPerformanceConfigController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.performance;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigRespVO;
  9 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigSaveReqVO;
  10 +import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
  11 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
  12 +import cn.iocoder.yudao.module.crm.service.performance.CrmPerformanceConfigService;
  13 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  14 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  15 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  16 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  17 +import io.swagger.v3.oas.annotations.Operation;
  18 +import io.swagger.v3.oas.annotations.Parameter;
  19 +import io.swagger.v3.oas.annotations.tags.Tag;
  20 +import jakarta.annotation.Resource;
  21 +import jakarta.validation.Valid;
  22 +import org.springframework.security.access.prepost.PreAuthorize;
  23 +import org.springframework.validation.annotation.Validated;
  24 +import org.springframework.web.bind.annotation.*;
  25 +
  26 +import java.util.List;
  27 +import java.util.Map;
  28 +import java.util.Objects;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  33 +import static java.util.Collections.emptyList;
  34 +import static java.util.Collections.singletonList;
  35 +
  36 +@Tag(name = "管理后台 - CRM 业绩目标设置")
  37 +@RestController
  38 +@RequestMapping("/crm/performance-config")
  39 +@Validated
  40 +public class CrmPerformanceConfigController {
  41 +
  42 + @Resource
  43 + private CrmPerformanceConfigService performanceConfigService;
  44 +
  45 + @Resource
  46 + private DeptApi deptApi;
  47 + @Resource
  48 + private AdminUserApi adminUserApi;
  49 +
  50 + @PostMapping("/create")
  51 + @Operation(summary = "创建业绩目标")
  52 + @PreAuthorize("@ss.hasPermission('crm:performance-config:create')")
  53 + public CommonResult<Long> createPerformanceConfig(@Valid @RequestBody CrmPerformanceConfigSaveReqVO createReqVO) {
  54 + return success(performanceConfigService.createPerformanceConfig(createReqVO));
  55 + }
  56 +
  57 + @PutMapping("/update")
  58 + @Operation(summary = "更新业绩目标")
  59 + @PreAuthorize("@ss.hasPermission('crm:performance-config:update')")
  60 + public CommonResult<Boolean> updatePerformanceConfig(@Valid @RequestBody CrmPerformanceConfigSaveReqVO updateReqVO) {
  61 + performanceConfigService.updatePerformanceConfig(updateReqVO);
  62 + return success(true);
  63 + }
  64 +
  65 + @DeleteMapping("/delete")
  66 + @Operation(summary = "删除业绩目标")
  67 + @Parameter(name = "id", description = "编号", required = true)
  68 + @PreAuthorize("@ss.hasPermission('crm:performance-config:delete')")
  69 + public CommonResult<Boolean> deletePerformanceConfig(@RequestParam("id") Long id) {
  70 + performanceConfigService.deletePerformanceConfig(id);
  71 + return success(true);
  72 + }
  73 +
  74 + @GetMapping("/get")
  75 + @Operation(summary = "获得业绩目标")
  76 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  77 + @PreAuthorize("@ss.hasPermission('crm:performance-config:query')")
  78 + public CommonResult<CrmPerformanceConfigRespVO> getPerformanceConfig(@RequestParam("id") Long id) {
  79 + // 1. 获取业绩目标
  80 + CrmPerformanceConfigDO performanceConfig = performanceConfigService.getPerformanceConfig(id);
  81 + // 2. 拼接数据
  82 + return success(buildPerformanceConfigDetail(performanceConfig));
  83 + }
  84 +
  85 + private CrmPerformanceConfigRespVO buildPerformanceConfigDetail(CrmPerformanceConfigDO performanceConfig) {
  86 + if (performanceConfig == null) {
  87 + return null;
  88 + }
  89 + // 1. 转换成 VO
  90 + CrmPerformanceConfigRespVO respVO = BeanUtils.toBean(performanceConfig, CrmPerformanceConfigRespVO.class);
  91 + // 2. 拼接目标对象名称
  92 + return buildPerformanceConfigObjectName(respVO);
  93 + }
  94 +
  95 + @GetMapping("/page")
  96 + @Operation(summary = "获得业绩目标分页")
  97 + @PreAuthorize("@ss.hasPermission('crm:performance-config:query')")
  98 + public CommonResult<PageResult<CrmPerformanceConfigRespVO>> getPerformanceConfigPage(@Valid CrmPerformanceConfigPageReqVO pageReqVO) {
  99 + // 1. 查询业绩目标分页
  100 + PageResult<CrmPerformanceConfigDO> pageResult = performanceConfigService.getPerformanceConfigPage(pageReqVO);
  101 + if (CollUtil.isEmpty(pageResult.getList())) {
  102 + return success(PageResult.empty(pageResult.getTotal()));
  103 + }
  104 + // 2. 拼接数据
  105 + return success(BeanUtils.toBean(pageResult, CrmPerformanceConfigRespVO.class)
  106 + .setList(buildPerformanceConfigDetailList(pageResult.getList())));
  107 + }
  108 +
  109 + private List<CrmPerformanceConfigRespVO> buildPerformanceConfigDetailList(List<CrmPerformanceConfigDO> list) {
  110 + if (CollUtil.isEmpty(list)) {
  111 + return emptyList();
  112 + }
  113 + // 1. 转换成 VO
  114 + List<CrmPerformanceConfigRespVO> voList = BeanUtils.toBean(list, CrmPerformanceConfigRespVO.class);
  115 + // 2. 拼接目标对象名称
  116 + return buildPerformanceConfigObjectNameList(voList);
  117 + }
  118 +
  119 + // ========== VO 拼装 ==========
  120 +
  121 + private CrmPerformanceConfigRespVO buildPerformanceConfigObjectName(CrmPerformanceConfigRespVO config) {
  122 + if (config == null) {
  123 + return null;
  124 + }
  125 + return buildPerformanceConfigObjectNameList(singletonList(config)).get(0);
  126 + }
  127 +
  128 + private List<CrmPerformanceConfigRespVO> buildPerformanceConfigObjectNameList(List<CrmPerformanceConfigRespVO> list) {
  129 + if (CollUtil.isEmpty(list)) {
  130 + return emptyList();
  131 + }
  132 + // 1.1 获取部门列表
  133 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, CrmPerformanceConfigRespVO::getObjectId,
  134 + item -> Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType())));
  135 + // 1.2 获取员工列表
  136 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(list, CrmPerformanceConfigRespVO::getObjectId,
  137 + item -> Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.USER.getObjectType())));
  138 + // 2. 设置目标对象名称
  139 + list.forEach(item -> {
  140 + if (Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType())) {
  141 + findAndThen(deptMap, item.getObjectId(), dept -> item.setObjectName(dept.getName()));
  142 + } else if (Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.USER.getObjectType())) {
  143 + findAndThen(userMap, item.getObjectId(), user -> item.setObjectName(user.getNickname()));
  144 + }
  145 + });
  146 + return list;
  147 + }
  148 +
  149 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/performance/vo/config/CrmPerformanceConfigPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
  6 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +
  12 +@Schema(description = "管理后台 - CRM 业绩目标设置分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class CrmPerformanceConfigPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "目标对象编号", example = "1")
  19 + private Long objectId;
  20 +
  21 + @Schema(description = "目标对象类型", example = "2")
  22 + @InEnum(value = CrmPerformanceConfigObjectTypeEnum.class, message = "目标对象类型,必须是 {value}")
  23 + private Integer objectType;
  24 +
  25 + @Schema(description = "年份", example = "2026")
  26 + private Integer year;
  27 +
  28 + @Schema(description = "目标类型", example = "5")
  29 + @InEnum(value = CrmPerformanceConfigBizTypeEnum.class, message = "目标类型,必须是 {value}")
  30 + private Integer bizType;
  31 +
  32 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/performance/vo/config/CrmPerformanceConfigRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - CRM 业绩目标设置 Response VO")
  10 +@Data
  11 +public class CrmPerformanceConfigRespVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + private Long id;
  15 +
  16 + @Schema(description = "目标对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + private Long objectId;
  18 +
  19 + @Schema(description = "目标对象名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "销售部")
  20 + private String objectName;
  21 +
  22 + @Schema(description = "目标对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  23 + private Integer objectType;
  24 +
  25 + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
  26 + private Integer year;
  27 +
  28 + @Schema(description = "一月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + private BigDecimal januaryTargetPrice;
  30 +
  31 + @Schema(description = "二月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + private BigDecimal februaryTargetPrice;
  33 +
  34 + @Schema(description = "三月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private BigDecimal marchTargetPrice;
  36 +
  37 + @Schema(description = "四月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private BigDecimal aprilTargetPrice;
  39 +
  40 + @Schema(description = "五月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + private BigDecimal mayTargetPrice;
  42 +
  43 + @Schema(description = "六月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  44 + private BigDecimal juneTargetPrice;
  45 +
  46 + @Schema(description = "七月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  47 + private BigDecimal julyTargetPrice;
  48 +
  49 + @Schema(description = "八月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  50 + private BigDecimal augustTargetPrice;
  51 +
  52 + @Schema(description = "九月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  53 + private BigDecimal septemberTargetPrice;
  54 +
  55 + @Schema(description = "十月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  56 + private BigDecimal octoberTargetPrice;
  57 +
  58 + @Schema(description = "十一月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  59 + private BigDecimal novemberTargetPrice;
  60 +
  61 + @Schema(description = "十二月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  62 + private BigDecimal decemberTargetPrice;
  63 +
  64 + @Schema(description = "目标类型",
  65 + requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  66 + private Integer bizType;
  67 +
  68 + @Schema(description = "年度目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
  69 + private BigDecimal yearTargetPrice;
  70 +
  71 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  72 + private LocalDateTime createTime;
  73 +
  74 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/performance/vo/config/CrmPerformanceConfigSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +
  10 +import java.math.BigDecimal;
  11 +
  12 +@Schema(description = "管理后台 - CRM 业绩目标设置新增/修改 Request VO")
  13 +@Data
  14 +public class CrmPerformanceConfigSaveReqVO {
  15 +
  16 + @Schema(description = "编号", example = "1")
  17 + private Long id;
  18 +
  19 + @Schema(description = "目标对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  20 + @NotNull(message = "目标对象编号不能为空")
  21 + private Long objectId;
  22 +
  23 + @Schema(description = "目标对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  24 + @NotNull(message = "目标对象类型不能为空")
  25 + @InEnum(value = CrmPerformanceConfigObjectTypeEnum.class, message = "目标对象类型,必须是 {value}")
  26 + private Integer objectType;
  27 +
  28 + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
  29 + @NotNull(message = "年份不能为空")
  30 + private Integer year;
  31 +
  32 + @Schema(description = "一月目标金额")
  33 + private BigDecimal januaryTargetPrice;
  34 +
  35 + @Schema(description = "二月目标金额")
  36 + private BigDecimal februaryTargetPrice;
  37 +
  38 + @Schema(description = "三月目标金额")
  39 + private BigDecimal marchTargetPrice;
  40 +
  41 + @Schema(description = "四月目标金额")
  42 + private BigDecimal aprilTargetPrice;
  43 +
  44 + @Schema(description = "五月目标金额")
  45 + private BigDecimal mayTargetPrice;
  46 +
  47 + @Schema(description = "六月目标金额")
  48 + private BigDecimal juneTargetPrice;
  49 +
  50 + @Schema(description = "七月目标金额")
  51 + private BigDecimal julyTargetPrice;
  52 +
  53 + @Schema(description = "八月目标金额")
  54 + private BigDecimal augustTargetPrice;
  55 +
  56 + @Schema(description = "九月目标金额")
  57 + private BigDecimal septemberTargetPrice;
  58 +
  59 + @Schema(description = "十月目标金额")
  60 + private BigDecimal octoberTargetPrice;
  61 +
  62 + @Schema(description = "十一月目标金额")
  63 + private BigDecimal novemberTargetPrice;
  64 +
  65 + @Schema(description = "十二月目标金额")
  66 + private BigDecimal decemberTargetPrice;
  67 +
  68 + @Schema(description = "目标类型",
  69 + requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  70 + @NotNull(message = "目标类型不能为空")
  71 + @InEnum(value = CrmPerformanceConfigBizTypeEnum.class, message = "目标类型,必须是 {value}")
  72 + private Integer bizType;
  73 +
  74 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsFunnelController.java
@@ -45,6 +45,13 @@ public class CrmStatisticsFunnelController { @@ -45,6 +45,13 @@ public class CrmStatisticsFunnelController {
45 return success(funnelService.getBusinessSummaryByEndStatus(reqVO)); 45 return success(funnelService.getBusinessSummaryByEndStatus(reqVO));
46 } 46 }
47 47
  48 + @GetMapping("/get-business-summary-by-status")
  49 + @Operation(summary = "获取商机阶段统计", description = "用于【销售漏斗】页面的【销售漏斗分析】")
  50 + @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
  51 + public CommonResult<List<CrmStatisticsBusinessSummaryByStatusRespVO>> getBusinessSummaryByStatus(@Valid CrmStatisticsFunnelReqVO reqVO) {
  52 + return success(funnelService.getBusinessSummaryByStatus(reqVO));
  53 + }
  54 +
48 @GetMapping("/get-business-summary-by-date") 55 @GetMapping("/get-business-summary-by-date")
49 @Operation(summary = "获取新增商机分析(按日期)", description = "用于【销售漏斗】页面") 56 @Operation(summary = "获取新增商机分析(按日期)", description = "用于【销售漏斗】页面")
50 @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')") 57 @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceSummaryRespVO;
6 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceService; 7 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceService;
7 import io.swagger.v3.oas.annotations.Operation; 8 import io.swagger.v3.oas.annotations.Operation;
8 import io.swagger.v3.oas.annotations.tags.Tag; 9 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -49,4 +50,11 @@ public class CrmStatisticsPerformanceController { @@ -49,4 +50,11 @@ public class CrmStatisticsPerformanceController {
49 return success(performanceService.getReceivablePricePerformance(performanceReqVO)); 50 return success(performanceService.getReceivablePricePerformance(performanceReqVO));
50 } 51 }
51 52
  53 + @GetMapping("/get-contract-summary")
  54 + @Operation(summary = "合同汇总表")
  55 + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')")
  56 + public CommonResult<List<CrmStatisticsPerformanceSummaryRespVO>> getContractSummary(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {
  57 + return success(performanceService.getContractSummary(performanceReqVO));
  58 + }
  59 +
52 } 60 }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceTargetController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetRespVO;
  6 +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceTargetService;
  7 +import io.swagger.v3.oas.annotations.Operation;
  8 +import io.swagger.v3.oas.annotations.tags.Tag;
  9 +import jakarta.annotation.Resource;
  10 +import jakarta.validation.Valid;
  11 +import org.springframework.security.access.prepost.PreAuthorize;
  12 +import org.springframework.validation.annotation.Validated;
  13 +import org.springframework.web.bind.annotation.GetMapping;
  14 +import org.springframework.web.bind.annotation.RequestMapping;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import java.util.List;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  20 +
  21 +@Tag(name = "管理后台 - CRM 业绩目标统计")
  22 +@RestController
  23 +@RequestMapping("/crm/statistics-performance-target")
  24 +@Validated
  25 +public class CrmStatisticsPerformanceTargetController {
  26 +
  27 + @Resource
  28 + private CrmStatisticsPerformanceTargetService performanceTargetService;
  29 +
  30 + @GetMapping("/get-performance-target-summary")
  31 + @Operation(summary = "获取业绩目标完成情况")
  32 + @PreAuthorize("@ss.hasPermission('crm:statistics-performance-target:query')")
  33 + public CommonResult<List<CrmStatisticsPerformanceTargetRespVO>> getPerformanceTargetSummary(@Valid CrmStatisticsPerformanceTargetReqVO reqVO) {
  34 + return success(performanceTargetService.getPerformanceTargetSummary(reqVO));
  35 + }
  36 +
  37 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsProductController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
  7 +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsProductService;
  8 +import io.swagger.v3.oas.annotations.Operation;
  9 +import io.swagger.v3.oas.annotations.tags.Tag;
  10 +import jakarta.annotation.Resource;
  11 +import jakarta.validation.Valid;
  12 +import org.springframework.security.access.prepost.PreAuthorize;
  13 +import org.springframework.validation.annotation.Validated;
  14 +import org.springframework.web.bind.annotation.GetMapping;
  15 +import org.springframework.web.bind.annotation.RequestMapping;
  16 +import org.springframework.web.bind.annotation.RestController;
  17 +
  18 +import java.util.List;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +@Tag(name = "管理后台 - CRM 产品统计")
  23 +@RestController
  24 +@RequestMapping("/crm/statistics-product")
  25 +@Validated
  26 +public class CrmStatisticsProductController {
  27 +
  28 + @Resource
  29 + private CrmStatisticsProductService productService;
  30 +
  31 + @GetMapping("/get-product-sales-list")
  32 + @Operation(summary = "获取产品销售情况统计")
  33 + @PreAuthorize("@ss.hasPermission('crm:statistics-product:query')")
  34 + public CommonResult<List<CrmStatisticsProductSalesRespVO>> getProductSalesList(@Valid CrmStatisticsProductReqVO reqVO) {
  35 + return success(productService.getProductSalesList(reqVO));
  36 + }
  37 +
  38 + @GetMapping("/get-product-category-summary")
  39 + @Operation(summary = "获取产品分类销售分析")
  40 + @PreAuthorize("@ss.hasPermission('crm:statistics-product:query')")
  41 + public CommonResult<List<CrmStatisticsProductCategoryRespVO>> getProductCategorySummary(@Valid CrmStatisticsProductReqVO reqVO) {
  42 + return success(productService.getProductCategorySummary(reqVO));
  43 + }
  44 +
  45 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByStatusRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +import java.math.BigDecimal;
  9 +
  10 +@Schema(description = "管理后台 - CRM 商机阶段统计 Response VO")
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +@Data
  14 +public class CrmStatisticsBusinessSummaryByStatusRespVO {
  15 +
  16 + @Schema(description = "商机阶段编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + private Long statusId;
  18 +
  19 + @Schema(description = "商机阶段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "初步接洽")
  20 + private String statusName;
  21 +
  22 + @Schema(description = "赢单率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
  23 + private Integer statusPercent;
  24 +
  25 + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Integer sort;
  27 +
  28 + @Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  29 + private Long businessCount;
  30 +
  31 + @Schema(description = "商机总金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  32 + private BigDecimal totalPrice;
  33 +
  34 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsFunnelReqVO.java
@@ -28,6 +28,9 @@ public class CrmStatisticsFunnelReqVO extends PageParam { @@ -28,6 +28,9 @@ public class CrmStatisticsFunnelReqVO extends PageParam {
28 @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") 28 @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
29 private Long userId; 29 private Long userId;
30 30
  31 + @Schema(description = "商机状态组编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
  32 + private Long statusTypeId;
  33 +
31 /** 34 /**
32 * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 35 * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
33 * 后续,可能会支持选择部分用户进行查询 36 * 后续,可能会支持选择部分用户进行查询
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceSummaryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +
  8 +@Schema(description = "管理后台 - CRM 员工业绩合同汇总 Response VO")
  9 +@Data
  10 +public class CrmStatisticsPerformanceSummaryRespVO {
  11 +
  12 + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
  13 + private String time;
  14 +
  15 + @Schema(description = "合同数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  16 + private Integer contractCount;
  17 +
  18 + @Schema(description = "合同金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00")
  19 + private BigDecimal contractPrice;
  20 +
  21 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00")
  22 + private BigDecimal receivablePrice;
  23 +
  24 + @Schema(description = "未回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000.00")
  25 + private BigDecimal unreceivedPrice;
  26 +
  27 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceTargetActualRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.experimental.Accessors;
  6 +
  7 +import java.math.BigDecimal;
  8 +
  9 +@Schema(description = "管理后台 - CRM 业绩目标完成实际值 Response VO")
  10 +@Data
  11 +@Accessors(chain = true)
  12 +public class CrmStatisticsPerformanceTargetActualRespVO {
  13 +
  14 + @Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + private Integer month;
  16 +
  17 + @Schema(description = "完成金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00")
  18 + private BigDecimal currentPrice;
  19 +
  20 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceTargetReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +import java.util.List;
  10 +
  11 +@Schema(description = "管理后台 - CRM 业绩目标完成情况 Request VO")
  12 +@Data
  13 +public class CrmStatisticsPerformanceTargetReqVO {
  14 +
  15 + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  16 + @NotNull(message = "部门 id 不能为空")
  17 + private Long deptId;
  18 +
  19 + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
  20 + private Long userId;
  21 +
  22 + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
  23 + private List<Long> userIds;
  24 +
  25 + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024")
  26 + @NotNull(message = "年份不能为空")
  27 + private Integer year;
  28 +
  29 + @Schema(description = "目标类型",
  30 + requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  31 + @NotNull(message = "目标类型不能为空")
  32 + @InEnum(value = CrmPerformanceConfigBizTypeEnum.class, message = "目标类型,必须是 {value}")
  33 + private Integer bizType;
  34 +
  35 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceTargetRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.experimental.Accessors;
  6 +
  7 +import java.math.BigDecimal;
  8 +
  9 +@Schema(description = "管理后台 - CRM 业绩目标完成情况 Response VO")
  10 +@Data
  11 +@Accessors(chain = true)
  12 +public class CrmStatisticsPerformanceTargetRespVO {
  13 +
  14 + @Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + private Integer month;
  16 +
  17 + @Schema(description = "目标金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00")
  18 + private BigDecimal targetPrice;
  19 +
  20 + @Schema(description = "完成金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00")
  21 + private BigDecimal currentPrice;
  22 +
  23 + @Schema(description = "完成率", requiredMode = Schema.RequiredMode.REQUIRED, example = "80.00")
  24 + private BigDecimal completionRate;
  25 +
  26 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/product/CrmStatisticsProductCategoryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +
  8 +@Schema(description = "管理后台 - CRM 产品分类销售分析 Response VO")
  9 +@Data
  10 +public class CrmStatisticsProductCategoryRespVO {
  11 +
  12 + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  13 + private Long categoryId;
  14 +
  15 + @Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "软件")
  16 + private String categoryName;
  17 +
  18 + @Schema(description = "合同数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  19 + private Integer contractCount;
  20 +
  21 + @Schema(description = "销售数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  22 + private BigDecimal productCount;
  23 +
  24 + @Schema(description = "销售金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20000.00")
  25 + private BigDecimal productTotalPrice;
  26 +
  27 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/product/CrmStatisticsProductReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +import java.util.List;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@Schema(description = "管理后台 - CRM 产品分析 Request VO")
  16 +@Data
  17 +public class CrmStatisticsProductReqVO {
  18 +
  19 + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  20 + @NotNull(message = "部门 id 不能为空")
  21 + private Long deptId;
  22 +
  23 + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
  24 + private Long userId;
  25 +
  26 + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
  27 + private List<Long> userIds;
  28 +
  29 + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED)
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + @NotEmpty(message = "请选择时间范围")
  32 + @Size(min = 2, max = 2, message = "请选择时间范围")
  33 + private LocalDateTime[] times;
  34 +
  35 + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "10")
  36 + private Long categoryId;
  37 +
  38 + @Schema(description = "产品分类编号集合", hidden = true, example = "10")
  39 + private List<Long> categoryIds;
  40 +
  41 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "20")
  42 + private Long productId;
  43 +
  44 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/product/CrmStatisticsProductSalesRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +
  8 +@Schema(description = "管理后台 - CRM 产品销售情况 Response VO")
  9 +@Data
  10 +public class CrmStatisticsProductSalesRespVO {
  11 +
  12 + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  13 + private Long categoryId;
  14 +
  15 + @Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "软件")
  16 + private String categoryName;
  17 +
  18 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  19 + private Long productId;
  20 +
  21 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CRM 专业版")
  22 + private String productName;
  23 +
  24 + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
  25 + private Long contractId;
  26 +
  27 + @Schema(description = "合同编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "HT20240101001")
  28 + private String contractNo;
  29 +
  30 + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CRM 采购合同")
  31 + private String contractName;
  32 +
  33 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  34 + private Long ownerUserId;
  35 +
  36 + @Schema(description = "负责人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
  37 + private String ownerUserName;
  38 +
  39 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "40")
  40 + private Long customerId;
  41 +
  42 + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试客户")
  43 + private String customerName;
  44 +
  45 + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  46 + private BigDecimal productPrice;
  47 +
  48 + @Schema(description = "销售数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  49 + private BigDecimal productCount;
  50 +
  51 + @Schema(description = "销售金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "200.00")
  52 + private BigDecimal productTotalPrice;
  53 +
  54 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/performance/CrmPerformanceConfigDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.dataobject.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
  6 +import com.baomidou.mybatisplus.annotation.KeySequence;
  7 +import com.baomidou.mybatisplus.annotation.TableId;
  8 +import com.baomidou.mybatisplus.annotation.TableName;
  9 +import lombok.*;
  10 +
  11 +import java.math.BigDecimal;
  12 +
  13 +/**
  14 + * CRM 业绩目标 DO
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@TableName("crm_performance_config")
  19 +@KeySequence("crm_performance_config_seq")
  20 +@Data
  21 +@EqualsAndHashCode(callSuper = true)
  22 +@ToString(callSuper = true)
  23 +@Builder
  24 +@NoArgsConstructor
  25 +@AllArgsConstructor
  26 +public class CrmPerformanceConfigDO extends BaseDO {
  27 +
  28 + /**
  29 + * 编号
  30 + */
  31 + @TableId
  32 + private Long id;
  33 + /**
  34 + * 目标类型
  35 + *
  36 + * 枚举 {@link CrmPerformanceConfigBizTypeEnum}
  37 + */
  38 + private Integer bizType;
  39 + /**
  40 + * 目标对象编号
  41 + */
  42 + private Long objectId;
  43 + /**
  44 + * 目标对象类型
  45 + *
  46 + * 枚举 {@link CrmPerformanceConfigObjectTypeEnum}
  47 + */
  48 + private Integer objectType;
  49 +
  50 + /**
  51 + * 年份
  52 + */
  53 + private Integer year;
  54 + /**
  55 + * 年度目标金额
  56 + */
  57 + private BigDecimal yearTargetPrice;
  58 + /**
  59 + * 一月目标金额
  60 + */
  61 + private BigDecimal januaryTargetPrice;
  62 + /**
  63 + * 二月目标金额
  64 + */
  65 + private BigDecimal februaryTargetPrice;
  66 + /**
  67 + * 三月目标金额
  68 + */
  69 + private BigDecimal marchTargetPrice;
  70 + /**
  71 + * 四月目标金额
  72 + */
  73 + private BigDecimal aprilTargetPrice;
  74 + /**
  75 + * 五月目标金额
  76 + */
  77 + private BigDecimal mayTargetPrice;
  78 + /**
  79 + * 六月目标金额
  80 + */
  81 + private BigDecimal juneTargetPrice;
  82 + /**
  83 + * 七月目标金额
  84 + */
  85 + private BigDecimal julyTargetPrice;
  86 + /**
  87 + * 八月目标金额
  88 + */
  89 + private BigDecimal augustTargetPrice;
  90 + /**
  91 + * 九月目标金额
  92 + */
  93 + private BigDecimal septemberTargetPrice;
  94 + /**
  95 + * 十月目标金额
  96 + */
  97 + private BigDecimal octoberTargetPrice;
  98 + /**
  99 + * 十一月目标金额
  100 + */
  101 + private BigDecimal novemberTargetPrice;
  102 + /**
  103 + * 十二月目标金额
  104 + */
  105 + private BigDecimal decemberTargetPrice;
  106 +
  107 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmOwnerRecordDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.dataobject.permission;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  5 +import com.baomidou.mybatisplus.annotation.KeySequence;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.*;
  9 +
  10 +/**
  11 + * CRM 负责人变更记录 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("crm_owner_record")
  16 +@KeySequence("crm_owner_record_seq")
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class CrmOwnerRecordDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * CRM 业务类型
  32 + *
  33 + * 枚举 {@link CrmBizTypeEnum}
  34 + */
  35 + private Integer bizType;
  36 + /**
  37 + * CRM 业务编号
  38 + */
  39 + private Long bizId;
  40 + /**
  41 + * 变更前负责人
  42 + */
  43 + private Long preOwnerUserId;
  44 + /**
  45 + * 变更后负责人
  46 + */
  47 + private Long postOwnerUserId;
  48 +
  49 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
@@ -72,6 +72,7 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; { @@ -72,6 +72,7 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
72 default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) { 72 default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
73 return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>() 73 return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
74 .in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds()) 74 .in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
  75 + .eqIfPresent(CrmBusinessDO::getStatusTypeId, pageVO.getStatusTypeId())
75 .betweenIfPresent(CrmBusinessDO::getCreateTime, pageVO.getTimes())); 76 .betweenIfPresent(CrmBusinessDO::getCreateTime, pageVO.getTimes()));
76 } 77 }
77 78
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/performance/CrmPerformanceConfigMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * CRM 业绩目标配置 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface CrmPerformanceConfigMapper extends BaseMapperX<CrmPerformanceConfigDO> {
  20 +
  21 + default PageResult<CrmPerformanceConfigDO> selectPage(CrmPerformanceConfigPageReqVO reqVO) {
  22 + return selectPage(reqVO, new LambdaQueryWrapperX<CrmPerformanceConfigDO>()
  23 + .eqIfPresent(CrmPerformanceConfigDO::getObjectId, reqVO.getObjectId())
  24 + .eqIfPresent(CrmPerformanceConfigDO::getObjectType, reqVO.getObjectType())
  25 + .eqIfPresent(CrmPerformanceConfigDO::getYear, reqVO.getYear())
  26 + .eqIfPresent(CrmPerformanceConfigDO::getBizType, reqVO.getBizType())
  27 + .orderByDesc(CrmPerformanceConfigDO::getYear)
  28 + .orderByAsc(CrmPerformanceConfigDO::getObjectType)
  29 + .orderByAsc(CrmPerformanceConfigDO::getObjectId)
  30 + .orderByAsc(CrmPerformanceConfigDO::getBizType));
  31 + }
  32 +
  33 + default CrmPerformanceConfigDO selectByObjectIdAndObjectTypeAndYearAndBizType(Long objectId, Integer objectType,
  34 + Integer year, Integer bizType) {
  35 + return selectOne(new LambdaQueryWrapperX<CrmPerformanceConfigDO>()
  36 + .eq(CrmPerformanceConfigDO::getObjectId, objectId)
  37 + .eq(CrmPerformanceConfigDO::getObjectType, objectType)
  38 + .eq(CrmPerformanceConfigDO::getYear, year)
  39 + .eq(CrmPerformanceConfigDO::getBizType, bizType));
  40 + }
  41 +
  42 + default List<CrmPerformanceConfigDO> selectListByObjectIdsAndObjectTypeAndYearAndBizType(Collection<Long> objectIds,
  43 + Integer objectType,
  44 + Integer year,
  45 + Integer bizType) {
  46 + return selectList(new LambdaQueryWrapperX<CrmPerformanceConfigDO>()
  47 + .in(CrmPerformanceConfigDO::getObjectId, objectIds)
  48 + .eq(CrmPerformanceConfigDO::getObjectType, objectType)
  49 + .eq(CrmPerformanceConfigDO::getYear, year)
  50 + .eq(CrmPerformanceConfigDO::getBizType, bizType));
  51 + }
  52 +
  53 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmOwnerRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.permission;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmOwnerRecordDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * CRM 负责人变更记录 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface CrmOwnerRecordMapper extends BaseMapperX<CrmOwnerRecordDO> {
  19 +
  20 + /**
  21 + * 进入公海客户数(按日期)
  22 + *
  23 + * @param reqVO 请求参数
  24 + * @return 统计数据
  25 + */
  26 + List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO);
  27 +
  28 + /**
  29 + * 公海领取客户数(按日期)
  30 + *
  31 + * @param reqVO 请求参数
  32 + * @return 统计数据
  33 + */
  34 + List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO);
  35 +
  36 + /**
  37 + * 进入公海客户数(按用户)
  38 + *
  39 + * @param reqVO 请求参数
  40 + * @return 统计数据
  41 + */
  42 + List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO);
  43 +
  44 + /**
  45 + * 公海领取客户数(按用户)
  46 + *
  47 + * @param reqVO 请求参数
  48 + * @return 统计数据
  49 + */
  50 + List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO);
  51 +
  52 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
1 package cn.iocoder.yudao.module.crm.dal.mysql.statistics; 1 package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
2 2
3 -import cn.hutool.core.date.LocalDateTimeUtil;  
4 -import cn.hutool.core.util.RandomUtil;  
5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
6 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Mapper;
7 5
8 -import java.time.LocalDateTime;  
9 -import java.util.ArrayList;  
10 import java.util.List; 6 import java.util.List;
11 7
12 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
13 -  
14 /** 8 /**
15 * CRM 客户分析 Mapper 9 * CRM 客户分析 Mapper
16 * 10 *
@@ -118,65 +112,6 @@ public interface CrmStatisticsCustomerMapper { @@ -118,65 +112,6 @@ public interface CrmStatisticsCustomerMapper {
118 112
119 113
120 /** 114 /**
121 - * 进入公海客户数(按日期)  
122 - *  
123 - * @param reqVO 请求参数  
124 - * @return 统计数据  
125 - */  
126 - // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表  
127 - default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO) {  
128 - LocalDateTime currrentDate = LocalDateTimeUtil.beginOfDay(reqVO.getTimes()[0]);  
129 - LocalDateTime endDate = LocalDateTimeUtil.endOfDay(reqVO.getTimes()[1]);  
130 - List<CrmStatisticsPoolSummaryByDateRespVO> voList = new ArrayList<>();  
131 - while (currrentDate.isBefore(endDate)) {  
132 - voList.add(new CrmStatisticsPoolSummaryByDateRespVO()  
133 - .setTime(LocalDateTimeUtil.format(currrentDate, "yyyy-MM-dd"))  
134 - .setCustomerPutCount(RandomUtil.randomInt(0, 10))  
135 - .setCustomerTakeCount(RandomUtil.randomInt(0, 10)));  
136 - currrentDate = currrentDate.plusDays(1);  
137 - }  
138 -  
139 - return voList;  
140 - }  
141 -  
142 - /**  
143 - * 公海领取客户数(按日期)  
144 - *  
145 - * @param reqVO 请求参数  
146 - * @return 统计数据  
147 - */  
148 - // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表  
149 - default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO) {  
150 - return selectPoolCustomerPutCountByDate(reqVO);  
151 - }  
152 -  
153 - /**  
154 - * 进入公海客户数(按用户)  
155 - *  
156 - * @param reqVO 请求参数  
157 - * @return 统计数据  
158 - */  
159 - // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表  
160 - default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO) {  
161 - return convertList(reqVO.getUserIds(), userId ->  
162 - (CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO()  
163 - .setCustomerPutCount(RandomUtil.randomInt(0, 10))  
164 - .setCustomerTakeCount(RandomUtil.randomInt(0, 10))  
165 - .setOwnerUserId(userId));  
166 - }  
167 -  
168 - /**  
169 - * 公海领取客户数(按用户)  
170 - *  
171 - * @param reqVO 请求参数  
172 - * @return 统计数据  
173 - */  
174 - // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表  
175 - default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO) {  
176 - return selectPoolCustomerPutCountByUser(reqVO);  
177 - }  
178 -  
179 - /**  
180 * 客户成交周期(按日期) 115 * 客户成交周期(按日期)
181 * 116 *
182 * @param reqVO 请求参数 117 * @param reqVO 请求参数
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsFunnelMapper.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessInversionRateSummaryByDateRespVO; 3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessInversionRateSummaryByDateRespVO;
4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByStatusRespVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
7 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
8 import org.apache.ibatis.annotations.Param; 9 import org.apache.ibatis.annotations.Param;
@@ -23,6 +24,8 @@ public interface CrmStatisticsFunnelMapper { @@ -23,6 +24,8 @@ public interface CrmStatisticsFunnelMapper {
23 24
24 List<CrmStatisticsBusinessSummaryByEndStatusRespVO> selectBusinessSummaryListGroupByEndStatus(CrmStatisticsFunnelReqVO reqVO); 25 List<CrmStatisticsBusinessSummaryByEndStatusRespVO> selectBusinessSummaryListGroupByEndStatus(CrmStatisticsFunnelReqVO reqVO);
25 26
  27 + List<CrmStatisticsBusinessSummaryByStatusRespVO> selectBusinessSummaryListGroupByStatus(CrmStatisticsFunnelReqVO reqVO);
  28 +
26 List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessSummaryGroupByDate(CrmStatisticsFunnelReqVO reqVO); 29 List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessSummaryGroupByDate(CrmStatisticsFunnelReqVO reqVO);
27 30
28 List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> selectBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO); 31 List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> selectBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO);
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
2 2
3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; 3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceSummaryRespVO;
5 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
6 7
7 import java.util.List; 8 import java.util.List;
@@ -38,4 +39,12 @@ public interface CrmStatisticsPerformanceMapper { @@ -38,4 +39,12 @@ public interface CrmStatisticsPerformanceMapper {
38 */ 39 */
39 List<CrmStatisticsPerformanceRespVO> selectReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); 40 List<CrmStatisticsPerformanceRespVO> selectReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
40 41
  42 + /**
  43 + * 合同汇总表
  44 + *
  45 + * @param performanceReqVO 参数
  46 + * @return 合同汇总表
  47 + */
  48 + List<CrmStatisticsPerformanceSummaryRespVO> selectContractSummary(CrmStatisticsPerformanceReqVO performanceReqVO);
  49 +
41 } 50 }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceTargetMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetActualRespVO;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * CRM 业绩目标完成情况 Mapper
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Mapper
  15 +public interface CrmStatisticsPerformanceTargetMapper {
  16 +
  17 + /**
  18 + * 按月统计合同金额
  19 + *
  20 + * @param reqVO 请求参数
  21 + * @return 按月统计合同金额
  22 + */
  23 + List<CrmStatisticsPerformanceTargetActualRespVO> selectContractActualByMonth(CrmStatisticsPerformanceTargetReqVO reqVO);
  24 +
  25 + /**
  26 + * 按月统计回款金额
  27 + *
  28 + * @param reqVO 请求参数
  29 + * @return 按月统计回款金额
  30 + */
  31 + List<CrmStatisticsPerformanceTargetActualRespVO> selectReceivableActualByMonth(CrmStatisticsPerformanceTargetReqVO reqVO);
  32 +
  33 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsProductMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * CRM 产品分析 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface CrmStatisticsProductMapper {
  17 +
  18 + /**
  19 + * 获取产品销售情况统计
  20 + *
  21 + * @param reqVO 请求参数
  22 + * @return 产品销售情况
  23 + */
  24 + List<CrmStatisticsProductSalesRespVO> selectProductSalesList(CrmStatisticsProductReqVO reqVO);
  25 +
  26 + /**
  27 + * 获取产品分类销售分析
  28 + *
  29 + * @param reqVO 请求参数
  30 + * @return 产品分类销售分析
  31 + */
  32 + List<CrmStatisticsProductCategoryRespVO> selectProductCategorySummary(CrmStatisticsProductReqVO reqVO);
  33 +
  34 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -104,6 +104,9 @@ public interface ErrorCodeConstants { @@ -104,6 +104,9 @@ public interface ErrorCodeConstants {
104 ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在"); 104 ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在");
105 ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); 105 ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限");
106 106
107 - // ========== 数据统计 1_020_014_000 ========== 107 + // ========== 业绩目标配置 1_020_011_000 ==========
  108 + ErrorCode PERFORMANCE_CONFIG_NOT_EXISTS = new ErrorCode(1_020_011_000, "业绩目标配置不存在");
  109 + ErrorCode PERFORMANCE_CONFIG_EXISTS = new ErrorCode(1_020_011_001, "该对象当前年份的业绩目标配置已存在");
  110 + ErrorCode PERFORMANCE_CONFIG_TYPE_INVALID = new ErrorCode(1_020_011_002, "目标对象类型不正确");
108 111
109 } 112 }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/performance/CrmPerformanceConfigBizTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Getter;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * CRM 业绩目标业务类型枚举
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Getter
  16 +@AllArgsConstructor
  17 +public enum CrmPerformanceConfigBizTypeEnum implements ArrayValuable<Integer> {
  18 +
  19 + CRM_CONTRACT(CrmBizTypeEnum.CRM_CONTRACT.getType(), "销售目标"),
  20 + CRM_RECEIVABLE(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), "回款目标");
  21 +
  22 + public static final Integer[] ARRAYS = Arrays.stream(values())
  23 + .map(CrmPerformanceConfigBizTypeEnum::getBizType).toArray(Integer[]::new);
  24 +
  25 + /**
  26 + * 业务类型
  27 + */
  28 + private final Integer bizType;
  29 + /**
  30 + * 名称
  31 + */
  32 + private final String name;
  33 +
  34 + @Override
  35 + public Integer[] array() {
  36 + return ARRAYS;
  37 + }
  38 +
  39 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/performance/CrmPerformanceConfigObjectTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * CRM 业绩目标对象类型枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum CrmPerformanceConfigObjectTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + DEPT(2, "部门"),
  19 + USER(3, "员工");
  20 +
  21 + public static final Integer[] ARRAYS = Arrays.stream(values())
  22 + .map(CrmPerformanceConfigObjectTypeEnum::getObjectType).toArray(Integer[]::new);
  23 +
  24 + /**
  25 + * 对象类型
  26 + */
  27 + private final Integer objectType;
  28 + /**
  29 + * 名称
  30 + */
  31 + private final String name;
  32 +
  33 + @Override
  34 + public Integer[] array() {
  35 + return ARRAYS;
  36 + }
  37 +
  38 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -28,7 +28,9 @@ import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; @@ -28,7 +28,9 @@ import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
28 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 28 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
29 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 29 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
30 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; 30 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
  31 +import cn.iocoder.yudao.module.crm.service.permission.CrmOwnerRecordService;
31 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 32 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
  33 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmOwnerRecordCreateReqBO;
32 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 34 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
33 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; 35 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
34 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 36 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -66,6 +68,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -66,6 +68,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
66 68
67 @Resource 69 @Resource
68 private CrmCustomerMapper customerMapper; 70 private CrmCustomerMapper customerMapper;
  71 + @Resource
  72 + private CrmOwnerRecordService ownerRecordService;
69 73
70 @Resource 74 @Resource
71 private CrmPermissionService permissionService; 75 private CrmPermissionService permissionService;
@@ -407,6 +411,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -407,6 +411,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
407 // 2. 领取公海数据 411 // 2. 领取公海数据
408 List<CrmCustomerDO> updateCustomers = new ArrayList<>(); 412 List<CrmCustomerDO> updateCustomers = new ArrayList<>();
409 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>(); 413 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
  414 + List<CrmOwnerRecordCreateReqBO> ownerRecords = new ArrayList<>();
410 customers.forEach(customer -> { 415 customers.forEach(customer -> {
411 // 2.1. 设置负责人 416 // 2.1. 设置负责人
412 updateCustomers.add(new CrmCustomerDO().setId(customer.getId()) 417 updateCustomers.add(new CrmCustomerDO().setId(customer.getId())
@@ -414,11 +419,16 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -414,11 +419,16 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
414 // 2.2. 创建负责人数据权限 419 // 2.2. 创建负责人数据权限
415 createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 420 createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
416 .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); 421 .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
  422 + // 2.3. 记录负责人从公海变更为指定人员
  423 + ownerRecords.add(new CrmOwnerRecordCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
  424 + .setBizId(customer.getId()).setPreOwnerUserId(customer.getOwnerUserId()).setPostOwnerUserId(ownerUserId));
417 }); 425 });
418 // 2.2 更新客户负责人 426 // 2.2 更新客户负责人
419 customerMapper.updateBatch(updateCustomers); 427 customerMapper.updateBatch(updateCustomers);
420 // 2.3 创建负责人数据权限 428 // 2.3 创建负责人数据权限
421 permissionService.createPermissionBatch(createPermissions); 429 permissionService.createPermissionBatch(createPermissions);
  430 + // 2.4 记录负责人变更历史
  431 + ownerRecordService.createOwnerRecordList(ownerRecords);
422 // TODO @芋艿:要不要处理关联的联系人??? 432 // TODO @芋艿:要不要处理关联的联系人???
423 433
424 // 3. 记录操作日志 434 // 3. 记录操作日志
@@ -459,6 +469,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -459,6 +469,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
459 if (updateOwnerUserIncr == 0) { 469 if (updateOwnerUserIncr == 0) {
460 throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); 470 throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
461 } 471 }
  472 + ownerRecordService.createOwnerRecord(new CrmOwnerRecordCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
  473 + .setBizId(customer.getId()).setPreOwnerUserId(customer.getOwnerUserId()).setPostOwnerUserId(null));
462 474
463 // 2. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的 475 // 2. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的
464 contactService.updateOwnerUserIdByCustomerId(customer.getId(), null); 476 contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/performance/CrmPerformanceConfigService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigSaveReqVO;
  6 +import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * CRM 业绩目标设置 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface CrmPerformanceConfigService {
  18 +
  19 + /**
  20 + * 创建业绩目标
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createPerformanceConfig(@Valid CrmPerformanceConfigSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新业绩目标
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updatePerformanceConfig(@Valid CrmPerformanceConfigSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除业绩目标
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deletePerformanceConfig(Long id);
  40 +
  41 + /**
  42 + * 获得业绩目标
  43 + *
  44 + * @param id 编号
  45 + * @return 业绩目标
  46 + */
  47 + CrmPerformanceConfigDO getPerformanceConfig(Long id);
  48 +
  49 + /**
  50 + * 获得业绩目标分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return 业绩目标分页
  54 + */
  55 + PageResult<CrmPerformanceConfigDO> getPerformanceConfigPage(CrmPerformanceConfigPageReqVO pageReqVO);
  56 +
  57 + /**
  58 + * 根据对象、年份、目标类型获得业绩目标配置
  59 + *
  60 + * @param objectId 对象编号
  61 + * @param objectType 对象类型
  62 + * @param year 年份
  63 + * @param bizType 业务类型
  64 + * @return 业绩目标配置
  65 + */
  66 + CrmPerformanceConfigDO getPerformanceConfig(Long objectId, Integer objectType, Integer year, Integer bizType);
  67 +
  68 + /**
  69 + * 根据对象数组、年份、目标类型获得业绩目标配置列表
  70 + *
  71 + * @param objectIds 对象编号数组
  72 + * @param objectType 对象类型
  73 + * @param year 年份
  74 + * @param bizType 业务类型
  75 + * @return 业绩目标配置列表
  76 + */
  77 + List<CrmPerformanceConfigDO> getPerformanceConfigList(Collection<Long> objectIds, Integer objectType, Integer year,
  78 + Integer bizType);
  79 +
  80 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/performance/CrmPerformanceConfigServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.performance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigSaveReqVO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
  8 +import cn.iocoder.yudao.module.crm.dal.mysql.performance.CrmPerformanceConfigMapper;
  9 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
  10 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  11 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  12 +import jakarta.annotation.Resource;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.validation.annotation.Validated;
  15 +
  16 +import java.math.BigDecimal;
  17 +import java.util.Collection;
  18 +import java.util.Collections;
  19 +import java.util.List;
  20 +import java.util.Objects;
  21 +
  22 +import static cn.hutool.core.collection.CollUtil.isEmpty;
  23 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  24 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
  25 +import static java.util.Collections.singleton;
  26 +
  27 +/**
  28 + * CRM 业绩目标设置 Service 实现类
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +@Service
  33 +@Validated
  34 +public class CrmPerformanceConfigServiceImpl implements CrmPerformanceConfigService {
  35 +
  36 + @Resource
  37 + private CrmPerformanceConfigMapper performanceConfigMapper;
  38 +
  39 + @Resource
  40 + private DeptApi deptApi;
  41 + @Resource
  42 + private AdminUserApi adminUserApi;
  43 +
  44 + @Override
  45 + public Long createPerformanceConfig(CrmPerformanceConfigSaveReqVO createReqVO) {
  46 + // 1.1 校验目标对象
  47 + validatePerformanceConfigObject(createReqVO.getObjectType(), createReqVO.getObjectId());
  48 + // 1.2 校验唯一
  49 + validatePerformanceConfigNotExists(null, createReqVO);
  50 +
  51 + // 2. 插入
  52 + CrmPerformanceConfigDO performanceConfig = BeanUtils.toBean(createReqVO, CrmPerformanceConfigDO.class);
  53 + fillTargetPrice(performanceConfig);
  54 + performanceConfigMapper.insert(performanceConfig);
  55 + return performanceConfig.getId();
  56 + }
  57 +
  58 + @Override
  59 + public void updatePerformanceConfig(CrmPerformanceConfigSaveReqVO updateReqVO) {
  60 + // 1.1 校验存在
  61 + validatePerformanceConfigExists(updateReqVO.getId());
  62 + // 1.2 校验目标对象
  63 + validatePerformanceConfigObject(updateReqVO.getObjectType(), updateReqVO.getObjectId());
  64 + // 1.3 校验唯一
  65 + validatePerformanceConfigNotExists(updateReqVO.getId(), updateReqVO);
  66 +
  67 + // 2. 更新
  68 + CrmPerformanceConfigDO updateObj = BeanUtils.toBean(updateReqVO, CrmPerformanceConfigDO.class);
  69 + fillTargetPrice(updateObj);
  70 + performanceConfigMapper.updateById(updateObj);
  71 + }
  72 +
  73 + @Override
  74 + public void deletePerformanceConfig(Long id) {
  75 + // 1. 校验存在
  76 + validatePerformanceConfigExists(id);
  77 + // 2. 删除
  78 + performanceConfigMapper.deleteById(id);
  79 + }
  80 +
  81 + @Override
  82 + public CrmPerformanceConfigDO getPerformanceConfig(Long id) {
  83 + return validatePerformanceConfigExists(id);
  84 + }
  85 +
  86 + @Override
  87 + public PageResult<CrmPerformanceConfigDO> getPerformanceConfigPage(CrmPerformanceConfigPageReqVO pageReqVO) {
  88 + return performanceConfigMapper.selectPage(pageReqVO);
  89 + }
  90 +
  91 + private CrmPerformanceConfigDO validatePerformanceConfigExists(Long id) {
  92 + CrmPerformanceConfigDO performanceConfig = performanceConfigMapper.selectById(id);
  93 + if (performanceConfig == null) {
  94 + throw exception(PERFORMANCE_CONFIG_NOT_EXISTS);
  95 + }
  96 + return performanceConfig;
  97 + }
  98 +
  99 + private void validatePerformanceConfigNotExists(Long id, CrmPerformanceConfigSaveReqVO reqVO) {
  100 + CrmPerformanceConfigDO performanceConfig = performanceConfigMapper.selectByObjectIdAndObjectTypeAndYearAndBizType(
  101 + reqVO.getObjectId(), reqVO.getObjectType(), reqVO.getYear(), reqVO.getBizType());
  102 + if (performanceConfig == null || Objects.equals(performanceConfig.getId(), id)) {
  103 + return;
  104 + }
  105 + throw exception(PERFORMANCE_CONFIG_EXISTS);
  106 + }
  107 +
  108 + private void validatePerformanceConfigObject(Integer objectType, Long objectId) {
  109 + // 情况一:部门
  110 + if (Objects.equals(objectType, CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType())) {
  111 + deptApi.validateDeptList(singleton(objectId));
  112 + return;
  113 + }
  114 + // 情况二:员工
  115 + if (Objects.equals(objectType, CrmPerformanceConfigObjectTypeEnum.USER.getObjectType())) {
  116 + adminUserApi.validateUserList(singleton(objectId));
  117 + return;
  118 + }
  119 + throw exception(PERFORMANCE_CONFIG_TYPE_INVALID);
  120 + }
  121 +
  122 + @Override
  123 + public CrmPerformanceConfigDO getPerformanceConfig(Long objectId, Integer objectType, Integer year, Integer bizType) {
  124 + return performanceConfigMapper.selectByObjectIdAndObjectTypeAndYearAndBizType(objectId, objectType, year, bizType);
  125 + }
  126 +
  127 + @Override
  128 + public List<CrmPerformanceConfigDO> getPerformanceConfigList(Collection<Long> objectIds, Integer objectType,
  129 + Integer year, Integer bizType) {
  130 + if (isEmpty(objectIds)) {
  131 + return Collections.emptyList();
  132 + }
  133 + return performanceConfigMapper.selectListByObjectIdsAndObjectTypeAndYearAndBizType(objectIds, objectType,
  134 + year, bizType);
  135 + }
  136 +
  137 + private void fillTargetPrice(CrmPerformanceConfigDO performanceConfig) {
  138 + // 1. 空值设置为 0
  139 + performanceConfig.setJanuaryTargetPrice(defaultIfNull(performanceConfig.getJanuaryTargetPrice()));
  140 + performanceConfig.setFebruaryTargetPrice(defaultIfNull(performanceConfig.getFebruaryTargetPrice()));
  141 + performanceConfig.setMarchTargetPrice(defaultIfNull(performanceConfig.getMarchTargetPrice()));
  142 + performanceConfig.setAprilTargetPrice(defaultIfNull(performanceConfig.getAprilTargetPrice()));
  143 + performanceConfig.setMayTargetPrice(defaultIfNull(performanceConfig.getMayTargetPrice()));
  144 + performanceConfig.setJuneTargetPrice(defaultIfNull(performanceConfig.getJuneTargetPrice()));
  145 + performanceConfig.setJulyTargetPrice(defaultIfNull(performanceConfig.getJulyTargetPrice()));
  146 + performanceConfig.setAugustTargetPrice(defaultIfNull(performanceConfig.getAugustTargetPrice()));
  147 + performanceConfig.setSeptemberTargetPrice(defaultIfNull(performanceConfig.getSeptemberTargetPrice()));
  148 + performanceConfig.setOctoberTargetPrice(defaultIfNull(performanceConfig.getOctoberTargetPrice()));
  149 + performanceConfig.setNovemberTargetPrice(defaultIfNull(performanceConfig.getNovemberTargetPrice()));
  150 + performanceConfig.setDecemberTargetPrice(defaultIfNull(performanceConfig.getDecemberTargetPrice()));
  151 + // 2. 计算年度目标
  152 + performanceConfig.setYearTargetPrice(performanceConfig.getJanuaryTargetPrice()
  153 + .add(performanceConfig.getFebruaryTargetPrice())
  154 + .add(performanceConfig.getMarchTargetPrice())
  155 + .add(performanceConfig.getAprilTargetPrice())
  156 + .add(performanceConfig.getMayTargetPrice())
  157 + .add(performanceConfig.getJuneTargetPrice())
  158 + .add(performanceConfig.getJulyTargetPrice())
  159 + .add(performanceConfig.getAugustTargetPrice())
  160 + .add(performanceConfig.getSeptemberTargetPrice())
  161 + .add(performanceConfig.getOctoberTargetPrice())
  162 + .add(performanceConfig.getNovemberTargetPrice())
  163 + .add(performanceConfig.getDecemberTargetPrice()));
  164 + }
  165 +
  166 + private BigDecimal defaultIfNull(BigDecimal value) {
  167 + return value == null ? BigDecimal.ZERO : value;
  168 + }
  169 +
  170 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmOwnerRecordService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.permission;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO;
  6 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmOwnerRecordCreateReqBO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * CRM 负责人变更记录 Service 接口
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface CrmOwnerRecordService {
  17 +
  18 + /**
  19 + * 创建负责人变更记录
  20 + *
  21 + * @param createReqBO 创建信息
  22 + * @return 编号
  23 + */
  24 + Long createOwnerRecord(@Valid CrmOwnerRecordCreateReqBO createReqBO);
  25 +
  26 + /**
  27 + * 批量创建负责人变更记录
  28 + *
  29 + * @param createReqBOs 创建信息
  30 + */
  31 + void createOwnerRecordList(@Valid List<CrmOwnerRecordCreateReqBO> createReqBOs);
  32 +
  33 + /**
  34 + * 进入公海客户数(按日期)
  35 + *
  36 + * @param reqVO 请求参数
  37 + * @return 统计数据
  38 + */
  39 + List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO);
  40 +
  41 + /**
  42 + * 公海领取客户数(按日期)
  43 + *
  44 + * @param reqVO 请求参数
  45 + * @return 统计数据
  46 + */
  47 + List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO);
  48 +
  49 + /**
  50 + * 进入公海客户数(按用户)
  51 + *
  52 + * @param reqVO 请求参数
  53 + * @return 统计数据
  54 + */
  55 + List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO);
  56 +
  57 + /**
  58 + * 公海领取客户数(按用户)
  59 + *
  60 + * @param reqVO 请求参数
  61 + * @return 统计数据
  62 + */
  63 + List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO);
  64 +
  65 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmOwnerRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.permission;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO;
  8 +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmOwnerRecordDO;
  9 +import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmOwnerRecordMapper;
  10 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmOwnerRecordCreateReqBO;
  11 +import jakarta.annotation.Resource;
  12 +import org.springframework.stereotype.Service;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * CRM 负责人变更记录 Service 实现类
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +@Service
  23 +@Validated
  24 +public class CrmOwnerRecordServiceImpl implements CrmOwnerRecordService {
  25 +
  26 + @Resource
  27 + private CrmOwnerRecordMapper ownerRecordMapper;
  28 +
  29 + @Override
  30 + public Long createOwnerRecord(CrmOwnerRecordCreateReqBO createReqBO) {
  31 + CrmOwnerRecordDO ownerRecord = BeanUtils.toBean(createReqBO, CrmOwnerRecordDO.class);
  32 + ownerRecordMapper.insert(ownerRecord);
  33 + return ownerRecord.getId();
  34 + }
  35 +
  36 + @Override
  37 + public void createOwnerRecordList(List<CrmOwnerRecordCreateReqBO> createReqBOs) {
  38 + if (CollUtil.isEmpty(createReqBOs)) {
  39 + return;
  40 + }
  41 + List<CrmOwnerRecordDO> list = BeanUtils.toBean(createReqBOs, CrmOwnerRecordDO.class);
  42 + ownerRecordMapper.insertBatch(list);
  43 + }
  44 +
  45 + @Override
  46 + public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO) {
  47 + return ownerRecordMapper.selectPoolCustomerPutCountByDate(reqVO);
  48 + }
  49 +
  50 + @Override
  51 + public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO) {
  52 + return ownerRecordMapper.selectPoolCustomerTakeCountByDate(reqVO);
  53 + }
  54 +
  55 + @Override
  56 + public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO) {
  57 + return ownerRecordMapper.selectPoolCustomerPutCountByUser(reqVO);
  58 + }
  59 +
  60 + @Override
  61 + public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO) {
  62 + return ownerRecordMapper.selectPoolCustomerTakeCountByUser(reqVO);
  63 + }
  64 +
  65 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmOwnerRecordCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.permission.bo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +/**
  9 + * CRM 负责人变更记录 Create Req BO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Data
  14 +public class CrmOwnerRecordCreateReqBO {
  15 +
  16 + /**
  17 + * CRM 业务类型
  18 + */
  19 + @NotNull(message = "CRM 业务类型不能为空")
  20 + @InEnum(CrmBizTypeEnum.class)
  21 + private Integer bizType;
  22 +
  23 + /**
  24 + * CRM 业务编号
  25 + */
  26 + @NotNull(message = "CRM 业务编号不能为空")
  27 + private Long bizId;
  28 +
  29 + /**
  30 + * 变更前负责人
  31 + */
  32 + private Long preOwnerUserId;
  33 +
  34 + /**
  35 + * 变更后负责人
  36 + */
  37 + private Long postOwnerUserId;
  38 +
  39 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
10 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; 10 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
11 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 11 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
12 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; 12 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
  13 +import cn.iocoder.yudao.module.crm.service.permission.CrmOwnerRecordService;
13 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 14 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
14 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 15 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
15 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 16 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -42,6 +43,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -42,6 +43,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
42 private CrmStatisticsCustomerMapper customerMapper; 43 private CrmStatisticsCustomerMapper customerMapper;
43 44
44 @Resource 45 @Resource
  46 + private CrmOwnerRecordService ownerRecordService;
  47 +
  48 + @Resource
45 private AdminUserApi adminUserApi; 49 private AdminUserApi adminUserApi;
46 @Resource 50 @Resource
47 private DeptApi deptApi; 51 private DeptApi deptApi;
@@ -201,8 +205,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -201,8 +205,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
201 } 205 }
202 206
203 // 2. 按天统计,获取分项统计数据 207 // 2. 按天统计,获取分项统计数据
204 - List<CrmStatisticsPoolSummaryByDateRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByDate(reqVO);  
205 - List<CrmStatisticsPoolSummaryByDateRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByDate(reqVO); 208 + List<CrmStatisticsPoolSummaryByDateRespVO> customerPutCountList = ownerRecordService.getPoolCustomerPutCountByDate(reqVO);
  209 + List<CrmStatisticsPoolSummaryByDateRespVO> customerTakeCountList = ownerRecordService.getPoolCustomerTakeCountByDate(reqVO);
206 210
207 // 3. 按照日期间隔,合并数据 211 // 3. 按照日期间隔,合并数据
208 List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); 212 List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
@@ -228,8 +232,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -228,8 +232,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
228 } 232 }
229 233
230 // 2. 按用户统计,获取分项统计数据 234 // 2. 按用户统计,获取分项统计数据
231 - List<CrmStatisticsPoolSummaryByUserRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByUser(reqVO);  
232 - List<CrmStatisticsPoolSummaryByUserRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByUser(reqVO); 235 + List<CrmStatisticsPoolSummaryByUserRespVO> customerPutCountList = ownerRecordService.getPoolCustomerPutCountByUser(reqVO);
  236 + List<CrmStatisticsPoolSummaryByUserRespVO> customerTakeCountList = ownerRecordService.getPoolCustomerTakeCountByUser(reqVO);
233 237
234 // 3.1 按照用户,合并统计数据 238 // 3.1 按照用户,合并统计数据
235 List<CrmStatisticsPoolSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> { 239 List<CrmStatisticsPoolSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
@@ -331,7 +335,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -331,7 +335,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
331 reqVO.setUserIds(userIds); 335 reqVO.setUserIds(userIds);
332 336
333 // 2. 获取客户产品统计数据 337 // 2. 获取客户产品统计数据
334 - // TODO @dhb52:未读取产品名  
335 return customerMapper.selectCustomerDealCycleGroupByProductId(reqVO); 338 return customerMapper.selectCustomerDealCycleGroupByProductId(reqVO);
336 } 339 }
337 340
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelService.java
@@ -30,6 +30,14 @@ public interface CrmStatisticsFunnelService { @@ -30,6 +30,14 @@ public interface CrmStatisticsFunnelService {
30 List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO); 30 List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO);
31 31
32 /** 32 /**
  33 + * 获得商机阶段统计
  34 + *
  35 + * @param reqVO 请求
  36 + * @return 商机阶段统计
  37 + */
  38 + List<CrmStatisticsBusinessSummaryByStatusRespVO> getBusinessSummaryByStatus(CrmStatisticsFunnelReqVO reqVO);
  39 +
  40 + /**
33 * 获取新增商机分析(按日期) 41 * 获取新增商机分析(按日期)
34 * 42 *
35 * @param reqVO 请求 43 * @param reqVO 请求
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelServiceImpl.java
@@ -71,6 +71,18 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic @@ -71,6 +71,18 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
71 } 71 }
72 72
73 @Override 73 @Override
  74 + public List<CrmStatisticsBusinessSummaryByStatusRespVO> getBusinessSummaryByStatus(CrmStatisticsFunnelReqVO reqVO) {
  75 + // 1. 获得用户编号数组
  76 + reqVO.setUserIds(getUserIds(reqVO));
  77 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
  78 + return Collections.emptyList();
  79 + }
  80 +
  81 + // 2. 获得统计数据
  82 + return funnelMapper.selectBusinessSummaryListGroupByStatus(reqVO);
  83 + }
  84 +
  85 + @Override
74 public List<CrmStatisticsBusinessSummaryByDateRespVO> getBusinessSummaryByDate(CrmStatisticsFunnelReqVO reqVO) { 86 public List<CrmStatisticsBusinessSummaryByDateRespVO> getBusinessSummaryByDate(CrmStatisticsFunnelReqVO reqVO) {
75 // 1. 获得用户编号数组 87 // 1. 获得用户编号数组
76 reqVO.setUserIds(getUserIds(reqVO)); 88 reqVO.setUserIds(getUserIds(reqVO));
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java
@@ -4,11 +4,12 @@ package cn.iocoder.yudao.module.crm.service.statistics; @@ -4,11 +4,12 @@ package cn.iocoder.yudao.module.crm.service.statistics;
4 4
5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceSummaryRespVO;
7 8
8 import java.util.List; 9 import java.util.List;
9 10
10 /** 11 /**
11 - * CRM 员工绩效统计 Service 接口 12 + * CRM 员工业绩统计 Service 接口
12 * 13 *
13 * @author scholar 14 * @author scholar
14 */ 15 */
@@ -38,5 +39,12 @@ public interface CrmStatisticsPerformanceService { @@ -38,5 +39,12 @@ public interface CrmStatisticsPerformanceService {
38 */ 39 */
39 List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); 40 List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
40 41
  42 + /**
  43 + * 员工合同汇总表
  44 + *
  45 + * @param performanceReqVO 排行参数
  46 + * @return 员工合同汇总表
  47 + */
  48 + List<CrmStatisticsPerformanceSummaryRespVO> getContractSummary(CrmStatisticsPerformanceReqVO performanceReqVO);
41 49
42 } 50 }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil; @@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
6 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceSummaryRespVO;
8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper; 9 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper;
9 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 10 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
10 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 11 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -56,6 +57,29 @@ public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerform @@ -56,6 +57,29 @@ public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerform
56 return getPerformance(performanceReqVO, performanceMapper::selectReceivablePricePerformance); 57 return getPerformance(performanceReqVO, performanceMapper::selectReceivablePricePerformance);
57 } 58 }
58 59
  60 + @Override
  61 + public List<CrmStatisticsPerformanceSummaryRespVO> getContractSummary(CrmStatisticsPerformanceReqVO performanceReqVO) {
  62 + List<Long> userIds = getUserIds(performanceReqVO);
  63 + if (CollUtil.isEmpty(userIds)) {
  64 + return Collections.emptyList();
  65 + }
  66 + performanceReqVO.setUserIds(userIds);
  67 +
  68 + int year = performanceReqVO.getTimes()[0].getYear();
  69 + List<CrmStatisticsPerformanceSummaryRespVO> summaryList = performanceMapper.selectContractSummary(performanceReqVO);
  70 + Map<String, CrmStatisticsPerformanceSummaryRespVO> summaryMap = convertMap(summaryList,
  71 + CrmStatisticsPerformanceSummaryRespVO::getTime);
  72 + List<CrmStatisticsPerformanceSummaryRespVO> result = new ArrayList<>();
  73 + for (int month = 1; month <= 12; month++) {
  74 + String currentMonth = String.format("%d%02d", year, month);
  75 + CrmStatisticsPerformanceSummaryRespVO summary = summaryMap.get(currentMonth);
  76 + result.add(summary != null ? summary : new CrmStatisticsPerformanceSummaryRespVO().setTime(currentMonth)
  77 + .setContractCount(0).setContractPrice(BigDecimal.ZERO)
  78 + .setReceivablePrice(BigDecimal.ZERO).setUnreceivedPrice(BigDecimal.ZERO));
  79 + }
  80 + return result;
  81 + }
  82 +
59 /** 83 /**
60 * 获得员工业绩数据 84 * 获得员工业绩数据
61 * 85 *
@@ -117,4 +141,4 @@ public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerform @@ -117,4 +141,4 @@ public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerform
117 return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); 141 return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
118 } 142 }
119 143
120 -}  
121 \ No newline at end of file 144 \ No newline at end of file
  145 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceTargetService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.statistics;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetRespVO;
  5 +
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * CRM 业绩目标统计 Service 接口
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +public interface CrmStatisticsPerformanceTargetService {
  14 +
  15 + /**
  16 + * 获取业绩目标完成情况
  17 + *
  18 + * @param reqVO 请求参数
  19 + * @return 业绩目标完成情况
  20 + */
  21 + List<CrmStatisticsPerformanceTargetRespVO> getPerformanceTargetSummary(CrmStatisticsPerformanceTargetReqVO reqVO);
  22 +
  23 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceTargetServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.statistics;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetActualRespVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetRespVO;
  9 +import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
  10 +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceTargetMapper;
  11 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
  12 +import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
  13 +import cn.iocoder.yudao.module.crm.service.performance.CrmPerformanceConfigService;
  14 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  15 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  16 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  17 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  18 +import jakarta.annotation.Resource;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import java.math.BigDecimal;
  23 +import java.math.RoundingMode;
  24 +import java.util.ArrayList;
  25 +import java.util.Collections;
  26 +import java.util.List;
  27 +import java.util.Map;
  28 +
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  31 +
  32 +/**
  33 + * CRM 业绩目标统计 Service 实现类
  34 + *
  35 + * @author 芋道源码
  36 + */
  37 +@Service
  38 +@Validated
  39 +public class CrmStatisticsPerformanceTargetServiceImpl implements CrmStatisticsPerformanceTargetService {
  40 +
  41 + @Resource
  42 + private CrmStatisticsPerformanceTargetMapper performanceTargetMapper;
  43 + @Resource
  44 + private CrmPerformanceConfigService performanceConfigService;
  45 +
  46 + @Resource
  47 + private AdminUserApi adminUserApi;
  48 + @Resource
  49 + private DeptApi deptApi;
  50 +
  51 + @Override
  52 + public List<CrmStatisticsPerformanceTargetRespVO> getPerformanceTargetSummary(CrmStatisticsPerformanceTargetReqVO reqVO) {
  53 + // 1. 获得用户编号数组
  54 + reqVO.setUserIds(getUserIds(reqVO));
  55 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
  56 + return Collections.emptyList();
  57 + }
  58 +
  59 + // 2.1 获得目标配置
  60 + List<CrmPerformanceConfigDO> performanceConfigs = getPerformanceConfigs(reqVO);
  61 + // 2.2 获得实际完成金额
  62 + List<CrmStatisticsPerformanceTargetActualRespVO> actualList = ObjUtil.equal(reqVO.getBizType(),
  63 + CrmPerformanceConfigBizTypeEnum.CRM_RECEIVABLE.getBizType())
  64 + ? performanceTargetMapper.selectReceivableActualByMonth(reqVO)
  65 + : performanceTargetMapper.selectContractActualByMonth(reqVO);
  66 + Map<Integer, BigDecimal> actualMap = convertMap(actualList,
  67 + CrmStatisticsPerformanceTargetActualRespVO::getMonth,
  68 + CrmStatisticsPerformanceTargetActualRespVO::getCurrentPrice);
  69 +
  70 + // 3. 组装数据返回
  71 + List<CrmStatisticsPerformanceTargetRespVO> result = new ArrayList<>();
  72 + for (int month = 1; month <= 12; month++) {
  73 + BigDecimal targetPrice = getMonthTargetPrice(performanceConfigs, month);
  74 + BigDecimal currentPrice = actualMap.getOrDefault(month, BigDecimal.ZERO);
  75 + result.add(new CrmStatisticsPerformanceTargetRespVO().setMonth(month).setTargetPrice(targetPrice)
  76 + .setCurrentPrice(currentPrice)
  77 + .setCompletionRate(calculateCompletionRate(currentPrice, targetPrice)));
  78 + }
  79 + return result;
  80 + }
  81 +
  82 + private List<CrmPerformanceConfigDO> getPerformanceConfigs(CrmStatisticsPerformanceTargetReqVO reqVO) {
  83 + // 情况一:选中某个用户
  84 + if (ObjUtil.isNotNull(reqVO.getUserId())) {
  85 + CrmPerformanceConfigDO performanceConfig = performanceConfigService.getPerformanceConfig(reqVO.getUserId(),
  86 + CrmPerformanceConfigObjectTypeEnum.USER.getObjectType(), reqVO.getYear(), reqVO.getBizType());
  87 + return performanceConfig == null ? Collections.emptyList() : ListUtil.of(performanceConfig);
  88 + }
  89 + // 情况二:选中某个部门
  90 + return performanceConfigService.getPerformanceConfigList(getDeptIds(reqVO.getDeptId()),
  91 + CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType(), reqVO.getYear(), reqVO.getBizType());
  92 + }
  93 +
  94 + private BigDecimal getMonthTargetPrice(List<CrmPerformanceConfigDO> performanceConfigs, int month) {
  95 + return performanceConfigs.stream()
  96 + .map(performanceConfig -> getMonthTargetPrice(performanceConfig, month))
  97 + .reduce(BigDecimal.ZERO, BigDecimal::add);
  98 + }
  99 +
  100 + @SuppressWarnings("EnhancedSwitchMigration")
  101 + private BigDecimal getMonthTargetPrice(CrmPerformanceConfigDO performanceConfig, int month) {
  102 + // 1. 无目标配置,返回 0
  103 + if (performanceConfig == null) {
  104 + return BigDecimal.ZERO;
  105 + }
  106 + // 2. 获得月份目标
  107 + BigDecimal targetPrice;
  108 + switch (month) {
  109 + case 1:
  110 + targetPrice = performanceConfig.getJanuaryTargetPrice();
  111 + break;
  112 + case 2:
  113 + targetPrice = performanceConfig.getFebruaryTargetPrice();
  114 + break;
  115 + case 3:
  116 + targetPrice = performanceConfig.getMarchTargetPrice();
  117 + break;
  118 + case 4:
  119 + targetPrice = performanceConfig.getAprilTargetPrice();
  120 + break;
  121 + case 5:
  122 + targetPrice = performanceConfig.getMayTargetPrice();
  123 + break;
  124 + case 6:
  125 + targetPrice = performanceConfig.getJuneTargetPrice();
  126 + break;
  127 + case 7:
  128 + targetPrice = performanceConfig.getJulyTargetPrice();
  129 + break;
  130 + case 8:
  131 + targetPrice = performanceConfig.getAugustTargetPrice();
  132 + break;
  133 + case 9:
  134 + targetPrice = performanceConfig.getSeptemberTargetPrice();
  135 + break;
  136 + case 10:
  137 + targetPrice = performanceConfig.getOctoberTargetPrice();
  138 + break;
  139 + case 11:
  140 + targetPrice = performanceConfig.getNovemberTargetPrice();
  141 + break;
  142 + case 12:
  143 + targetPrice = performanceConfig.getDecemberTargetPrice();
  144 + break;
  145 + default:
  146 + targetPrice = BigDecimal.ZERO;
  147 + }
  148 + return targetPrice != null ? targetPrice : BigDecimal.ZERO;
  149 + }
  150 +
  151 + private BigDecimal calculateCompletionRate(BigDecimal currentPrice, BigDecimal targetPrice) {
  152 + if (targetPrice == null || targetPrice.compareTo(BigDecimal.ZERO) == 0) {
  153 + return BigDecimal.ZERO;
  154 + }
  155 + return currentPrice.multiply(BigDecimal.valueOf(100)).divide(targetPrice, 2, RoundingMode.HALF_UP);
  156 + }
  157 +
  158 + /**
  159 + * 获取用户编号数组
  160 + *
  161 + * 如果用户编号为空,则获得部门下的用户编号数组,包括子部门的所有用户编号
  162 + *
  163 + * @param reqVO 请求参数
  164 + * @return 用户编号数组
  165 + */
  166 + private List<Long> getUserIds(CrmStatisticsPerformanceTargetReqVO reqVO) {
  167 + // 情况一:选中某个用户
  168 + if (ObjUtil.isNotNull(reqVO.getUserId())) {
  169 + return ListUtil.of(reqVO.getUserId());
  170 + }
  171 + // 情况二:选中某个部门
  172 + // 2.1 获得部门列表
  173 + List<Long> deptIds = getDeptIds(reqVO.getDeptId());
  174 + // 2.2 获得用户编号
  175 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
  176 + }
  177 +
  178 + /**
  179 + * 获得部门编号数组,包括子部门
  180 + *
  181 + * @param deptId 部门编号
  182 + * @return 部门编号数组
  183 + */
  184 + private List<Long> getDeptIds(Long deptId) {
  185 + List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
  186 + deptIds.add(deptId);
  187 + return deptIds;
  188 + }
  189 +
  190 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsProductService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.statistics;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * CRM 产品分析 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface CrmStatisticsProductService {
  15 +
  16 + /**
  17 + * 获取产品销售情况统计
  18 + *
  19 + * @param reqVO 请求参数
  20 + * @return 产品销售情况
  21 + */
  22 + List<CrmStatisticsProductSalesRespVO> getProductSalesList(CrmStatisticsProductReqVO reqVO);
  23 +
  24 + /**
  25 + * 获取产品分类销售分析
  26 + *
  27 + * @param reqVO 请求参数
  28 + * @return 产品分类销售分析
  29 + */
  30 + List<CrmStatisticsProductCategoryRespVO> getProductCategorySummary(CrmStatisticsProductReqVO reqVO);
  31 +
  32 +}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsProductServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.statistics;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
  9 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
  10 +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
  11 +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsProductMapper;
  12 +import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService;
  13 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  14 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  15 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  16 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  17 +import jakarta.annotation.Resource;
  18 +import org.springframework.stereotype.Service;
  19 +import org.springframework.validation.annotation.Validated;
  20 +
  21 +import java.util.Collections;
  22 +import java.util.List;
  23 +import java.util.Objects;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  26 +
  27 +/**
  28 + * CRM 产品分析 Service 实现类
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +@Service
  33 +@Validated
  34 +public class CrmStatisticsProductServiceImpl implements CrmStatisticsProductService {
  35 +
  36 + @Resource
  37 + private CrmStatisticsProductMapper productMapper;
  38 +
  39 + @Resource
  40 + private AdminUserApi adminUserApi;
  41 + @Resource
  42 + private DeptApi deptApi;
  43 + @Resource
  44 + private CrmProductCategoryService productCategoryService;
  45 +
  46 + @Override
  47 + public List<CrmStatisticsProductSalesRespVO> getProductSalesList(CrmStatisticsProductReqVO reqVO) {
  48 + reqVO.setUserIds(getUserIds(reqVO));
  49 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
  50 + return Collections.emptyList();
  51 + }
  52 + setCategoryIds(reqVO);
  53 + return productMapper.selectProductSalesList(reqVO);
  54 + }
  55 +
  56 + @Override
  57 + public List<CrmStatisticsProductCategoryRespVO> getProductCategorySummary(CrmStatisticsProductReqVO reqVO) {
  58 + reqVO.setUserIds(getUserIds(reqVO));
  59 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
  60 + return Collections.emptyList();
  61 + }
  62 + setCategoryIds(reqVO);
  63 + return productMapper.selectProductCategorySummary(reqVO);
  64 + }
  65 +
  66 + /**
  67 + * 设置产品分类编号数组,选择父分类时,包含子分类
  68 + *
  69 + * @param reqVO 请求参数
  70 + */
  71 + private void setCategoryIds(CrmStatisticsProductReqVO reqVO) {
  72 + if (ObjUtil.isNull(reqVO.getCategoryId())) {
  73 + return;
  74 + }
  75 + List<Long> categoryIds = ListUtil.of(reqVO.getCategoryId());
  76 + List<CrmProductCategoryDO> categoryList = productCategoryService.getProductCategoryList(
  77 + new CrmProductCategoryListReqVO());
  78 + categoryIds.addAll(convertList(categoryList, CrmProductCategoryDO::getId,
  79 + category -> Objects.equals(category.getParentId(), reqVO.getCategoryId())));
  80 + reqVO.setCategoryIds(categoryIds);
  81 + }
  82 +
  83 + /**
  84 + * 获取用户编号数组
  85 + *
  86 + * 如果用户编号为空,则获得部门下的用户编号数组,包括子部门的所有用户编号
  87 + *
  88 + * @param reqVO 请求参数
  89 + * @return 用户编号数组
  90 + */
  91 + private List<Long> getUserIds(CrmStatisticsProductReqVO reqVO) {
  92 + // 情况一:选中某个用户
  93 + if (ObjUtil.isNotNull(reqVO.getUserId())) {
  94 + return ListUtil.of(reqVO.getUserId());
  95 + }
  96 + // 情况二:选中某个部门
  97 + // 2.1 获得部门列表
  98 + List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
  99 + deptIds.add(reqVO.getDeptId());
  100 + // 2.2 获得用户编号
  101 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
  102 + }
  103 +
  104 +}
yudao-module-crm/src/main/resources/mapper/permission/CrmOwnerRecordMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmOwnerRecordMapper">
  5 +
  6 + <select id="selectPoolCustomerPutCountByDate"
  7 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO">
  8 + SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
  9 + COUNT(*) AS customer_put_count
  10 + FROM crm_owner_record
  11 + WHERE deleted = 0
  12 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  13 + AND pre_owner_user_id IN
  14 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  15 + #{userId}
  16 + </foreach>
  17 + AND post_owner_user_id IS NULL
  18 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  19 + GROUP BY time
  20 + </select>
  21 +
  22 + <select id="selectPoolCustomerTakeCountByDate"
  23 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO">
  24 + SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
  25 + COUNT(*) AS customer_take_count
  26 + FROM crm_owner_record
  27 + WHERE deleted = 0
  28 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  29 + AND pre_owner_user_id IS NULL
  30 + AND post_owner_user_id IN
  31 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  32 + #{userId}
  33 + </foreach>
  34 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  35 + GROUP BY time
  36 + </select>
  37 +
  38 + <select id="selectPoolCustomerPutCountByUser"
  39 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO">
  40 + SELECT pre_owner_user_id AS owner_user_id,
  41 + COUNT(*) AS customer_put_count
  42 + FROM crm_owner_record
  43 + WHERE deleted = 0
  44 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  45 + AND pre_owner_user_id IN
  46 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  47 + #{userId}
  48 + </foreach>
  49 + AND post_owner_user_id IS NULL
  50 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  51 + GROUP BY pre_owner_user_id
  52 + </select>
  53 +
  54 + <select id="selectPoolCustomerTakeCountByUser"
  55 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO">
  56 + SELECT post_owner_user_id AS owner_user_id,
  57 + COUNT(*) AS customer_take_count
  58 + FROM crm_owner_record
  59 + WHERE deleted = 0
  60 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  61 + AND pre_owner_user_id IS NULL
  62 + AND post_owner_user_id IN
  63 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  64 + #{userId}
  65 + </foreach>
  66 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  67 + GROUP BY post_owner_user_id
  68 + </select>
  69 +
  70 +</mapper>
yudao-module-crm/src/main/resources/mapper/statistics/CrmStatisticsFunnelMapper.xml
@@ -25,6 +25,9 @@ @@ -25,6 +25,9 @@
25 <if test="status != null"> 25 <if test="status != null">
26 AND end_status = #{status} 26 AND end_status = #{status}
27 </if> 27 </if>
  28 + <if test="reqVO.statusTypeId != null">
  29 + AND status_type_id = #{reqVO.statusTypeId}
  30 + </if>
28 AND owner_user_id IN 31 AND owner_user_id IN
29 <foreach collection="reqVO.userIds" item="userId" open="(" close=")" separator=","> 32 <foreach collection="reqVO.userIds" item="userId" open="(" close=")" separator=",">
30 #{userId} 33 #{userId}
@@ -38,9 +41,12 @@ @@ -38,9 +41,12 @@
38 SELECT 41 SELECT
39 end_status AS endStatus, 42 end_status AS endStatus,
40 COUNT(*) AS businessCount, 43 COUNT(*) AS businessCount,
41 - SUM(total_price) AS totalPrice 44 + IFNULL(SUM(total_price), 0) AS totalPrice
42 FROM crm_business 45 FROM crm_business
43 WHERE deleted = 0 AND end_status IS NOT NULL 46 WHERE deleted = 0 AND end_status IS NOT NULL
  47 + <if test="statusTypeId != null">
  48 + AND status_type_id = #{statusTypeId}
  49 + </if>
44 AND owner_user_id IN 50 AND owner_user_id IN
45 <foreach collection="userIds" item="userId" open="(" close=")" separator=","> 51 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
46 #{userId} 52 #{userId}
@@ -50,14 +56,44 @@ @@ -50,14 +56,44 @@
50 GROUP BY end_status 56 GROUP BY end_status
51 </select> 57 </select>
52 58
  59 + <select id="selectBusinessSummaryListGroupByStatus"
  60 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByStatusRespVO">
  61 + SELECT
  62 + status.id AS statusId,
  63 + status.name AS statusName,
  64 + status.percent AS statusPercent,
  65 + status.sort AS sort,
  66 + COUNT(business.id) AS businessCount,
  67 + IFNULL(SUM(business.total_price), 0) AS totalPrice
  68 + FROM crm_business_status status
  69 + LEFT JOIN crm_business business ON business.status_id = status.id
  70 + AND business.deleted = 0
  71 + AND business.end_status IS NULL
  72 + AND business.owner_user_id IN
  73 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  74 + #{userId}
  75 + </foreach>
  76 + AND business.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  77 + #{times[1],javaType=java.time.LocalDateTime}
  78 + WHERE status.deleted = 0
  79 + <if test="statusTypeId != null">
  80 + AND status.type_id = #{statusTypeId}
  81 + </if>
  82 + GROUP BY status.id, status.name, status.percent, status.sort
  83 + ORDER BY status.sort ASC
  84 + </select>
  85 +
53 <select id="selectBusinessSummaryGroupByDate" 86 <select id="selectBusinessSummaryGroupByDate"
54 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO"> 87 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO">
55 SELECT 88 SELECT
56 DATE_FORMAT(create_time, '%Y-%m-%d') AS time, 89 DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
57 COUNT(*) AS businessCreateCount, 90 COUNT(*) AS businessCreateCount,
58 - SUM(total_price) AS totalPrice 91 + IFNULL(SUM(total_price), 0) AS totalPrice
59 FROM crm_business 92 FROM crm_business
60 WHERE deleted = 0 93 WHERE deleted = 0
  94 + <if test="statusTypeId != null">
  95 + AND status_type_id = #{statusTypeId}
  96 + </if>
61 AND owner_user_id IN 97 AND owner_user_id IN
62 <foreach collection="userIds" item="userId" open="(" close=")" separator=","> 98 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
63 #{userId} 99 #{userId}
@@ -75,6 +111,9 @@ @@ -75,6 +111,9 @@
75 SUM(IF(end_status = 1, 1, 0)) AS businessWinCount 111 SUM(IF(end_status = 1, 1, 0)) AS businessWinCount
76 FROM crm_business 112 FROM crm_business
77 WHERE deleted = 0 113 WHERE deleted = 0
  114 + <if test="statusTypeId != null">
  115 + AND status_type_id = #{statusTypeId}
  116 + </if>
78 AND owner_user_id IN 117 AND owner_user_id IN
79 <foreach collection="userIds" item="userId" open="(" close=")" separator=","> 118 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
80 #{userId} 119 #{userId}
yudao-module-crm/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml
@@ -53,4 +53,31 @@ @@ -53,4 +53,31 @@
53 GROUP BY time 53 GROUP BY time
54 </select> 54 </select>
55 55
  56 + <select id="selectContractSummary"
  57 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceSummaryRespVO">
  58 + SELECT DATE_FORMAT(contract.order_date, '%Y%m') AS time,
  59 + COUNT(DISTINCT contract.id) AS contract_count,
  60 + IFNULL(SUM(contract.total_price), 0) AS contract_price,
  61 + IFNULL(SUM(receivable.receivable_price), 0) AS receivable_price,
  62 + IFNULL(SUM(contract.total_price), 0) - IFNULL(SUM(receivable.receivable_price), 0) AS unreceived_price
  63 + FROM crm_contract AS contract
  64 + LEFT JOIN (
  65 + SELECT contract_id,
  66 + IFNULL(SUM(price), 0) AS receivable_price
  67 + FROM crm_receivable
  68 + WHERE deleted = 0
  69 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
  70 + GROUP BY contract_id
  71 + ) AS receivable ON contract.id = receivable.contract_id
  72 + WHERE contract.deleted = 0
  73 + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
  74 + AND contract.owner_user_id in
  75 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  76 + #{userId}
  77 + </foreach>
  78 + AND contract.order_date between #{times[0],javaType=java.time.LocalDateTime} and
  79 + #{times[1],javaType=java.time.LocalDateTime}
  80 + GROUP BY time
  81 + </select>
  82 +
56 </mapper> 83 </mapper>
yudao-module-crm/src/main/resources/mapper/statistics/CrmStatisticsPerformanceTargetMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceTargetMapper">
  4 +
  5 + <resultMap id="CrmStatisticsPerformanceTargetActualResultMap"
  6 + type="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetActualRespVO">
  7 + <result property="month" column="month"/>
  8 + <result property="currentPrice" column="current_price"/>
  9 + </resultMap>
  10 +
  11 + <select id="selectContractActualByMonth"
  12 + resultMap="CrmStatisticsPerformanceTargetActualResultMap">
  13 + SELECT MONTH(order_date) AS month,
  14 + IFNULL(SUM(total_price), 0) AS current_price
  15 + FROM crm_contract
  16 + WHERE deleted = 0
  17 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
  18 + AND owner_user_id IN
  19 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  20 + #{userId}
  21 + </foreach>
  22 + AND YEAR(order_date) = #{year}
  23 + GROUP BY MONTH(order_date)
  24 + </select>
  25 +
  26 + <select id="selectReceivableActualByMonth"
  27 + resultMap="CrmStatisticsPerformanceTargetActualResultMap">
  28 + SELECT MONTH(return_time) AS month,
  29 + IFNULL(SUM(price), 0) AS current_price
  30 + FROM crm_receivable
  31 + WHERE deleted = 0
  32 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
  33 + AND owner_user_id IN
  34 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  35 + #{userId}
  36 + </foreach>
  37 + AND YEAR(return_time) = #{year}
  38 + GROUP BY MONTH(return_time)
  39 + </select>
  40 +
  41 +</mapper>
yudao-module-crm/src/main/resources/mapper/statistics/CrmStatisticsProductMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsProductMapper">
  4 +
  5 + <sql id="ProductStatisticsWhere">
  6 + contract.deleted = 0
  7 + AND contract_product.deleted = 0
  8 + AND product.deleted = 0
  9 + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
  10 + AND contract.owner_user_id IN
  11 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  12 + #{userId}
  13 + </foreach>
  14 + AND contract.order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  15 + <if test="categoryIds != null and categoryIds.size() > 0">
  16 + AND product.category_id IN
  17 + <foreach collection="categoryIds" item="categoryId" open="(" close=")" separator=",">
  18 + #{categoryId}
  19 + </foreach>
  20 + </if>
  21 + <if test="productId != null">
  22 + AND product.id = #{productId}
  23 + </if>
  24 + </sql>
  25 +
  26 + <select id="selectProductSalesList"
  27 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO">
  28 + SELECT category.id AS category_id,
  29 + IFNULL(category.name, '未分类') AS category_name,
  30 + product.id AS product_id,
  31 + product.name AS product_name,
  32 + contract.id AS contract_id,
  33 + contract.no AS contract_no,
  34 + contract.name AS contract_name,
  35 + contract.owner_user_id,
  36 + owner.nickname AS owner_user_name,
  37 + customer.id AS customer_id,
  38 + customer.name AS customer_name,
  39 + contract_product.product_price,
  40 + contract_product.count AS product_count,
  41 + contract_product.total_price AS product_total_price
  42 + FROM crm_contract_product AS contract_product
  43 + INNER JOIN crm_contract AS contract ON contract_product.contract_id = contract.id
  44 + INNER JOIN crm_product AS product ON contract_product.product_id = product.id
  45 + LEFT JOIN crm_product_category AS category ON product.category_id = category.id AND category.deleted = 0
  46 + LEFT JOIN crm_customer AS customer ON contract.customer_id = customer.id AND customer.deleted = 0
  47 + LEFT JOIN system_users AS owner ON contract.owner_user_id = owner.id AND owner.deleted = 0
  48 + WHERE <include refid="ProductStatisticsWhere"/>
  49 + ORDER BY category.id ASC, product.id ASC, contract.order_date DESC
  50 + </select>
  51 +
  52 + <select id="selectProductCategorySummary"
  53 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO">
  54 + SELECT category.id AS category_id,
  55 + IFNULL(category.name, '未分类') AS category_name,
  56 + COUNT(DISTINCT contract.id) AS contract_count,
  57 + IFNULL(SUM(contract_product.count), 0) AS product_count,
  58 + IFNULL(SUM(contract_product.total_price), 0) AS product_total_price
  59 + FROM crm_contract_product AS contract_product
  60 + INNER JOIN crm_contract AS contract ON contract_product.contract_id = contract.id
  61 + INNER JOIN crm_product AS product ON contract_product.product_id = product.id
  62 + LEFT JOIN crm_product_category AS category ON product.category_id = category.id AND category.deleted = 0
  63 + WHERE <include refid="ProductStatisticsWhere"/>
  64 + GROUP BY category.id, category.name
  65 + ORDER BY product_count DESC, product_total_price DESC
  66 + </select>
  67 +
  68 +</mapper>
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/checkrecord/MesDvCheckRecordLineController.java
@@ -83,6 +83,16 @@ public class MesDvCheckRecordLineController { @@ -83,6 +83,16 @@ public class MesDvCheckRecordLineController {
83 return success(new PageResult<>(buildCheckRecordLineRespVOList(pageResult.getList()), pageResult.getTotal())); 83 return success(new PageResult<>(buildCheckRecordLineRespVOList(pageResult.getList()), pageResult.getTotal()));
84 } 84 }
85 85
  86 + @GetMapping("/list-by-record-id")
  87 + @Operation(summary = "获得指定点检记录的明细列表")
  88 + @Parameter(name = "recordId", description = "点检记录编号", required = true)
  89 + @PreAuthorize("@ss.hasPermission('mes:dv-check-record:query')")
  90 + public CommonResult<List<MesDvCheckRecordLineRespVO>> getCheckRecordLineListByRecordId(
  91 + @RequestParam("recordId") Long recordId) {
  92 + List<MesDvCheckRecordLineDO> list = checkRecordLineService.getCheckRecordLineListByRecordId(recordId);
  93 + return success(buildCheckRecordLineRespVOList(list));
  94 + }
  95 +
86 // ==================== 拼接 VO ==================== 96 // ==================== 拼接 VO ====================
87 97
88 private List<MesDvCheckRecordLineRespVO> buildCheckRecordLineRespVOList(List<MesDvCheckRecordLineDO> list) { 98 private List<MesDvCheckRecordLineRespVO> buildCheckRecordLineRespVOList(List<MesDvCheckRecordLineDO> list) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/maintenrecord/MesDvMaintenRecordLineController.java
@@ -89,6 +89,16 @@ public class MesDvMaintenRecordLineController { @@ -89,6 +89,16 @@ public class MesDvMaintenRecordLineController {
89 return success(new PageResult<>(buildMaintenRecordLineRespVOList(pageResult.getList()), pageResult.getTotal())); 89 return success(new PageResult<>(buildMaintenRecordLineRespVOList(pageResult.getList()), pageResult.getTotal()));
90 } 90 }
91 91
  92 + @GetMapping("/list-by-record-id")
  93 + @Operation(summary = "获得指定保养记录的明细列表")
  94 + @Parameter(name = "recordId", description = "保养记录编号", required = true)
  95 + @PreAuthorize("@ss.hasPermission('mes:dv-mainten-record:query')")
  96 + public CommonResult<List<MesDvMaintenRecordLineRespVO>> getMaintenRecordLineListByRecordId(
  97 + @RequestParam("recordId") Long recordId) {
  98 + List<MesDvMaintenRecordLineDO> list = maintenRecordLineService.getMaintenRecordLineListByRecordId(recordId);
  99 + return success(buildMaintenRecordLineRespVOList(list));
  100 + }
  101 +
92 @GetMapping("/export-excel") 102 @GetMapping("/export-excel")
93 @Operation(summary = "导出设备保养记录明细 Excel") 103 @Operation(summary = "导出设备保养记录明细 Excel")
94 @PreAuthorize("@ss.hasPermission('mes:dv-mainten-record:export')") 104 @PreAuthorize("@ss.hasPermission('mes:dv-mainten-record:export')")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/repair/MesDvRepairLineController.java
@@ -89,6 +89,16 @@ public class MesDvRepairLineController { @@ -89,6 +89,16 @@ public class MesDvRepairLineController {
89 return success(new PageResult<>(buildRepairLineRespVOList(pageResult.getList()), pageResult.getTotal())); 89 return success(new PageResult<>(buildRepairLineRespVOList(pageResult.getList()), pageResult.getTotal()));
90 } 90 }
91 91
  92 + @GetMapping("/list-by-repair-id")
  93 + @Operation(summary = "获得指定维修工单的明细列表")
  94 + @Parameter(name = "repairId", description = "维修工单编号", required = true)
  95 + @PreAuthorize("@ss.hasPermission('mes:dv-repair:query')")
  96 + public CommonResult<List<MesDvRepairLineRespVO>> getRepairLineListByRepairId(
  97 + @RequestParam("repairId") Long repairId) {
  98 + List<MesDvRepairLineDO> list = repairLineService.getRepairLineListByRepairId(repairId);
  99 + return success(buildRepairLineRespVOList(list));
  100 + }
  101 +
92 @GetMapping("/export-excel") 102 @GetMapping("/export-excel")
93 @Operation(summary = "导出维修工单行 Excel") 103 @Operation(summary = "导出维修工单行 Excel")
94 @PreAuthorize("@ss.hasPermission('mes:dv-repair:export')") 104 @PreAuthorize("@ss.hasPermission('mes:dv-repair:export')")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -96,6 +96,16 @@ public class MesProWorkOrderBomController { @@ -96,6 +96,16 @@ public class MesProWorkOrderBomController {
96 return success(new PageResult<>(buildWorkOrderBomRespVOList(pageResult.getList()), pageResult.getTotal())); 96 return success(new PageResult<>(buildWorkOrderBomRespVOList(pageResult.getList()), pageResult.getTotal()));
97 } 97 }
98 98
  99 + @GetMapping("/list-by-work-order-id")
  100 + @Operation(summary = "获得工单 BOM 列表")
  101 + @Parameter(name = "workOrderId", description = "工单编号", required = true)
  102 + @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
  103 + public CommonResult<List<MesProWorkOrderBomRespVO>> getWorkOrderBomListByWorkOrderId(
  104 + @RequestParam("workOrderId") Long workOrderId) {
  105 + List<MesProWorkOrderBomDO> list = workOrderBomService.getWorkOrderBomListByWorkOrderId(workOrderId);
  106 + return success(buildWorkOrderBomRespVOList(list));
  107 + }
  108 +
99 @GetMapping("/item-list-by-work-order-id") 109 @GetMapping("/item-list-by-work-order-id")
100 @Operation(summary = "获得工单物料需求列表") 110 @Operation(summary = "获得工单物料需求列表")
101 @Parameter(name = "workOrderId", description = "工单编号", required = true) 111 @Parameter(name = "workOrderId", description = "工单编号", required = true)
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/vo/result/MesWmStockTakingTaskResultRespVO.java
@@ -77,6 +77,10 @@ public class MesWmStockTakingTaskResultRespVO { @@ -77,6 +77,10 @@ public class MesWmStockTakingTaskResultRespVO {
77 @ExcelProperty("账面数量") 77 @ExcelProperty("账面数量")
78 private BigDecimal quantity; 78 private BigDecimal quantity;
79 79
  80 + @Schema(description = "实盘数量", example = "98.000000")
  81 + @ExcelProperty("实盘数量")
  82 + private BigDecimal takingQuantity;
  83 +
80 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 84 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
81 @ExcelProperty("创建时间") 85 @ExcelProperty("创建时间")
82 private LocalDateTime createTime; 86 private LocalDateTime createTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/repair/MesDvRepairLineService.java
@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.dv.repair.MesDvRepairLineDO; @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.dv.repair.MesDvRepairLineDO;
7 7
8 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
9 9
  10 +import java.util.List;
  11 +
10 /** 12 /**
11 * MES 维修工单行 Service 接口 13 * MES 维修工单行 Service 接口
12 * 14 *
@@ -53,6 +55,14 @@ public interface MesDvRepairLineService { @@ -53,6 +55,14 @@ public interface MesDvRepairLineService {
53 PageResult<MesDvRepairLineDO> getRepairLinePage(MesDvRepairLinePageReqVO pageReqVO); 55 PageResult<MesDvRepairLineDO> getRepairLinePage(MesDvRepairLinePageReqVO pageReqVO);
54 56
55 /** 57 /**
  58 + * 获得指定维修工单的明细列表
  59 + *
  60 + * @param repairId 维修工单编号
  61 + * @return 明细列表
  62 + */
  63 + List<MesDvRepairLineDO> getRepairLineListByRepairId(Long repairId);
  64 +
  65 + /**
56 * 根据维修工单编号删除所有行 66 * 根据维修工单编号删除所有行
57 * 67 *
58 * @param repairId 维修工单编号 68 * @param repairId 维修工单编号
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/repair/MesDvRepairLineServiceImpl.java
@@ -12,6 +12,8 @@ import org.springframework.context.annotation.Lazy; @@ -12,6 +12,8 @@ import org.springframework.context.annotation.Lazy;
12 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
13 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
14 14
  15 +import java.util.List;
  16 +
15 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 18 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
17 19
@@ -100,6 +102,11 @@ public class MesDvRepairLineServiceImpl implements MesDvRepairLineService { @@ -100,6 +102,11 @@ public class MesDvRepairLineServiceImpl implements MesDvRepairLineService {
100 } 102 }
101 103
102 @Override 104 @Override
  105 + public List<MesDvRepairLineDO> getRepairLineListByRepairId(Long repairId) {
  106 + return repairLineMapper.selectListByRepairId(repairId);
  107 + }
  108 +
  109 + @Override
103 public void deleteByRepairId(Long repairId) { 110 public void deleteByRepairId(Long repairId) {
104 repairLineMapper.deleteByRepairId(repairId); 111 repairLineMapper.deleteByRepairId(repairId);
105 } 112 }
yudao-server/src/main/resources/application.yaml
@@ -273,7 +273,7 @@ yudao: @@ -273,7 +273,7 @@ yudao:
273 permit-all_urls: 273 permit-all_urls:
274 - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录 274 - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录
275 api-encrypt: 275 api-encrypt:
276 - enable: true # 是否开启 API 加密 276 + enable: false # 是否开启 API 加密。默认关闭,避免低版本 JDK 8(8u120 以下)启动时 bcprov 的 JCE 认证报错
277 algorithm: AES # 加密算法,支持 AES、RSA 等 277 algorithm: AES # 加密算法,支持 AES、RSA 等
278 request-key: 52549111389893486934626385991395 # 【AES 案例】请求加密的秘钥,,必须 16、24、32 位 278 request-key: 52549111389893486934626385991395 # 【AES 案例】请求加密的秘钥,,必须 16、24、32 位
279 response-key: 96103715984234343991809655248883 # 【AES 案例】响应加密的秘钥,AES 案例,必须 16、24、32 位 279 response-key: 96103715984234343991809655248883 # 【AES 案例】响应加密的秘钥,AES 案例,必须 16、24、32 位