mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-09-07 13:42:13 +08:00
Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk21
This commit is contained in:
@@ -15,6 +15,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态");
|
||||
ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, "更新合同审核状态失败,原因:合同不是审核中状态");
|
||||
ErrorCode CONTRACT_NO_EXISTS = new ErrorCode(1_020_000_004, "生成合同序列号重复,请重试");
|
||||
ErrorCode CONTRACT_DELETE_FAIL = new ErrorCode(1_020_000_005, "删除合同失败,原因:有被回款所使用");
|
||||
|
||||
// ========== 线索管理 1-020-001-000 ==========
|
||||
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
|
||||
@@ -40,6 +41,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试");
|
||||
ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态");
|
||||
ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, "创建回款失败,原因:回款金额超出合同金额,目前剩余可退:{} 元");
|
||||
ErrorCode RECEIVABLE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_020_004_008, "删除回款失败,原因:回款审批已通过");
|
||||
|
||||
// ========== 回款计划 1-020-005-000 ==========
|
||||
ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
|
||||
@@ -72,6 +74,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限");
|
||||
ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人");
|
||||
ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限");
|
||||
ErrorCode CRM_PERMISSION_CREATE_FAIL_EXISTS = new ErrorCode(1_020_007_009, "同时添加数据权限失败,原因:用户【{}】已有模块【{}】数据【{}】的【{}】权限");
|
||||
|
||||
// ========== 产品 1_020_008_000 ==========
|
||||
ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
|
||||
@@ -100,4 +103,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在");
|
||||
ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限");
|
||||
|
||||
// ========== 数据统计 1_020_014_000 ==========
|
||||
|
||||
}
|
||||
|
@@ -142,11 +142,11 @@ public interface LogRecordConstants {
|
||||
|
||||
String CRM_RECEIVABLE_TYPE = "CRM 回款";
|
||||
String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款";
|
||||
String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
|
||||
String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款";
|
||||
String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款";
|
||||
String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
|
||||
String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款: {_DIFF{#updateReqVO}}";
|
||||
String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
|
||||
String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
|
||||
String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款";
|
||||
String CRM_RECEIVABLE_SUBMIT_SUB_TYPE = "提交回款审批";
|
||||
String CRM_RECEIVABLE_SUBMIT_SUCCESS = "提交编号为【{{#receivableNo}}】的回款审批成功";
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.crm.enums.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -50,4 +51,10 @@ public enum CrmPermissionLevelEnum implements IntArrayValuable {
|
||||
return ObjUtil.equal(WRITE.level, level);
|
||||
}
|
||||
|
||||
public static String getNameByLevel(Integer level) {
|
||||
CrmPermissionLevelEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmPermissionLevelEnum.values()),
|
||||
item -> ObjUtil.equal(item.level, level));
|
||||
return typeEnum == null ? null : typeEnum.getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -66,13 +66,13 @@ public class CrmBusinessSaveReqVO {
|
||||
private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
private List<Product> products;
|
||||
private List<BusinessProduct> businessProducts;
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Product {
|
||||
public static class BusinessProduct {
|
||||
|
||||
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
|
||||
@NotNull(message = "产品编号不能为空")
|
||||
|
@@ -3,10 +3,14 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - 商机转移 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CrmBusinessTransferReqVO {
|
||||
|
||||
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
|
@@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 联系人转移 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CrmContactTransferReqVO {
|
||||
|
||||
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
|
@@ -3,12 +3,16 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 合同转移 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CrmContractTransferReqVO {
|
||||
|
||||
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
|
@@ -11,9 +11,7 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
@@ -38,7 +36,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -49,7 +46,6 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 客户")
|
||||
@@ -67,8 +63,6 @@ public class CrmCustomerController {
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DictDataApi dictDataApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建客户")
|
||||
@@ -142,7 +136,7 @@ public class CrmCustomerController {
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(list,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 1.2 获取距离进入公海的时间
|
||||
@@ -265,25 +259,7 @@ public class CrmCustomerController {
|
||||
.areaId(null).detailAddress("").remark("").build()
|
||||
);
|
||||
// 输出
|
||||
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list, builderSelectMap());
|
||||
}
|
||||
|
||||
private List<KeyValue<ExcelColumn, List<String>>> builderSelectMap() {
|
||||
List<KeyValue<ExcelColumn, List<String>>> selectMap = new ArrayList<>();
|
||||
// 获取地区下拉数据
|
||||
// TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么?
|
||||
Area area = AreaUtils.getArea(Area.ID_CHINA);
|
||||
selectMap.add(new KeyValue<>(ExcelColumn.G, AreaUtils.getAreaNodePathList(area.getChildren())));
|
||||
// 获取客户所属行业
|
||||
List<String> customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY);
|
||||
selectMap.add(new KeyValue<>(ExcelColumn.I, customerIndustries));
|
||||
// 获取客户等级
|
||||
List<String> customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL);
|
||||
selectMap.add(new KeyValue<>(ExcelColumn.J, customerLevels));
|
||||
// 获取客户来源
|
||||
List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE);
|
||||
selectMap.add(new KeyValue<>(ExcelColumn.K, customerSources));
|
||||
return selectMap;
|
||||
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -41,6 +43,7 @@ public class CrmCustomerImportExcelVO {
|
||||
private String email;
|
||||
|
||||
@ExcelProperty(value = "地区", converter = AreaConvert.class)
|
||||
@ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME)
|
||||
private Integer areaId;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
@@ -48,14 +51,17 @@ public class CrmCustomerImportExcelVO {
|
||||
|
||||
@ExcelProperty(value = "所属行业", converter = DictConvert.class)
|
||||
@DictFormat(CRM_CUSTOMER_INDUSTRY)
|
||||
@ExcelColumnSelect(dictType = CRM_CUSTOMER_INDUSTRY)
|
||||
private Integer industryId;
|
||||
|
||||
@ExcelProperty(value = "客户等级", converter = DictConvert.class)
|
||||
@DictFormat(CRM_CUSTOMER_LEVEL)
|
||||
@ExcelColumnSelect(dictType = CRM_CUSTOMER_LEVEL)
|
||||
private Integer level;
|
||||
|
||||
@ExcelProperty(value = "客户来源", converter = DictConvert.class)
|
||||
@DictFormat(CRM_CUSTOMER_SOURCE)
|
||||
@ExcelColumnSelect(dictType = CRM_CUSTOMER_SOURCE)
|
||||
private Integer source;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
|
@@ -5,6 +5,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户转移 Request VO")
|
||||
@Data
|
||||
public class CrmCustomerTransferReqVO {
|
||||
@@ -28,4 +30,10 @@ public class CrmCustomerTransferReqVO {
|
||||
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer oldOwnerPermissionLevel;
|
||||
|
||||
/**
|
||||
* 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。选中时,也一起转移
|
||||
*/
|
||||
@Schema(description = "同时转移", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
private List<Integer> toBizTypes;
|
||||
|
||||
}
|
||||
|
@@ -1,16 +1,24 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
@@ -27,13 +35,11 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
@@ -50,7 +56,6 @@ public class CrmPermissionController {
|
||||
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
@@ -61,9 +66,8 @@ public class CrmPermissionController {
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据权限")
|
||||
@PreAuthorize("@ss.hasPermission('crm:permission:create')")
|
||||
@CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER)
|
||||
public CommonResult<Boolean> addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) {
|
||||
permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class));
|
||||
public CommonResult<Boolean> create(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {
|
||||
permissionService.createPermission(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
@@ -1,14 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.permission.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 数据权限创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CrmPermissionCreateReqVO extends CrmPermissionBaseVO {
|
||||
|
||||
}
|
@@ -1,6 +1,10 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.permission.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -8,11 +12,29 @@ import java.util.Set;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 数据权限 Response VO")
|
||||
@Data
|
||||
public class CrmPermissionRespVO extends CrmPermissionBaseVO {
|
||||
public class CrmPermissionRespVO {
|
||||
|
||||
@Schema(description = "数据权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "CRM 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@InEnum(CrmBizTypeEnum.class)
|
||||
@NotNull(message = "CRM 类型不能为空")
|
||||
private Integer bizType;
|
||||
|
||||
@Schema(description = "CRM 类型数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "CRM 类型数据编号不能为空")
|
||||
private Long bizId;
|
||||
|
||||
@Schema(description = "权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@InEnum(CrmPermissionLevelEnum.class)
|
||||
@NotNull(message = "权限级别不能为空")
|
||||
private Integer level;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||
private String nickname;
|
||||
|
||||
|
@@ -8,14 +8,11 @@ import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 数据权限 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 数据权限创建/更新 Request VO")
|
||||
@Data
|
||||
public class CrmPermissionBaseVO {
|
||||
public class CrmPermissionSaveReqVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
@@ -35,4 +32,11 @@ public class CrmPermissionBaseVO {
|
||||
@NotNull(message = "权限级别不能为空")
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。
|
||||
* 选中时,同时添加对应的权限
|
||||
*/
|
||||
@Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
private List<Integer> toBizTypes;
|
||||
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -8,53 +9,69 @@ import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// TODO @puhui999:缺导出
|
||||
@Schema(description = "管理后台 - CRM 回款计划 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class CrmReceivablePlanRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("期数")
|
||||
private Integer period;
|
||||
|
||||
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("客户编号")
|
||||
private Long customerId;
|
||||
@Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
|
||||
@ExcelProperty("客户名字")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("合同编号")
|
||||
private Long contractId;
|
||||
@Schema(description = "合同编号", example = "Q110")
|
||||
@ExcelProperty("合同编号")
|
||||
private String contractNo;
|
||||
|
||||
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("负责人编号")
|
||||
private Long ownerUserId;
|
||||
@Schema(description = "负责人", example = "test")
|
||||
@ExcelProperty("负责人")
|
||||
private String ownerUserName;
|
||||
|
||||
@Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
|
||||
@ExcelProperty("计划回款日期")
|
||||
private LocalDateTime returnTime;
|
||||
|
||||
@Schema(description = "计划回款方式", example = "1")
|
||||
@ExcelProperty("计划回款方式")
|
||||
private Integer returnType;
|
||||
|
||||
@Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
|
||||
@ExcelProperty("计划回款金额")
|
||||
private BigDecimal price;
|
||||
|
||||
@Schema(description = "回款编号", example = "19852")
|
||||
@ExcelProperty("回款编号")
|
||||
private Long receivableId;
|
||||
@Schema(description = "回款信息")
|
||||
@ExcelProperty("回款信息")
|
||||
private CrmReceivableRespVO receivable;
|
||||
|
||||
@Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("提前几天提醒")
|
||||
private Integer remindDays;
|
||||
|
||||
@Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
|
||||
@ExcelProperty("提醒日期")
|
||||
private LocalDateTime remindTime;
|
||||
|
||||
@Schema(description = "备注", example = "备注")
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -8,37 +9,47 @@ import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// TODO 芋艿:导出的 VO,可以考虑使用 @Excel 注解,实现导出功能
|
||||
@Schema(description = "管理后台 - CRM 回款 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class CrmReceivableRespVO {
|
||||
|
||||
@Schema(description = "编号", example = "25787")
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "回款编号", example = "31177")
|
||||
@ExcelProperty("回款编号")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "回款计划编号", example = "1024")
|
||||
@ExcelProperty("回款计划编号")
|
||||
private Long planId;
|
||||
|
||||
@Schema(description = "回款方式", example = "2")
|
||||
@ExcelProperty("回款方式")
|
||||
private Integer returnType;
|
||||
|
||||
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
|
||||
@ExcelProperty("回款金额")
|
||||
private BigDecimal price;
|
||||
|
||||
@Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
|
||||
@ExcelProperty("计划回款日期")
|
||||
private LocalDateTime returnTime;
|
||||
|
||||
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("客户编号")
|
||||
private Long customerId;
|
||||
@Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
|
||||
@ExcelProperty("客户名字")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty("合同编号")
|
||||
private Long contractId;
|
||||
@Schema(description = "合同信息")
|
||||
@ExcelProperty("合同信息")
|
||||
private CrmContractRespVO contract;
|
||||
|
||||
@Schema(description = "负责人的用户编号", example = "25682")
|
||||
@@ -56,20 +67,26 @@ public class CrmReceivableRespVO {
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@ExcelProperty("审批状态")
|
||||
private Integer auditStatus;
|
||||
|
||||
@Schema(description = "备注", example = "备注")
|
||||
@Schema(description = "工作流编号", example = "备注")
|
||||
@ExcelProperty("工作流编号")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(description = "创建人", example = "25682")
|
||||
@ExcelProperty("创建人")
|
||||
private String creator;
|
||||
@Schema(description = "创建人名字", example = "test")
|
||||
@ExcelProperty("创建人名字")
|
||||
private String creatorName;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,55 @@
|
||||
# == 1. 客户总量分析 ==
|
||||
### 1.1 客户总量分析(按日期)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 1.2 客户总量统计(按用户)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
# == 2. 客户跟进次数分析 ==
|
||||
### 2.1 客户跟进次数分析(按日期)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 2.2 客户总量统计(按用户)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
# == 3. 客户跟进方式分析 ==
|
||||
### 3.1 客户跟进方式分析
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-type?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
# == 4. 客户成交周期 ==
|
||||
### 4.1 合同摘要信息(客户转化率页面)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
# == 5. 客户成交周期 ==
|
||||
### 5.1 获取客户公海分析(按日期)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-date?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 5.2 获取客户公海分析(按用户)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
# == 6. 客户成交周期 ==
|
||||
### 6.1 客户成交周期(按日期)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 6.2 获取客户成交周期(按用户)
|
||||
GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
@@ -0,0 +1,101 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 客户统计")
|
||||
@RestController
|
||||
@RequestMapping("/crm/statistics-customer")
|
||||
@Validated
|
||||
public class CrmStatisticsCustomerController {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsCustomerService customerService;
|
||||
|
||||
@GetMapping("/get-customer-summary-by-date")
|
||||
@Operation(summary = "获取客户总量分析(按日期)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsCustomerSummaryByDateRespVO>> getCustomerSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getCustomerSummaryByDate(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-summary-by-user")
|
||||
@Operation(summary = "获取客户总量分析(按用户)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsCustomerSummaryByUserRespVO>> getCustomerSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getCustomerSummaryByUser(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-follow-up-summary-by-date")
|
||||
@Operation(summary = "获取客户跟进次数分析(按日期)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsFollowUpSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getFollowUpSummaryByDate(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-follow-up-summary-by-user")
|
||||
@Operation(summary = "获取客户跟进次数分析(按用户)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsFollowUpSummaryByUserRespVO>> getFollowUpSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getFollowUpSummaryByUser(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-follow-up-summary-by-type")
|
||||
@Operation(summary = "获取客户跟进次数分析(按类型)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsFollowUpSummaryByTypeRespVO>> getFollowUpSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getFollowUpSummaryByType(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-contract-summary")
|
||||
@Operation(summary = "获取客户的首次合同、回款信息列表", description = "用于【客户转化率】页面")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsCustomerContractSummaryRespVO>> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getContractSummary(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-pool-summary-by-date")
|
||||
@Operation(summary = "获取公海客户分析(按日期)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsPoolSummaryByDateRespVO>> getPoolSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getPoolSummaryByDate(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-pool-summary-by-user")
|
||||
@Operation(summary = "获取公海客户分析(按用户)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsPoolSummaryByUserRespVO>> getPoolSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getPoolSummaryByUser(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-deal-cycle-by-date")
|
||||
@Operation(summary = "获取客户成交周期(按日期)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsCustomerDealCycleByDateRespVO>> getCustomerDealCycleByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getCustomerDealCycleByDate(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-deal-cycle-by-user")
|
||||
@Operation(summary = "获取客户成交周期(按用户)")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
|
||||
public CommonResult<List<CrmStatisticsCustomerDealCycleByUserRespVO>> getCustomerDealCycleByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
|
||||
return success(customerService.getCustomerDealCycleByUser(reqVO));
|
||||
}
|
||||
|
||||
// TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析;
|
||||
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
@Tag(name = "管理后台 - CRM 员工业绩统计")
|
||||
@RestController
|
||||
@RequestMapping("/crm/statistics-performance")
|
||||
@Validated
|
||||
public class CrmStatisticsPerformanceController {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsPerformanceService performanceService;
|
||||
|
||||
@GetMapping("/get-contract-count-performance")
|
||||
@Operation(summary = "合同数量统计", description = "用于【合同数量分析】页面")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')")
|
||||
public CommonResult<List<CrmStatisticsPerformanceRespVO>> getContractCountPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {
|
||||
return success(performanceService.getContractCountPerformance(performanceReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-contract-price-performance")
|
||||
@Operation(summary = "合同金额统计")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')")
|
||||
public CommonResult<List<CrmStatisticsPerformanceRespVO>> getContractPriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {
|
||||
return success(performanceService.getContractPricePerformance(performanceReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-receivable-price-performance")
|
||||
@Operation(summary = "回款金额统计")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')")
|
||||
public CommonResult<List<CrmStatisticsPerformanceRespVO>> getReceivablePriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) {
|
||||
return success(performanceService.getReceivablePricePerformance(performanceReqVO));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticsPortraitReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPortraitService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 客户画像")
|
||||
@RestController
|
||||
@RequestMapping("/crm/statistics-portrait")
|
||||
@Validated
|
||||
public class CrmStatisticsPortraitController {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsPortraitService statisticsPortraitService;
|
||||
|
||||
@GetMapping("/get-customer-area-summary")
|
||||
@Operation(summary = "获取客户地区统计数据", description = "用于【城市分布分析】页面")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
|
||||
public CommonResult<List<CrmStatisticCustomerAreaRespVO>> getCustomerAreaSummary(@Valid CrmStatisticsPortraitReqVO reqVO) {
|
||||
return success(statisticsPortraitService.getCustomerSummaryByArea(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-industry-summary")
|
||||
@Operation(summary = "获取客户行业统计数据", description = "用于【客户行业分析】页面")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
|
||||
public CommonResult<List<CrmStatisticCustomerIndustryRespVO>> getCustomerIndustrySummary(@Valid CrmStatisticsPortraitReqVO reqVO) {
|
||||
return success(statisticsPortraitService.getCustomerSummaryByIndustry(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-level-summary")
|
||||
@Operation(summary = "获取客户级别统计数据", description = "用于【客户级别分析】页面")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
|
||||
public CommonResult<List<CrmStatisticCustomerLevelRespVO>> getCustomerLevelSummary(@Valid CrmStatisticsPortraitReqVO reqVO) {
|
||||
return success(statisticsPortraitService.getCustomerSummaryByLevel(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-source-summary")
|
||||
@Operation(summary = "获取客户来源统计数据", description = "用于【客户来源分析】页面")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
|
||||
public CommonResult<List<CrmStatisticCustomerSourceRespVO>> getCustomerSourceSummary(@Valid CrmStatisticsPortraitReqVO reqVO) {
|
||||
return success(statisticsPortraitService.getCustomerSummaryBySource(reqVO));
|
||||
}
|
||||
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankingService;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -18,7 +18,6 @@ import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
@Tag(name = "管理后台 - CRM 排行榜统计")
|
||||
@RestController
|
||||
@RequestMapping("/crm/statistics-rank")
|
||||
@@ -26,62 +25,62 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
public class CrmStatisticsRankController {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsRankingService rankingService;
|
||||
private CrmStatisticsRankService rankService;
|
||||
|
||||
@GetMapping("/get-contract-price-rank")
|
||||
@Operation(summary = "获得合同金额排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getContractPriceRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getContractPriceRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-receivable-price-rank")
|
||||
@Operation(summary = "获得回款金额排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getReceivablePriceRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getReceivablePriceRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-contract-count-rank")
|
||||
@Operation(summary = "获得签约合同数量排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getContractCountRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getContractCountRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-product-sales-rank")
|
||||
@Operation(summary = "获得产品销量排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getProductSalesRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getProductSalesRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-customer-count-rank")
|
||||
@Operation(summary = "获得新增客户数排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getCustomerCountRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getCustomerCountRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-contacts-count-rank")
|
||||
@Operation(summary = "获得新增联系人数排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getContactsCountRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getContactsCountRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-follow-count-rank")
|
||||
@Operation(summary = "获得跟进次数排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getFollowCountRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getFollowCountRank(rankingReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-follow-customer-count-rank")
|
||||
@Operation(summary = "获得跟进客户数排行榜")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
|
||||
public CommonResult<List<CrmStatisticsRanKRespVO>> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankingService.getFollowCustomerCountRank(rankingReqVO));
|
||||
public CommonResult<List<CrmStatisticsRankRespVO>> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
|
||||
return success(rankService.getFollowCustomerCountRank(rankingReqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户客户统计响应 Base Response VO
|
||||
*
|
||||
* 目的:可以统一拼接子 VO 的 ownerUserId、ownerUserName 属性
|
||||
*/
|
||||
@Data
|
||||
public class CrmStatisticsCustomerByUserBaseRespVO {
|
||||
|
||||
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
|
||||
private String ownerUserName;
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户转化率分析 VO")
|
||||
@Data
|
||||
public class CrmStatisticsCustomerContractSummaryRespVO {
|
||||
|
||||
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示合同")
|
||||
private String contractName;
|
||||
|
||||
@Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
|
||||
private BigDecimal receivablePrice;
|
||||
|
||||
@Schema(description = "客户行业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer industryId;
|
||||
|
||||
@Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer source;
|
||||
|
||||
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long ownerUserId;
|
||||
@Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
|
||||
private String ownerUserName;
|
||||
|
||||
@Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private String creator;
|
||||
@Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码")
|
||||
private String creatorUserName;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-01 13:24:26")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00")
|
||||
private LocalDateTime orderDate;
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户成交周期分析(按日期) VO")
|
||||
@Data
|
||||
public class CrmStatisticsCustomerDealCycleByDateRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
|
||||
private Double customerDealCycle;
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 成交周期分析(按用户) VO")
|
||||
@Data
|
||||
public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
|
||||
|
||||
@Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
|
||||
private Double customerDealCycle;
|
||||
|
||||
@Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerDealCount;
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 数据统计的员工客户分析 Request VO")
|
||||
@Data
|
||||
public class CrmStatisticsCustomerReqVO {
|
||||
|
||||
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "部门 id 不能为空")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 负责人用户 id, 当用户为空, 则计算部门下用户
|
||||
*/
|
||||
@Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
|
||||
* 后续,可能会支持选择部分用户进行查询
|
||||
*/
|
||||
@Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
|
||||
private List<Long> userIds;
|
||||
|
||||
@Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
|
||||
private Integer interval;
|
||||
|
||||
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Size(min = 2, max = 2, message = "请选择时间范围")
|
||||
private LocalDateTime[] times;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户总量分析(按日期) VO")
|
||||
@Data
|
||||
public class CrmStatisticsCustomerSummaryByDateRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerCreateCount;
|
||||
|
||||
@Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerDealCount;
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户总量分析(按用户) VO")
|
||||
@Data
|
||||
public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
|
||||
|
||||
@Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerCreateCount;
|
||||
|
||||
@Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerDealCount;
|
||||
|
||||
@Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
|
||||
private BigDecimal contractPrice;
|
||||
|
||||
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
|
||||
private BigDecimal receivablePrice;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO")
|
||||
@Data
|
||||
public class CrmStatisticsFollowUpSummaryByDateRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer followUpRecordCount;
|
||||
|
||||
@Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer followUpCustomerCount;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO")
|
||||
@Data
|
||||
public class CrmStatisticsFollowUpSummaryByTypeRespVO {
|
||||
|
||||
@Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer followUpType;
|
||||
|
||||
@Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer followUpRecordCount;
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO")
|
||||
@Data
|
||||
public class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
|
||||
|
||||
@Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer followUpRecordCount;
|
||||
|
||||
@Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer followUpCustomerCount;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 公海客户分析(按日期) VO")
|
||||
@Data
|
||||
public class CrmStatisticsPoolSummaryByDateRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "进入公海客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerPutCount;
|
||||
|
||||
@Schema(description = "公海领取客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerTakeCount;
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 公海客户分析(按用户) VO")
|
||||
@Data
|
||||
public class CrmStatisticsPoolSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
|
||||
|
||||
@Schema(description = "进入公海客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerPutCount;
|
||||
|
||||
@Schema(description = "公海领取客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerTakeCount;
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 员工业绩统计 Request VO")
|
||||
@Data
|
||||
public class CrmStatisticsPerformanceReqVO {
|
||||
|
||||
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "部门 id 不能为空")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 负责人用户 id, 当用户为空, 则计算部门下用户
|
||||
*/
|
||||
@Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
|
||||
* <p>
|
||||
* 后续,可能会支持选择部分用户进行查询
|
||||
*/
|
||||
@Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2")
|
||||
private List<Long> userIds;
|
||||
|
||||
// TODO @scholar:应该传递的是 int year;年份
|
||||
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@NotEmpty(message = "时间范围不能为空")
|
||||
private LocalDateTime[] times;
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
@Schema(description = "管理后台 - CRM 员工业绩统计 Response VO")
|
||||
@Data
|
||||
public class CrmStatisticsPerformanceRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "当月统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private BigDecimal currentMonthCount;
|
||||
|
||||
@Schema(description = "上月统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private BigDecimal lastMonthCount;
|
||||
|
||||
@Schema(description = "去年同期统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
private BigDecimal lastYearCount;
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户省份分析 VO")
|
||||
@Data
|
||||
public class CrmStatisticCustomerAreaRespVO {
|
||||
|
||||
@Schema(description = "省份编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer areaId;
|
||||
@Schema(description = "省份名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江省")
|
||||
private String areaName;
|
||||
|
||||
@Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerCount;
|
||||
|
||||
@Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer dealCount;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户行业分析 VO")
|
||||
@Data
|
||||
public class CrmStatisticCustomerIndustryRespVO {
|
||||
|
||||
@Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer industryId;
|
||||
|
||||
@Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerCount;
|
||||
|
||||
@Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer dealCount;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户级别分析 VO")
|
||||
@Data
|
||||
public class CrmStatisticCustomerLevelRespVO {
|
||||
|
||||
@Schema(description = "客户级别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer level;
|
||||
|
||||
@Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerCount;
|
||||
|
||||
@Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer dealCount;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户来源分析 VO")
|
||||
@Data
|
||||
public class CrmStatisticCustomerSourceRespVO {
|
||||
|
||||
@Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer source;
|
||||
|
||||
@Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer customerCount;
|
||||
|
||||
@Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer dealCount;
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户画像 Request VO")
|
||||
@Data
|
||||
public class CrmStatisticsPortraitReqVO {
|
||||
|
||||
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "部门 id 不能为空")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 负责人用户 id, 当用户为空, 则计算部门下用户
|
||||
*/
|
||||
@Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
|
||||
* 后续,可能会支持选择部分用户进行查询
|
||||
*/
|
||||
@Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
|
||||
private List<Long> userIds;
|
||||
|
||||
/**
|
||||
* 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
|
||||
* 并作为参数传递给Mapper
|
||||
*/
|
||||
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] times;
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo;
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
@@ -21,7 +21,7 @@ public class CrmStatisticsRankReqVO {
|
||||
|
||||
/**
|
||||
* userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
|
||||
*
|
||||
* <p>
|
||||
* 后续,可能会支持选择部分用户进行查询
|
||||
*/
|
||||
@Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2")
|
@@ -1,12 +1,14 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo;
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM BI 排行榜统计 Response VO")
|
||||
|
||||
@Schema(description = "管理后台 - CRM 排行榜统计 Response VO")
|
||||
@Data
|
||||
public class CrmStatisticsRanKRespVO {
|
||||
public class CrmStatisticsRankRespVO {
|
||||
|
||||
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long ownerUserId;
|
||||
@@ -22,8 +24,10 @@ public class CrmStatisticsRanKRespVO {
|
||||
*
|
||||
* 1. 金额:合同金额排行、回款金额排行
|
||||
* 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行
|
||||
*
|
||||
* 为什么使用 BigDecimal 的原因:
|
||||
*/
|
||||
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer count;
|
||||
private BigDecimal count;
|
||||
|
||||
}
|
@@ -6,12 +6,14 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商机 Mapper
|
||||
@@ -57,4 +59,10 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
||||
return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId);
|
||||
}
|
||||
|
||||
default List<CrmBusinessDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId){
|
||||
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||
.eq(CrmBusinessDO::getCustomerId, customerId)
|
||||
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -73,4 +73,9 @@ public interface CrmContactMapper extends BaseMapperX<CrmContactDO> {
|
||||
return selectList(CrmContactDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
default List<CrmContactDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return selectList(CrmContactDO::getCustomerId, customerId,
|
||||
CrmContactDO::getOwnerUserId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -117,4 +117,10 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
|
||||
return selectCount(query);
|
||||
}
|
||||
|
||||
default List<CrmContractDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmContractDO>()
|
||||
.eq(CrmContractDO::getCustomerId, customerId)
|
||||
.eq(CrmContractDO::getOwnerUserId, ownerUserId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -53,9 +53,11 @@ public interface CrmPermissionMapper extends BaseMapperX<CrmPermissionDO> {
|
||||
CrmPermissionDO::getUserId, userId);
|
||||
}
|
||||
|
||||
default CrmPermissionDO selectByBizIdAndUserId(Long bizId, Long userId) {
|
||||
return selectOne(CrmPermissionDO::getBizId, bizId,
|
||||
CrmPermissionDO::getUserId, userId);
|
||||
default CrmPermissionDO selectByBizAndUserId(Integer bizType, Long bizId, Long userId) {
|
||||
return selectOne(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.eq(CrmPermissionDO::getBizId, bizId)
|
||||
.eq(CrmPermissionDO::getUserId, userId));
|
||||
}
|
||||
|
||||
default int deletePermission(Integer bizType, Long bizId) {
|
||||
|
@@ -99,4 +99,8 @@ public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
|
||||
return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price"));
|
||||
}
|
||||
|
||||
default Long selectCountByContractId(Long contractId) {
|
||||
return selectCount(CrmReceivableDO::getContractId, contractId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,194 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* CRM 客户分析 Mapper
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsCustomerMapper {
|
||||
|
||||
/**
|
||||
* 新建客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 成交客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 新建客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 成交客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数@param reqVO 请求参数@param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 合同总金额(按用户)
|
||||
* @return 统计数据@return 统计数据@param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 合同回款金额(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进次数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进次数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
|
||||
/**
|
||||
* 首次合同、回款信息(用于【客户转化率】页面)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进次数(按类型)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByTypeRespVO> selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
|
||||
/**
|
||||
* 进入公海客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
// TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表
|
||||
default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
LocalDateTime currrentDate = LocalDateTimeUtil.beginOfDay(reqVO.getTimes()[0]);
|
||||
LocalDateTime endDate = LocalDateTimeUtil.endOfDay(reqVO.getTimes()[1]);
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> voList = new ArrayList<>();
|
||||
while (currrentDate.isBefore(endDate)) {
|
||||
voList.add(new CrmStatisticsPoolSummaryByDateRespVO()
|
||||
.setTime(LocalDateTimeUtil.format(currrentDate, "yyyy-MM-dd"))
|
||||
.setCustomerPutCount(RandomUtil.randomInt(0, 10))
|
||||
.setCustomerTakeCount(RandomUtil.randomInt(0, 10)));
|
||||
currrentDate = currrentDate.plusDays(1);
|
||||
}
|
||||
|
||||
return voList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 公海领取客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
// TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表
|
||||
default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return selectPoolCustomerPutCountByDate(reqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入公海客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
// TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表
|
||||
default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return convertList(reqVO.getUserIds(), userId ->
|
||||
(CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO()
|
||||
.setCustomerPutCount(RandomUtil.randomInt(0, 10))
|
||||
.setCustomerTakeCount(RandomUtil.randomInt(0, 10))
|
||||
.setOwnerUserId(userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 公海领取客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
// TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表
|
||||
default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return selectPoolCustomerPutCountByUser(reqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户成交周期(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 客户成交周期(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 员工业绩分析 Mapper
|
||||
*
|
||||
* @author scholar
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsPerformanceMapper {
|
||||
|
||||
/**
|
||||
* 员工签约合同数量
|
||||
*
|
||||
* @param performanceReqVO 参数
|
||||
* @return 员工签约合同数量
|
||||
*/
|
||||
List<CrmStatisticsPerformanceRespVO> selectContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
|
||||
|
||||
/**
|
||||
* 员工签约合同金额
|
||||
*
|
||||
* @param performanceReqVO 参数
|
||||
* @return 员工签约合同金额
|
||||
*/
|
||||
List<CrmStatisticsPerformanceRespVO> selectContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
|
||||
|
||||
/**
|
||||
* 员工回款金额
|
||||
*
|
||||
* @param performanceReqVO 参数
|
||||
* @return 员工回款金额
|
||||
*/
|
||||
List<CrmStatisticsPerformanceRespVO> selectReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 数据画像 Mapper
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsPortraitMapper {
|
||||
|
||||
List<CrmStatisticCustomerAreaRespVO> selectSummaryListGroupByAreaId(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
List<CrmStatisticCustomerIndustryRespVO> selectCustomerIndustryListGroupByIndustryId(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
List<CrmStatisticCustomerSourceRespVO> selectCustomerSourceListGroupBySource(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
List<CrmStatisticCustomerLevelRespVO> selectCustomerLevelListGroupByLevel(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
@@ -12,7 +12,7 @@ import java.util.List;
|
||||
* @author anhaohao
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsRankingMapper {
|
||||
public interface CrmStatisticsRankMapper {
|
||||
|
||||
/**
|
||||
* 查询合同金额排行榜
|
||||
@@ -20,7 +20,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 合同金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询回款金额排行榜
|
||||
@@ -28,7 +28,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 回款金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询签约合同数量排行榜
|
||||
@@ -36,7 +36,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 签约合同数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询产品销量排行榜
|
||||
@@ -44,7 +44,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 产品销量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询新增客户数排行榜
|
||||
@@ -52,7 +52,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 新增客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询联系人数量排行榜
|
||||
@@ -60,7 +60,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 联系人数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询跟进次数排行榜
|
||||
@@ -68,7 +68,7 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 跟进次数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询跟进客户数排行榜
|
||||
@@ -76,6 +76,6 @@ public interface CrmStatisticsRankingMapper {
|
||||
* @param rankReqVO 参数
|
||||
* @return 跟进客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.crm.framework.excel.core;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 地区下拉框数据源的 {@link ExcelColumnSelectFunction} 实现类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Service
|
||||
public class AreaExcelColumnSelectFunction implements ExcelColumnSelectFunction {
|
||||
|
||||
public static final String NAME = "getCrmAreaNameList"; // 防止和别的模块重名
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptions() {
|
||||
// 获取地区下拉数据
|
||||
// TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么?
|
||||
Area area = AreaUtils.getArea(Area.ID_CHINA);
|
||||
return AreaUtils.getAreaNodePathList(area.getChildren());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* crm 模块的 excel 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.excel;
|
@@ -1 +1,4 @@
|
||||
/**
|
||||
* crm 模块的 operatelog 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.operatelog;
|
@@ -1 +1,4 @@
|
||||
/**
|
||||
* crm 模块的 permission 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.permission;
|
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* trade 模块的 web 配置
|
||||
* crm 模块的 web 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.web;
|
||||
|
@@ -46,8 +46,8 @@ public interface CrmBusinessService {
|
||||
/**
|
||||
* 更新商机相关跟进信息
|
||||
*
|
||||
* @param id 编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param id 编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param contactLastContent 最后联系内容
|
||||
*/
|
||||
void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||
@@ -55,7 +55,7 @@ public interface CrmBusinessService {
|
||||
/**
|
||||
* 更新商机的下次联系时间
|
||||
*
|
||||
* @param ids 编号数组
|
||||
* @param ids 编号数组
|
||||
* @param contactNextTime 下次联系时间
|
||||
*/
|
||||
void updateBusinessContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime);
|
||||
@@ -185,4 +185,13 @@ public interface CrmBusinessService {
|
||||
return status.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得商机列表
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 商机列表
|
||||
*/
|
||||
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||
|
||||
}
|
||||
|
@@ -88,7 +88,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
success = CRM_BUSINESS_CREATE_SUCCESS)
|
||||
public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
|
||||
// 1.1 校验产品项的有效性
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts());
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getBusinessProducts());
|
||||
// 1.2 校验关联字段
|
||||
validateRelationDataExists(createReqVO);
|
||||
|
||||
@@ -129,7 +129,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
// 1.1 校验存在
|
||||
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
|
||||
// 1.2 校验产品项的有效性
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getBusinessProducts());
|
||||
// 1.3 校验关联字段
|
||||
validateRelationDataExists(updateReqVO);
|
||||
|
||||
@@ -202,9 +202,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
}
|
||||
}
|
||||
|
||||
private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.Product> list) {
|
||||
private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list) {
|
||||
// 1. 校验产品存在
|
||||
productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId));
|
||||
productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.BusinessProduct::getProductId));
|
||||
// 2. 转化为 CrmBusinessProductDO 列表
|
||||
return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class,
|
||||
item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()))));
|
||||
@@ -234,7 +234,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
}
|
||||
// 1.4 校验是不是状态没变更
|
||||
if ((reqVO.getStatusId() != null && reqVO.getStatusId().equals(business.getStatusId()))
|
||||
|| (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) {
|
||||
|| (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) {
|
||||
throw exception(BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
|
||||
// 2.1 数据权限转移
|
||||
permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_BUSINESS.getType(),
|
||||
reqVO.getNewOwnerUserId(), reqVO.getId(), CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
|
||||
// 2.2 设置新的负责人
|
||||
businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
|
||||
|
||||
@@ -370,4 +370,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
return businessMapper.selectCountByStatusTypeId(statusTypeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -75,8 +75,8 @@ public interface CrmContactService {
|
||||
/**
|
||||
* 更新联系人的下次联系时间
|
||||
*
|
||||
* @param ids 编号数组
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param ids 编号数组
|
||||
* @param contactNextTime 下次联系时间
|
||||
*/
|
||||
void updateContactContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime);
|
||||
|
||||
@@ -160,4 +160,13 @@ public interface CrmContactService {
|
||||
*/
|
||||
Long getContactCountByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 获得联系人列表
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 联系人列表
|
||||
*/
|
||||
List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||
|
||||
}
|
||||
|
@@ -298,4 +298,9 @@ public class CrmContactServiceImpl implements CrmContactService {
|
||||
return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
@@ -58,8 +58,8 @@ public interface CrmContractService {
|
||||
/**
|
||||
* 更新合同相关的更进信息
|
||||
*
|
||||
* @param id 合同编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param id 合同编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param contactLastContent 最后联系内容
|
||||
*/
|
||||
void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||
@@ -75,7 +75,7 @@ public interface CrmContractService {
|
||||
/**
|
||||
* 更新合同流程审批结果
|
||||
*
|
||||
* @param id 合同编号
|
||||
* @param id 合同编号
|
||||
* @param bpmResult BPM 审批结果
|
||||
*/
|
||||
void updateContractAuditStatus(Long id, Integer bpmResult);
|
||||
@@ -193,4 +193,13 @@ public interface CrmContractService {
|
||||
*/
|
||||
Long getRemindContractCount(Long userId);
|
||||
|
||||
/**
|
||||
* 获得合同列表
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 合同列表
|
||||
*/
|
||||
List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||
|
||||
}
|
||||
|
@@ -30,12 +30,14 @@ import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
|
||||
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import com.mzt.logapi.context.LogRecordContext;
|
||||
import com.mzt.logapi.service.impl.DiffParseFunction;
|
||||
import com.mzt.logapi.starter.annotation.LogRecord;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -86,7 +88,9 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmContractConfigService contractConfigService;
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private CrmReceivableService receivableService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
@@ -222,15 +226,19 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
success = CRM_CONTRACT_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteContract(Long id) {
|
||||
// TODO @puhui999:如果被 CrmReceivableDO 所使用,则不允许删除
|
||||
// 校验存在
|
||||
// 1.1 校验存在
|
||||
CrmContractDO contract = validateContractExists(id);
|
||||
// 删除
|
||||
// 1.2 如果被 CrmReceivableDO 所使用,则不允许删除
|
||||
if (receivableService.getReceivableCountByContractId(contract.getId()) > 0) {
|
||||
throw exception(CONTRACT_DELETE_FAIL);
|
||||
}
|
||||
|
||||
// 2.1 删除合同
|
||||
contractMapper.deleteById(id);
|
||||
// 删除数据权限
|
||||
// 2.2 删除数据权限
|
||||
crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id);
|
||||
|
||||
// 记录操作日志上下文
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contractName", contract.getName());
|
||||
}
|
||||
|
||||
@@ -399,4 +407,9 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
return contractMapper.selectCountByRemind(userId, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return contractMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,7 +9,13 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
|
||||
@@ -201,7 +207,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
CrmCustomerDO customer = validateCustomerExists(reqVO.getId());
|
||||
// 1.2 校验拥有客户是否到达上限
|
||||
validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1);
|
||||
|
||||
// 2.1 数据权限转移
|
||||
permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
|
||||
reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
|
||||
@@ -209,10 +214,45 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getId())
|
||||
.setOwnerUserId(reqVO.getNewOwnerUserId()).setOwnerTime(LocalDateTime.now()));
|
||||
|
||||
// 2.3 同时转移
|
||||
if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) {
|
||||
transfer(reqVO, userId);
|
||||
}
|
||||
|
||||
// 3. 记录转移日志
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转移客户时,需要额外有【联系人】【商机】【合同】
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
private void transfer(CrmCustomerTransferReqVO reqVO, Long userId) {
|
||||
if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) {
|
||||
List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getId(), userId);
|
||||
contactList.forEach(item -> {
|
||||
contactService.transferContact(new CrmContactTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(),
|
||||
reqVO.getOldOwnerPermissionLevel()), userId);
|
||||
});
|
||||
}
|
||||
if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_BUSINESS.getType())) {
|
||||
List<CrmBusinessDO> businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getId(), userId);
|
||||
businessList.forEach(item -> {
|
||||
businessService.transferBusiness(new CrmBusinessTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(),
|
||||
reqVO.getOldOwnerPermissionLevel()), userId);
|
||||
});
|
||||
}
|
||||
if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTRACT.getType())) {
|
||||
List<CrmContractDO> contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getId(), userId);
|
||||
contractList.forEach(item -> {
|
||||
contractService.transferContract(new CrmContractTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(),
|
||||
reqVO.getOldOwnerPermissionLevel()), userId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = "{{#lockReqVO.id}}",
|
||||
success = CRM_CUSTOMER_LOCK_SUCCESS)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.crm.service.permission;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
@@ -19,6 +20,14 @@ import java.util.List;
|
||||
*/
|
||||
public interface CrmPermissionService {
|
||||
|
||||
/**
|
||||
* 创建数据权限
|
||||
*
|
||||
* @param reqVO 创建信息
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void createPermission(CrmPermissionSaveReqVO reqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 创建数据权限
|
||||
*
|
||||
@@ -111,10 +120,10 @@ public interface CrmPermissionService {
|
||||
/**
|
||||
* 校验是否有指定数据的操作权限
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @param userId 用户编号
|
||||
* @param level 权限级别
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @param userId 用户编号
|
||||
* @param level 权限级别
|
||||
* @return 是否有权限
|
||||
*/
|
||||
boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level);
|
||||
|
@@ -4,28 +4,34 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
|
||||
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner;
|
||||
|
||||
@@ -40,18 +46,119 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
|
||||
|
||||
@Resource
|
||||
private CrmPermissionMapper permissionMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy // 解决依赖循环
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
@Lazy // 解决依赖循环
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
@Lazy // 解决依赖循环
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void createPermission(CrmPermissionSaveReqVO reqVO, Long userId) {
|
||||
// 1. 创建数据权限
|
||||
createPermission0(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class));
|
||||
|
||||
// 2. 处理【同时添加至】的权限
|
||||
if (CollUtil.isEmpty(reqVO.getToBizTypes())) {
|
||||
return;
|
||||
}
|
||||
List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
|
||||
buildContactPermissions(reqVO, userId, createPermissions);
|
||||
buildBusinessPermissions(reqVO, userId, createPermissions);
|
||||
buildContractPermissions(reqVO, userId, createPermissions);
|
||||
if (CollUtil.isEmpty(createPermissions)) {
|
||||
return;
|
||||
}
|
||||
createPermissionBatch(createPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理同时添加至联系人
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 操作人
|
||||
* @param createPermissions 待添加权限列表
|
||||
*/
|
||||
private void buildContactPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) {
|
||||
// 1. 校验是否被同时添加
|
||||
Integer type = CrmBizTypeEnum.CRM_CONTACT.getType();
|
||||
if (!reqVO.getToBizTypes().contains(type)) {
|
||||
return;
|
||||
}
|
||||
// 2. 添加数据权限
|
||||
List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), userId);
|
||||
contactList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理同时添加至商机
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 操作人
|
||||
* @param createPermissions 待添加权限列表
|
||||
*/
|
||||
private void buildBusinessPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) {
|
||||
// 1. 校验是否被同时添加
|
||||
Integer type = CrmBizTypeEnum.CRM_BUSINESS.getType();
|
||||
if (!reqVO.getToBizTypes().contains(type)) {
|
||||
return;
|
||||
}
|
||||
// 2. 添加数据权限
|
||||
List<CrmBusinessDO> businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getBizId(), userId);
|
||||
businessList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理同时添加至合同
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 操作人
|
||||
* @param createPermissions 待添加权限列表
|
||||
*/
|
||||
private void buildContractPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) {
|
||||
// 1. 校验是否被同时添加
|
||||
Integer type = CrmBizTypeEnum.CRM_CONTRACT.getType();
|
||||
if (!reqVO.getToBizTypes().contains(type)) {
|
||||
return;
|
||||
}
|
||||
// 2. 添加数据权限
|
||||
List<CrmContractDO> contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getBizId(), userId);
|
||||
contractList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions));
|
||||
}
|
||||
|
||||
private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO, Integer type, Long bizId, String name,
|
||||
List<CrmPermissionCreateReqBO> createPermissions) {
|
||||
AdminUserRespDTO user = adminUserApi.getUser(reqVO.getUserId());
|
||||
// 1. 需要考虑,被添加人,是不是应该有对应的权限了;
|
||||
CrmPermissionDO permission = hasAnyPermission(type, bizId, reqVO.getUserId());
|
||||
if (ObjUtil.isNotNull(permission)) {
|
||||
throw exception(CRM_PERMISSION_CREATE_FAIL_EXISTS, user.getNickname(), CrmBizTypeEnum.getNameByType(type),
|
||||
name, CrmPermissionLevelEnum.getNameByLevel(permission.getLevel()));
|
||||
}
|
||||
// 2. 添加数据权限
|
||||
createPermissions.add(new CrmPermissionCreateReqBO().setBizType(type)
|
||||
.setBizId(bizId).setUserId(reqVO.getUserId()).setLevel(reqVO.getLevel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createPermission(CrmPermissionCreateReqBO createReqBO) {
|
||||
return createPermission0(createReqBO);
|
||||
}
|
||||
|
||||
private Long createPermission0(CrmPermissionCreateReqBO createReqBO) {
|
||||
validatePermissionNotExists(Collections.singletonList(createReqBO));
|
||||
// 1. 校验用户是否存在
|
||||
adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId()));
|
||||
|
||||
// 2. 创建
|
||||
// 2. 插入权限
|
||||
CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class);
|
||||
permissionMapper.insert(permission);
|
||||
return permission.getId();
|
||||
@@ -170,7 +277,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
|
||||
throw exception(CRM_PERMISSION_DELETE_FAIL);
|
||||
}
|
||||
// 校验操作人是否为负责人
|
||||
CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.get(0).getBizId(), userId);
|
||||
CrmPermissionDO permission = permissionMapper.selectByBizAndUserId(permissions.get(0).getBizType(), permissions.get(0).getBizId(), userId);
|
||||
if (permission == null) {
|
||||
throw exception(CRM_PERMISSION_DELETE_DENIED);
|
||||
}
|
||||
@@ -220,4 +327,9 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
|
||||
ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), level.getLevel()));
|
||||
}
|
||||
|
||||
public CrmPermissionDO hasAnyPermission(Integer bizType, Long bizId, Long userId) {
|
||||
List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
|
||||
return findFirst(permissionList, permission -> ObjUtil.equal(permission.getUserId(), userId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ import com.mzt.logapi.context.LogRecordContext;
|
||||
import com.mzt.logapi.service.impl.DiffParseFunction;
|
||||
import com.mzt.logapi.starter.annotation.LogRecord;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -46,9 +45,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
|
||||
@Resource
|
||||
private CrmReceivablePlanMapper receivablePlanMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private CrmReceivableService receivableService;
|
||||
@Resource
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
@@ -144,7 +140,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
|
||||
// 2. 删除
|
||||
receivablePlanMapper.deleteById(id);
|
||||
// 3. 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), id);
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivablePlan", receivablePlan);
|
||||
|
@@ -122,4 +122,12 @@ public interface CrmReceivableService {
|
||||
*/
|
||||
Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds);
|
||||
|
||||
/**
|
||||
* 根据合同编号查询回款数量
|
||||
*
|
||||
* @param contractId 合同编号
|
||||
* @return 回款数量
|
||||
*/
|
||||
Long getReceivableCountByContractId(Long contractId);
|
||||
|
||||
}
|
||||
|
@@ -37,10 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
@@ -81,7 +78,6 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
@Resource
|
||||
private BpmProcessInstanceApi bpmProcessInstanceApi;
|
||||
|
||||
// TODO @puhui999:操作日志没记录上
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
|
||||
@@ -115,6 +111,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
|
||||
// 5. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivable", receivable);
|
||||
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
|
||||
return receivable.getId();
|
||||
}
|
||||
|
||||
@@ -156,7 +153,6 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @puhui999:操作日志没记录上
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
@@ -164,11 +160,14 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
|
||||
Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
|
||||
// 1.1 校验可回款金额超过上限
|
||||
validateReceivablePriceExceedsLimit(updateReqVO);
|
||||
updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段
|
||||
// 1.2 校验存在
|
||||
// 1.1 校验存在
|
||||
CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId());
|
||||
updateReqVO.setOwnerUserId(receivable.getOwnerUserId()).setCustomerId(receivable.getCustomerId())
|
||||
.setContractId(receivable.getContractId()).setPlanId(receivable.getPlanId()); // 设置已存在的值
|
||||
// 1.2 校验可回款金额超过上限
|
||||
validateReceivablePriceExceedsLimit(updateReqVO);
|
||||
|
||||
// 1.3 只有草稿、审批中,可以编辑;
|
||||
if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
|
||||
CrmAuditStatusEnum.PROCESS.getStatus())) {
|
||||
@@ -180,8 +179,17 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
receivableMapper.updateById(updateObj);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class));
|
||||
LogRecordContext.putVariable("receivable", receivable);
|
||||
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class));
|
||||
}
|
||||
|
||||
private Integer getReceivablePeriod(Long planId) {
|
||||
if (Objects.isNull(planId)) {
|
||||
return null;
|
||||
}
|
||||
CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(planId);
|
||||
return receivablePlan.getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -212,15 +220,19 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
|
||||
throw exception(RECEIVABLE_DELETE_FAIL);
|
||||
}
|
||||
// TODO @puhui999:审批通过时,不允许删除;
|
||||
// 1.3 审批通过时,不允许删除
|
||||
if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
|
||||
throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE);
|
||||
}
|
||||
|
||||
// 2. 删除
|
||||
// 2.1 删除回款
|
||||
receivableMapper.deleteById(id);
|
||||
// 3. 删除数据权限
|
||||
// 2.2 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivable", receivable);
|
||||
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -289,4 +301,9 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
return receivableMapper.selectReceivablePriceMapByContractId(contractIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getReceivableCountByContractId(Long contractId) {
|
||||
return receivableMapper.selectCountByContractId(contractId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 客户分析 Service 接口
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
public interface CrmStatisticsCustomerService {
|
||||
|
||||
/**
|
||||
* 总量分析(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 总量分析(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进次数分析(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 跟进次数分析(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 客户跟进次数分析(按类型)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取客户的首次合同、回款信息列表,用于【客户转化率】页面
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 公海客户分析(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> getPoolSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 公海客户分析(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> getPoolSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 客户成交周期(按日期)
|
||||
*
|
||||
* 成交周期的定义:客户 customer 在创建出来,到合同 contract 第一次成交的时间差
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 客户成交周期(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,323 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
|
||||
/**
|
||||
* CRM 客户分析 Service 实现类
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsCustomerMapper customerMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按天统计,获取分项统计数据
|
||||
List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO);
|
||||
List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);
|
||||
|
||||
// 3. 按照日期间隔,合并数据
|
||||
List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
|
||||
return convertList(timeRanges, times -> {
|
||||
Integer customerCreateCount = customerCreateCountList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount).sum();
|
||||
Integer customerDealCount = customerDealCountList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount).sum();
|
||||
return new CrmStatisticsCustomerSummaryByDateRespVO()
|
||||
.setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
|
||||
.setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按用户统计,获取分项统计数据
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByUser(reqVO);
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> contractPriceList = customerMapper.selectContractPriceGroupByUser(reqVO);
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePriceList = customerMapper.selectReceivablePriceGroupByUser(reqVO);
|
||||
|
||||
// 3.1 按照用户,合并统计数据
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
|
||||
Integer customerCreateCount = customerCreateCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount).sum();
|
||||
Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();
|
||||
BigDecimal contractPrice = contractPriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getContractPrice()), BigDecimal::add);
|
||||
BigDecimal receivablePrice = receivablePriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getReceivablePrice()), BigDecimal::add);
|
||||
return (CrmStatisticsCustomerSummaryByUserRespVO) new CrmStatisticsCustomerSummaryByUserRespVO()
|
||||
.setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount)
|
||||
.setContractPrice(contractPrice).setReceivablePrice(receivablePrice).setOwnerUserId(userId);
|
||||
});
|
||||
// 3.2 拼接用户信息
|
||||
appendUserInfo(summaryList);
|
||||
return summaryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按天统计,获取分项统计数据
|
||||
List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByDate(reqVO);
|
||||
List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByDate(reqVO);
|
||||
|
||||
// 3. 按照时间间隔,合并统计数据
|
||||
List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
|
||||
return convertList(timeRanges, times -> {
|
||||
Integer followUpRecordCount = followUpRecordCountList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpRecordCount).sum();
|
||||
Integer followUpCustomerCount = followUpCustomerCountList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpCustomerCount).sum();
|
||||
return new CrmStatisticsFollowUpSummaryByDateRespVO()
|
||||
.setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
|
||||
.setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按用户统计,获取分项统计数据
|
||||
List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByUser(reqVO);
|
||||
List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByUser(reqVO);
|
||||
|
||||
// 3.1 按照用户,合并统计数据
|
||||
List<CrmStatisticsFollowUpSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
|
||||
Integer followUpRecordCount = followUpRecordCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpRecordCount).sum();
|
||||
Integer followUpCustomerCount = followUpCustomerCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpCustomerCount).sum();
|
||||
return (CrmStatisticsFollowUpSummaryByUserRespVO) new CrmStatisticsFollowUpSummaryByUserRespVO()
|
||||
.setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount).setOwnerUserId(userId);
|
||||
});
|
||||
// 3.2 拼接用户信息
|
||||
appendUserInfo(summaryList);
|
||||
return summaryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 获得跟进数据
|
||||
return customerMapper.selectFollowUpRecordCountGroupByType(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按用户统计,获取统计数据
|
||||
List<CrmStatisticsCustomerContractSummaryRespVO> summaryList = customerMapper.selectContractSummary(reqVO);
|
||||
|
||||
// 3. 拼接信息
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSetByFlatMap(summaryList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreator()), vo.getOwnerUserId())));
|
||||
summaryList.forEach(vo -> {
|
||||
findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()), user -> vo.setCreatorUserName(user.getNickname()));
|
||||
findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));
|
||||
});
|
||||
return summaryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按天统计,获取分项统计数据
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByDate(reqVO);
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByDate(reqVO);
|
||||
|
||||
// 3. 按照日期间隔,合并数据
|
||||
List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
|
||||
return convertList(timeRanges, times -> {
|
||||
Integer customerPutCount = customerPutCountList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToInt(CrmStatisticsPoolSummaryByDateRespVO::getCustomerPutCount).sum();
|
||||
Integer customerTakeCount = customerTakeCountList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToInt(CrmStatisticsPoolSummaryByDateRespVO::getCustomerTakeCount).sum();
|
||||
return new CrmStatisticsPoolSummaryByDateRespVO()
|
||||
.setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
|
||||
.setCustomerPutCount(customerPutCount).setCustomerTakeCount(customerTakeCount);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按用户统计,获取分项统计数据
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByUser(reqVO);
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByUser(reqVO);
|
||||
|
||||
// 3.1 按照用户,合并统计数据
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
|
||||
Integer customerPutCount = customerPutCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsPoolSummaryByUserRespVO::getCustomerPutCount).sum();
|
||||
Integer customerTakeCount = customerTakeCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsPoolSummaryByUserRespVO::getCustomerTakeCount).sum();
|
||||
return (CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO()
|
||||
.setCustomerPutCount(customerPutCount).setCustomerTakeCount(customerTakeCount)
|
||||
.setOwnerUserId(userId);
|
||||
});
|
||||
// 3.2 拼接用户信息
|
||||
appendUserInfo(summaryList);
|
||||
return summaryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按天统计,获取分项统计数据
|
||||
List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);
|
||||
|
||||
// 3. 按照日期间隔,合并统计数据
|
||||
List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
|
||||
return convertList(timeRanges, times -> {
|
||||
Double customerDealCycle = customerDealCycleList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
|
||||
.mapToDouble(CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle).sum();
|
||||
return new CrmStatisticsCustomerDealCycleByDateRespVO()
|
||||
.setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
|
||||
.setCustomerDealCycle(customerDealCycle);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按用户统计,获取分项统计数据
|
||||
List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByUser(reqVO);
|
||||
List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
|
||||
|
||||
// 3.1 按照用户,合并统计数据
|
||||
List<CrmStatisticsCustomerDealCycleByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
|
||||
Double customerDealCycle = customerDealCycleList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToDouble(CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle).sum();
|
||||
Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
|
||||
.mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();
|
||||
return (CrmStatisticsCustomerDealCycleByUserRespVO) new CrmStatisticsCustomerDealCycleByUserRespVO()
|
||||
.setCustomerDealCycle(customerDealCycle).setCustomerDealCount(customerDealCount).setOwnerUserId(userId);
|
||||
});
|
||||
// 3.2 拼接用户信息
|
||||
appendUserInfo(summaryList);
|
||||
return summaryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接用户信息(昵称)
|
||||
*
|
||||
* @param voList 统计数据
|
||||
*/
|
||||
private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> voList) {
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(voList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
|
||||
voList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 用户编号数组
|
||||
*/
|
||||
private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) {
|
||||
// 情况一:选中某个用户
|
||||
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||
return List.of(reqVO.getUserId());
|
||||
}
|
||||
// 情况二:选中某个部门
|
||||
// 2.1 获得部门列表
|
||||
List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
|
||||
deptIds.add(reqVO.getDeptId());
|
||||
// 2.2 获得用户编号
|
||||
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 员工绩效统计 Service 接口
|
||||
*
|
||||
* @author scholar
|
||||
*/
|
||||
public interface CrmStatisticsPerformanceService {
|
||||
|
||||
/**
|
||||
* 员工签约合同数量分析
|
||||
*
|
||||
* @param performanceReqVO 排行参数
|
||||
* @return 员工签约合同数量排行分析
|
||||
*/
|
||||
List<CrmStatisticsPerformanceRespVO> getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
|
||||
|
||||
/**
|
||||
* 员工签约合同金额分析
|
||||
*
|
||||
* @param performanceReqVO 排行参数
|
||||
* @return 员工签约合同金额分析
|
||||
*/
|
||||
List<CrmStatisticsPerformanceRespVO> getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
|
||||
|
||||
/**
|
||||
* 员工获得回款金额分析
|
||||
*
|
||||
* @param performanceReqVO 排行参数
|
||||
* @return 员工获得回款金额分析
|
||||
*/
|
||||
List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO);
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* CRM 员工业绩分析 Service 实现类
|
||||
*
|
||||
* @author scholar
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerformanceService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsPerformanceMapper performanceMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPerformanceRespVO> getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO) {
|
||||
// TODO @scholar:我们可以换个思路实现,减少数据库的计算量;
|
||||
// 比如说,2024 年的合同数据,是不是 2022-12 到 2024-12-31,每个月的统计呢?
|
||||
// 理解之后,我们可以数据 group by 年-月,20222-12 到 2024-12-31 的,然后内存在聚合出 CrmStatisticsPerformanceRespVO 这样
|
||||
// 这样,我们就可以减少数据库的计算量,提升性能;同时 SQL 也会很简单,开发者理解起来也简单哈;
|
||||
return getPerformance(performanceReqVO, performanceMapper::selectContractCountPerformance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPerformanceRespVO> getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) {
|
||||
return getPerformance(performanceReqVO, performanceMapper::selectContractPricePerformance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) {
|
||||
return getPerformance(performanceReqVO, performanceMapper::selectReceivablePricePerformance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得员工业绩数据
|
||||
*
|
||||
* @param performanceReqVO 参数
|
||||
* @param performanceFunction 排行榜方法
|
||||
* @return 排行版数据
|
||||
*/
|
||||
private List<CrmStatisticsPerformanceRespVO> getPerformance(CrmStatisticsPerformanceReqVO performanceReqVO, Function<CrmStatisticsPerformanceReqVO,
|
||||
List<CrmStatisticsPerformanceRespVO>> performanceFunction) {
|
||||
|
||||
// 1. 获得用户编号数组
|
||||
final List<Long> userIds = getUserIds(performanceReqVO);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
performanceReqVO.setUserIds(userIds);
|
||||
// 2. 获得排行数据
|
||||
List<CrmStatisticsPerformanceRespVO> performance = performanceFunction.apply(performanceReqVO);
|
||||
if (CollUtil.isEmpty(performance)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return performance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 用户编号数组
|
||||
*/
|
||||
private List<Long> getUserIds(CrmStatisticsPerformanceReqVO reqVO) {
|
||||
// 情况一:选中某个用户
|
||||
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||
return List.of(reqVO.getUserId());
|
||||
}
|
||||
// 情况二:选中某个部门
|
||||
// 2.1 获得部门列表
|
||||
final Long deptId = reqVO.getDeptId();
|
||||
List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
|
||||
deptIds.add(deptId);
|
||||
// 2.2 获得用户编号
|
||||
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 客户画像 Service 接口
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface CrmStatisticsPortraitService {
|
||||
|
||||
/**
|
||||
* 获取客户地区统计数据
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticCustomerAreaRespVO> getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取客户行业统计数据
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticCustomerIndustryRespVO> getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取客户级别统计数据
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticCustomerLevelRespVO> getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取客户来源统计数据
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticCustomerSourceRespVO> getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO);
|
||||
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
|
||||
/**
|
||||
* CRM 客户画像 Service 实现类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Service
|
||||
public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsPortraitMapper portraitMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticCustomerAreaRespVO> getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
List<Long> userIds = getUserIds(reqVO);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
reqVO.setUserIds(userIds);
|
||||
|
||||
// 2. 获取客户地区统计数据
|
||||
List<CrmStatisticCustomerAreaRespVO> list = portraitMapper.selectSummaryListGroupByAreaId(reqVO);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 3. 拼接数据
|
||||
List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);
|
||||
areaList.add(new Area().setId(null).setName("未知")); // TODO @puhui999:是不是 65 find 的逻辑改下;不用 findAndThen,直接从 areaMap 拿;拿到就设置,不拿到就设置 null 和 未知;这样,58 本行可以删除掉完事了;这样代码更简单和一致
|
||||
Map<Integer, Area> areaMap = convertMap(areaList, Area::getId);
|
||||
return convertList(list, item -> {
|
||||
Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE);
|
||||
if (parentId == null) { // 找不到,归到未知
|
||||
return item.setAreaId(null).setAreaName("未知");
|
||||
}
|
||||
findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName()));
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticCustomerIndustryRespVO> getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
List<Long> userIds = getUserIds(reqVO);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
reqVO.setUserIds(userIds);
|
||||
|
||||
// 2. 获取客户行业统计数据
|
||||
return portraitMapper.selectCustomerIndustryListGroupByIndustryId(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticCustomerSourceRespVO> getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
List<Long> userIds = getUserIds(reqVO);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
reqVO.setUserIds(userIds);
|
||||
|
||||
// 2. 获取客户行业统计数据
|
||||
return portraitMapper.selectCustomerSourceListGroupBySource(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticCustomerLevelRespVO> getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
List<Long> userIds = getUserIds(reqVO);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
reqVO.setUserIds(userIds);
|
||||
|
||||
// 2. 获取客户级别统计数据
|
||||
return portraitMapper.selectCustomerLevelListGroupByLevel(reqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 用户编号数组
|
||||
*/
|
||||
private List<Long> getUserIds(CrmStatisticsPortraitReqVO reqVO) {
|
||||
// 情况一:选中某个用户
|
||||
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||
return List.of(reqVO.getUserId());
|
||||
}
|
||||
// 情况二:选中某个部门
|
||||
// 2.1 获得部门列表
|
||||
List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
|
||||
deptIds.add(reqVO.getDeptId());
|
||||
// 2.2 获得用户编号
|
||||
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||
}
|
||||
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.List;
|
||||
*
|
||||
* @author anhaohao
|
||||
*/
|
||||
public interface CrmStatisticsRankingService {
|
||||
public interface CrmStatisticsRankService {
|
||||
|
||||
/**
|
||||
* 获得合同金额排行榜
|
||||
@@ -19,7 +19,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 合同金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得回款金额排行榜
|
||||
@@ -27,7 +27,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 回款金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得签约合同数量排行榜
|
||||
@@ -35,7 +35,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 签约合同数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得产品销量排行榜
|
||||
@@ -43,7 +43,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 产品销量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得新增客户数排行榜
|
||||
@@ -51,7 +51,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 新增客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得联系人数量排行榜
|
||||
@@ -59,7 +59,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 联系人数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得跟进次数排行榜
|
||||
@@ -67,7 +67,7 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 跟进次数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得跟进客户数排行榜
|
||||
@@ -75,6 +75,6 @@ public interface CrmStatisticsRankingService {
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 跟进客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRanKRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
}
|
@@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
@@ -29,10 +29,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingService {
|
||||
public class CrmStatisticsRankServiceImpl implements CrmStatisticsRankService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsRankingMapper rankMapper;
|
||||
private CrmStatisticsRankMapper rankMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@@ -40,64 +40,64 @@ public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingServ
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectContractPriceRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectReceivablePriceRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectContractCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectProductSalesRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectCustomerCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectContactsCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectFollowCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRanKRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
public List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectFollowCustomerCountRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得排行版数据
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @param rankReqVO 参数
|
||||
* @param rankFunction 排行榜方法
|
||||
* @return 排行版数据
|
||||
*/
|
||||
private List<CrmStatisticsRanKRespVO> getRank(CrmStatisticsRankReqVO rankReqVO, Function<CrmStatisticsRankReqVO, List<CrmStatisticsRanKRespVO>> rankFunction) {
|
||||
private List<CrmStatisticsRankRespVO> getRank(CrmStatisticsRankReqVO rankReqVO, Function<CrmStatisticsRankReqVO, List<CrmStatisticsRankRespVO>> rankFunction) {
|
||||
// 1. 获得用户编号数组
|
||||
rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId()));
|
||||
if (CollUtil.isEmpty(rankReqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 2. 获得排行数据
|
||||
List<CrmStatisticsRanKRespVO> ranks = rankFunction.apply(rankReqVO);
|
||||
List<CrmStatisticsRankRespVO> ranks = rankFunction.apply(rankReqVO);
|
||||
if (CollUtil.isEmpty(ranks)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ranks.sort(Comparator.comparing(CrmStatisticsRanKRespVO::getCount).reversed());
|
||||
ranks.sort(Comparator.comparing(CrmStatisticsRankRespVO::getCount).reversed());
|
||||
// 3. 拼接用户信息
|
||||
appendUserInfo(ranks);
|
||||
return ranks;
|
||||
@@ -108,8 +108,8 @@ public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingServ
|
||||
*
|
||||
* @param ranks 排行榜数据
|
||||
*/
|
||||
private void appendUserInfo(List<CrmStatisticsRanKRespVO> ranks) {
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRanKRespVO::getOwnerUserId));
|
||||
private void appendUserInfo(List<CrmStatisticsRankRespVO> ranks) {
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRankRespVO::getOwnerUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> {
|
||||
rank.setNickname(user.getNickname());
|
@@ -0,0 +1,224 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper">
|
||||
|
||||
<select id="selectCustomerCreateCountGroupByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(*) AS customerCreateCount
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<!-- TODO 芋艿:应该不用过滤时间 -->
|
||||
<select id="selectCustomerDealCountGroupByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(customer.create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(DISTINCT customer.id) AS customer_deal_count
|
||||
FROM crm_customer AS customer
|
||||
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
|
||||
WHERE customer.deleted = 0 AND contract.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND customer.owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerCreateCountGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
|
||||
SELECT owner_user_id,
|
||||
COUNT(*) AS customer_create_count
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerDealCountGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
|
||||
SELECT customer.owner_user_id,
|
||||
COUNT(DISTINCT customer.id) AS customer_deal_count
|
||||
FROM crm_customer AS customer
|
||||
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
|
||||
WHERE customer.deleted = 0 AND contract.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND customer.owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY customer.owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectContractPriceGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
|
||||
SELECT owner_user_id,
|
||||
IFNULL(SUM(total_price), 0) AS contract_price
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectReceivablePriceGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
|
||||
SELECT owner_user_id,
|
||||
IFNULL(SUM(price), 0) AS receivable_price
|
||||
FROM crm_receivable
|
||||
WHERE deleted = 0
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectFollowUpRecordCountGroupByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(*) AS follow_up_record_count
|
||||
FROM crm_follow_up_record
|
||||
WHERE creator IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<select id="selectFollowUpCustomerCountGroupByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(DISTINCT biz_id) AS follow_up_customer_count
|
||||
FROM crm_follow_up_record
|
||||
WHERE creator IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<select id="selectFollowUpRecordCountGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO">
|
||||
SELECT creator as owner_user_id,
|
||||
COUNT(*) AS follow_up_record_count
|
||||
FROM crm_follow_up_record
|
||||
WHERE creator IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
GROUP BY creator
|
||||
</select>
|
||||
|
||||
<select id="selectFollowUpCustomerCountGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO">
|
||||
SELECT
|
||||
creator as owner_user_id,
|
||||
COUNT(DISTINCT biz_id) AS follow_up_customer_count
|
||||
FROM crm_follow_up_record
|
||||
WHERE creator IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
GROUP BY creator
|
||||
</select>
|
||||
|
||||
<select id="selectFollowUpRecordCountGroupByType"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByTypeRespVO">
|
||||
SELECT type AS follow_up_type,
|
||||
COUNT(*) AS follow_up_record_count
|
||||
FROM crm_follow_up_record
|
||||
WHERE creator IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
GROUP BY follow_up_type
|
||||
</select>
|
||||
|
||||
<select id="selectContractSummary"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO">
|
||||
SELECT customer.name AS customer_name,
|
||||
customer.industry_id,
|
||||
customer.source,
|
||||
customer.owner_user_id,
|
||||
customer.creator,
|
||||
customer.create_time,
|
||||
contract.name AS contract_name,
|
||||
contract.total_price,
|
||||
contract.order_date,
|
||||
IFNULL(receivable.price, 0) AS receivable_price
|
||||
FROM crm_customer AS customer
|
||||
INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id
|
||||
LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id
|
||||
WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND customer.owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
</select>
|
||||
|
||||
<!-- TIMESTAMPDIFF 用于求差值;AVG 求平均;TRUNCATE 去掉小数点、只保留整数 -->
|
||||
<select id="selectCustomerDealCycleGroupByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
|
||||
SELECT DATE_FORMAT(contract.order_date, '%Y-%m-%d') AS time,
|
||||
IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle
|
||||
FROM crm_customer AS customer
|
||||
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
|
||||
WHERE customer.deleted = 0 AND contract.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND customer.owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerDealCycleGroupByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByUserRespVO">
|
||||
SELECT customer.owner_user_id,
|
||||
IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle
|
||||
FROM crm_customer AS customer
|
||||
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
|
||||
WHERE customer.deleted = 0 AND contract.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND customer.owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY customer.owner_user_id
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper">
|
||||
|
||||
<select id="selectContractCountPerformance"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO">
|
||||
SELECT
|
||||
t.time as time,
|
||||
COALESCE(t.currentMonthCount,0) as currentMonthCount,
|
||||
COALESCE(y.lastMonthCount,0) as lastMonthCount,
|
||||
COALESCE(z.lastYearCount,0) as lastYearCount
|
||||
FROM
|
||||
(SELECT
|
||||
COUNT(1) AS currentMonthCount,
|
||||
DATE_FORMAT(order_date, '%Y-%m') AS time
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')
|
||||
GROUP BY time)t
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
COUNT(1) AS lastMonthCount,
|
||||
DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 MONTH), '%Y-%m') AS time
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND (DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')
|
||||
or DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1)
|
||||
GROUP BY time)y ON t.time = y.time
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
COUNT(1) AS lastYearCount,
|
||||
DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 YEAR), '%Y-%m') AS time
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1
|
||||
GROUP BY time)z ON t.time = z.time
|
||||
</select>
|
||||
<select id="selectContractPricePerformance"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO">
|
||||
SELECT
|
||||
t.time as time,
|
||||
COALESCE(t.currentMonthCount,0) as currentMonthCount,
|
||||
COALESCE(y.lastMonthCount,0) as lastMonthCount,
|
||||
COALESCE(z.lastYearCount,0) as lastYearCount
|
||||
FROM
|
||||
(SELECT
|
||||
IFNULL(SUM(total_price), 0) AS currentMonthCount,
|
||||
DATE_FORMAT(order_date, '%Y-%m') AS time
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')
|
||||
GROUP BY time)t
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
IFNULL(SUM(total_price), 0) AS lastMonthCount,
|
||||
DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 MONTH), '%Y-%m') AS time
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND (DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')
|
||||
or DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1)
|
||||
GROUP BY time)y ON t.time = y.time
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
IFNULL(SUM(total_price), 0) AS lastYearCount,
|
||||
DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 YEAR), '%Y-%m') AS time
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1
|
||||
GROUP BY time)z ON t.time = z.time
|
||||
</select>
|
||||
|
||||
<select id="selectReceivablePricePerformance"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO">
|
||||
SELECT
|
||||
t.time as time,
|
||||
COALESCE(t.currentMonthCount,0) as currentMonthCount,
|
||||
COALESCE(y.lastMonthCount,0) as lastMonthCount,
|
||||
COALESCE(z.lastYearCount,0) as lastYearCount
|
||||
FROM
|
||||
(SELECT
|
||||
IFNULL(SUM(price), 0) AS currentMonthCount,
|
||||
DATE_FORMAT(return_time, '%Y-%m') AS time
|
||||
FROM crm_receivable
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')
|
||||
GROUP BY time)t
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
IFNULL(SUM(price), 0) AS lastMonthCount,
|
||||
DATE_FORMAT(DATE_ADD(return_time,INTERVAL 1 MONTH), '%Y-%m') AS time
|
||||
FROM crm_receivable
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND (DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')
|
||||
or DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1)
|
||||
GROUP BY time)y ON t.time = y.time
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
IFNULL(SUM(price), 0) AS lastYearCount,
|
||||
DATE_FORMAT(DATE_ADD(return_time,INTERVAL 1 YEAR), '%Y-%m') AS time
|
||||
FROM crm_receivable
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1
|
||||
GROUP BY time)z ON t.time = z.time
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper">
|
||||
|
||||
<select id="selectSummaryListGroupByAreaId"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO">
|
||||
SELECT area_id, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0 AND area_id IS NOT NULL
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}
|
||||
AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY area_id
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerIndustryListGroupByIndustryId"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO">
|
||||
SELECT industry_id, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0 AND industry_id IS NOT NULL
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}
|
||||
AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY industry_id
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerSourceListGroupBySource"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO">
|
||||
SELECT source, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0 AND source IS NOT NULL
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}
|
||||
AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY source
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerLevelListGroupByLevel"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO">
|
||||
SELECT level, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0 AND level IS NOT NULL
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime}
|
||||
AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY level
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -1,125 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper">
|
||||
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper">
|
||||
|
||||
<select id="selectContractPriceRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT IFNULL(SUM(total_price), 0) AS count, owner_user_id
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
and owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND order_date between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectReceivablePriceRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT IFNULL(SUM(price), 0) AS count, owner_user_id
|
||||
FROM crm_receivable
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND return_time between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND return_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectContractCountRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT COUNT(1) AS count, owner_user_id
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND order_date between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<!-- TODO 待定 这里是否需要关联 crm_contract_product 表,计算销售额 -->
|
||||
<select id="selectProductSalesRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
SELECT COUNT(1) AS count, owner_user_id
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = 20
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND order_date between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT COUNT(product.count) AS count, contract.owner_user_id
|
||||
FROM crm_contract_product product
|
||||
INNER JOIN crm_contract contract ON product.contract_id = contract.id
|
||||
WHERE contract.deleted = 0 AND contract.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND contract.owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND contract.order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY contract.owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerCountRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT COUNT(1) AS count, owner_user_id
|
||||
FROM crm_customer
|
||||
WHERE deleted = 0
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectContactsCountRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT COUNT(1) AS count, owner_user_id
|
||||
FROM crm_contact
|
||||
WHERE deleted = 0
|
||||
AND owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectFollowCountRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT COUNT(1) AS count, cc.owner_user_id
|
||||
FROM crm_follow_up_record AS cfur
|
||||
LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids)
|
||||
WHERE cfur.deleted = 0
|
||||
AND cc.deleted = 0
|
||||
AND cc.owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY cc.owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectFollowCustomerCountRank"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO">
|
||||
SELECT COUNT(DISTINCT cc.id) AS count, cc.owner_user_id
|
||||
FROM crm_follow_up_record AS cfur
|
||||
LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids)
|
||||
WHERE cfur.deleted = 0
|
||||
AND cc.deleted = 0
|
||||
AND cc.owner_user_id in
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY cc.owner_user_id
|
||||
</select>
|
||||
|
Reference in New Issue
Block a user