+ * crm 系统,使用 1-020-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+ // ========== 合同管理 1-020-000-000 ==========
+ ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
+ ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑");
+ ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态");
+
+ // ========== 线索管理 1-020-001-000 ==========
+ ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
+ ErrorCode CLUE_ANY_CLUE_NOT_EXISTS = new ErrorCode(1_020_001_001, "线索【{}】不存在");
+ ErrorCode CLUE_ANY_CLUE_ALREADY_TRANSLATED = new ErrorCode(1_020_001_002, "线索【{}】已经转化过了,请勿重复转化");
+
+ // ========== 商机管理 1-020-002-000 ==========
+ ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");
+ ErrorCode BUSINESS_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关联合同,不能删除");
+
+ // TODO @lilleo:商机状态、商机类型,都单独错误码段
+
+
+ // ========== 联系人管理 1-020-003-000 ==========
+ ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
+ ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在");
+ ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除");
+
+ // ========== 回款 1-020-004-000 ==========
+ ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在");
+
+ // ========== 合同管理 1-020-005-000 ==========
+ ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
+
+ // ========== 客户管理 1_020_006_000 ==========
+ ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");
+ ErrorCode CUSTOMER_OWNER_EXISTS = new ErrorCode(1_020_006_001, "客户【{}】已存在所属负责人");
+ ErrorCode CUSTOMER_LOCKED = new ErrorCode(1_020_006_002, "客户【{}】状态已锁定");
+ ErrorCode CUSTOMER_ALREADY_DEAL = new ErrorCode(1_020_006_003, "客户已交易");
+ ErrorCode CUSTOMER_IN_POOL = new ErrorCode(1_020_006_004, "客户【{}】放入公海失败,原因:已经是公海客户");
+ ErrorCode CUSTOMER_LOCKED_PUT_POOL_FAIL = new ErrorCode(1_020_006_005, "客户【{}】放入公海失败,原因:客户已锁定");
+ ErrorCode CUSTOMER_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_006_006, "更新客户【{}】负责人失败, 原因:系统异常");
+ ErrorCode CUSTOMER_LOCK_FAIL_IS_LOCK = new ErrorCode(1_020_006_007, "锁定客户失败,它已经处于锁定状态");
+ ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, "解锁客户失败,它已经处于未锁定状态");
+ ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限");
+ ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限");
+ ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关联{}");
+ ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数据不能为空!");
+ ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户名称不能为空!");
+ ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在名为【{}】的客户!");
+
+ // ========== 权限管理 1_020_007_000 ==========
+ ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
+ ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限");
+ ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在");
+ ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:转移对象已经是该负责人");
+ ErrorCode CRM_PERMISSION_DELETE_FAIL = new ErrorCode(1_020_007_004, "删除数据权限失败,原因:批量删除权限的时候,只能属于同一个 bizId 下");
+ ErrorCode CRM_PERMISSION_DELETE_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_005, "删除数据权限失败,原因:存在负责人");
+ 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, "创建数据权限失败,原因:所加用户已有权限");
+
+ // ========== 产品 1_020_008_000 ==========
+ ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
+ ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在");
+
+ // ========== 产品分类 1_020_009_000 ==========
+ ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产品分类不存在");
+ ErrorCode PRODUCT_CATEGORY_EXISTS = new ErrorCode(1_020_009_001, "产品分类已存在");
+ ErrorCode PRODUCT_CATEGORY_USED = new ErrorCode(1_020_009_002, "产品分类已关联产品");
+ ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_020_009_003, "父分类不存在");
+ ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_020_009_004, "父分类不能是二级分类");
+ ErrorCode product_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_020_009_005, "存在子分类,无法删除");
+
+ // ========== 商机状态类型 1_020_010_000 ==========
+ ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_010_000, "商机状态类型不存在");
+ ErrorCode BUSINESS_STATUS_TYPE_NAME_EXISTS = new ErrorCode(1_020_010_001, "商机状态类型名称已存在");
+
+ // ========== 商机状态 1_020_011_000 ==========
+ ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状态不存在");
+
+ // ========== 客户公海规则设置 1_020_012_000 ==========
+ ErrorCode CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_020_012_000, "客户公海配置不存在或未启用");
+ ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户限制配置不存在");
+
+ // ========== 跟进记录 1_020_013_000 ==========
+ 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 ==========
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
new file mode 100644
index 000000000..98a66d2c9
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -0,0 +1,139 @@
+package cn.iocoder.yudao.module.crm.enums;
+
+/**
+ * CRM 操作日志枚举
+ * 目的:统一管理,也减少 Service 里各种“复杂”字符串
+ *
+ * @author HUIHUI
+ */
+public interface LogRecordConstants {
+
+ // ======================= CRM_LEADS 线索 =======================
+
+ String CRM_LEADS_TYPE = "CRM 线索";
+ String CRM_LEADS_CREATE_SUB_TYPE = "创建线索";
+ String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}";
+ String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索";
+ String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}";
+ String CRM_LEADS_DELETE_SUB_TYPE = "删除线索";
+ String CRM_LEADS_DELETE_SUCCESS = "删除了线索【{{#clueName}}】";
+ String CRM_LEADS_TRANSFER_SUB_TYPE = "转移线索";
+ String CRM_LEADS_TRANSFER_SUCCESS = "将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
+ String CRM_LEADS_TRANSLATE_SUB_TYPE = "线索转化为客户";
+ String CRM_LEADS_TRANSLATE_SUCCESS = "将线索【{{#clue.name}}】转化为客户";
+
+ // ======================= CRM_CUSTOMER 客户 =======================
+
+ String CRM_CUSTOMER_TYPE = "CRM 客户";
+ String CRM_CUSTOMER_CREATE_SUB_TYPE = "创建客户";
+ String CRM_CUSTOMER_CREATE_SUCCESS = "创建了客户{{#customer.name}}";
+ String CRM_CUSTOMER_UPDATE_SUB_TYPE = "更新客户";
+ String CRM_CUSTOMER_UPDATE_SUCCESS = "更新了客户【{{#customerName}}】: {_DIFF{#updateReqVO}}";
+ String CRM_CUSTOMER_DELETE_SUB_TYPE = "删除客户";
+ String CRM_CUSTOMER_DELETE_SUCCESS = "删除了客户【{{#customerName}}】";
+ String CRM_CUSTOMER_TRANSFER_SUB_TYPE = "转移客户";
+ String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#customer.name}}】的负责人从【{getAdminUserById{#customer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
+ String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#customer.lockStatus ? '解锁客户' : '锁定客户'}}";
+ String CRM_CUSTOMER_LOCK_SUCCESS = "{{#customer.lockStatus ? '将客户【' + #customer.name + '】解锁' : '将客户【' + #customer.name + '】锁定'}}";
+ String CRM_CUSTOMER_POOL_SUB_TYPE = "客户放入公海";
+ String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海";
+ String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}";
+ String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}";
+ String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}";
+ String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}";
+
+ // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 =======================
+
+ String CRM_CUSTOMER_LIMIT_CONFIG_TYPE = "CRM 客户限制配置";
+ String CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUB_TYPE = "创建客户限制配置";
+ String CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUCCESS = "创建了【{{#limitType}}】类型的客户限制配置";
+ String CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUB_TYPE = "更新客户限制配置";
+ String CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUCCESS = "更新了客户限制配置: {_DIFF{#updateReqVO}}";
+ String CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUB_TYPE = "删除客户限制配置";
+ String CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUCCESS = "删除了【{{#limitType}}】类型的客户限制配置";
+
+ // ======================= CRM_CUSTOMER_POOL_CONFIG 客户公海规则 =======================
+
+ String CRM_CUSTOMER_POOL_CONFIG_TYPE = "CRM 客户公海规则";
+ String CRM_CUSTOMER_POOL_CONFIG_SUB_TYPE = "{{#isPoolConfigUpdate ? '更新客户公海规则' : '创建客户公海规则'}}";
+ String CRM_CUSTOMER_POOL_CONFIG_SUCCESS = "{{#isPoolConfigUpdate ? '更新了客户公海规则' : '创建了客户公海规则'}}";
+
+ // ======================= CRM_CONTACT 联系人 =======================
+
+ String CRM_CONTACT_TYPE = "CRM 联系人";
+ String CRM_CONTACT_CREATE_SUB_TYPE = "创建联系人";
+ String CRM_CONTACT_CREATE_SUCCESS = "创建了联系人{{#contact.name}}";
+ String CRM_CONTACT_UPDATE_SUB_TYPE = "更新联系人";
+ String CRM_CONTACT_UPDATE_SUCCESS = "更新了联系人【{{#contactName}}】: {_DIFF{#updateReqVO}}";
+ String CRM_CONTACT_DELETE_SUB_TYPE = "删除联系人";
+ String CRM_CONTACT_DELETE_SUCCESS = "删除了联系人【{{#contactName}}】";
+ String CRM_CONTACT_TRANSFER_SUB_TYPE = "转移联系人";
+ String CRM_CONTACT_TRANSFER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
+
+ // ======================= CRM_BUSINESS 商机 =======================
+
+ String CRM_BUSINESS_TYPE = "CRM 商机";
+ String CRM_BUSINESS_CREATE_SUB_TYPE = "创建商机";
+ String CRM_BUSINESS_CREATE_SUCCESS = "创建了商机{{#business.name}}";
+ String CRM_BUSINESS_UPDATE_SUB_TYPE = "更新商机";
+ String CRM_BUSINESS_UPDATE_SUCCESS = "更新了商机【{{#businessName}}】: {_DIFF{#updateReqVO}}";
+ String CRM_BUSINESS_DELETE_SUB_TYPE = "删除商机";
+ String CRM_BUSINESS_DELETE_SUCCESS = "删除了商机【{{#businessName}}】";
+ String CRM_BUSINESS_TRANSFER_SUB_TYPE = "转移商机";
+ String CRM_BUSINESS_TRANSFER_SUCCESS = "将商机【{{#business.name}}】的负责人从【{getAdminUserById{#business.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
+
+ // ======================= CRM_CONTRACT 合同 =======================
+
+ String CRM_CONTRACT_TYPE = "CRM 合同";
+ String CRM_CONTRACT_CREATE_SUB_TYPE = "创建合同";
+ String CRM_CONTRACT_CREATE_SUCCESS = "创建了合同{{#contract.name}}";
+ String CRM_CONTRACT_UPDATE_SUB_TYPE = "更新合同";
+ String CRM_CONTRACT_UPDATE_SUCCESS = "更新了合同【{{#contractName}}】: {_DIFF{#updateReqVO}}";
+ String CRM_CONTRACT_DELETE_SUB_TYPE = "删除合同";
+ String CRM_CONTRACT_DELETE_SUCCESS = "删除了合同【{{#contractName}}】";
+ String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移合同";
+ String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{getAdminUserById{#contract.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
+ String CRM_CONTRACT_SUBMIT_SUB_TYPE = "提交合同审批";
+ String CRM_CONTRACT_SUBMIT_SUCCESS = "提交合同【{{#contractName}}】审批成功";
+
+ // ======================= CRM_PRODUCT 产品 =======================
+
+ String CRM_PRODUCT_TYPE = "CRM 产品";
+ String CRM_PRODUCT_CREATE_SUB_TYPE = "创建产品";
+ String CRM_PRODUCT_CREATE_SUCCESS = "创建了产品【{{#createReqVO.name}}】";
+ String CRM_PRODUCT_UPDATE_SUB_TYPE = "更新产品";
+ String CRM_PRODUCT_UPDATE_SUCCESS = "更新了产品【{{#updateReqVO.name}}】: {_DIFF{#updateReqVO}}";
+ String CRM_PRODUCT_DELETE_SUB_TYPE = "删除产品";
+ String CRM_PRODUCT_DELETE_SUCCESS = "删除了产品【{{#product.name}}】";
+
+ // ======================= CRM_PRODUCT_CATEGORY 产品分类 =======================
+
+ String CRM_PRODUCT_CATEGORY_TYPE = "CRM 产品分类";
+ String CRM_PRODUCT_CATEGORY_CREATE_SUB_TYPE = "创建产品分类";
+ String CRM_PRODUCT_CATEGORY_CREATE_SUCCESS = "创建了产品分类【{{#createReqVO.name}}】";
+ String CRM_PRODUCT_CATEGORY_UPDATE_SUB_TYPE = "更新产品分类";
+ String CRM_PRODUCT_CATEGORY_UPDATE_SUCCESS = "更新了产品分类【{{#updateReqVO.name}}】: {_DIFF{#updateReqVO}}";
+ String CRM_PRODUCT_CATEGORY_DELETE_SUB_TYPE = "删除产品分类";
+ String CRM_PRODUCT_CATEGORY_DELETE_SUCCESS = "删除了产品分类【{{#productCategory.name}}】";
+
+ // ======================= CRM_RECEIVABLE 回款 =======================
+
+ String CRM_RECEIVABLE_TYPE = "CRM 回款";
+ String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款";
+ String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
+ String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款";
+ String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
+ String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
+ String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
+
+ // ======================= CRM_RECEIVABLE_PLAN 回款计划 =======================
+
+ String CRM_RECEIVABLE_PLAN_TYPE = "CRM 回款计划";
+ String CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE = "创建回款计划";
+ String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = "创建了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
+ String CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE = "更新回款计划";
+ String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}";
+ String CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE = "删除回款计划";
+ String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = "删除了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java
new file mode 100644
index 000000000..55548dbff
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.crm.enums.business;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+// TODO @lzxhqs:1)title、description、create 可以删除,非标准的 javadoc 注释哈,然后可以在类上加下这个类的注释;2)CrmBizEndStatus 改成 CrmBusinessEndStatus,非必要不缩写哈,可阅读比较重要
+/**
+ * @author lzxhqs
+ * @version 1.0
+ * @title CrmBizEndStatus
+ * @description
+ * @create 2024/1/12
+ */
+@RequiredArgsConstructor
+@Getter
+public enum CrmBizEndStatus implements IntArrayValuable {
+
+ WIN(1, "赢单"),
+ LOSE(2, "输单"),
+ INVALID(3, "无效");
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizEndStatus::getStatus).toArray();
+
+ // TODO @lzxhqs:这里的方法,建议放到 49 行之后;一般类里是,静态变量,普通变量;静态方法;普通方法
+ public static boolean isWin(Integer status) {
+ return ObjectUtil.equal(WIN.getStatus(), status);
+ }
+
+ public static boolean isLose(Integer status) {
+ return ObjectUtil.equal(LOSE.getStatus(), status);
+ }
+
+ public static boolean isInvalid(Integer status) {
+ return ObjectUtil.equal(INVALID.getStatus(), status);
+ }
+
+ /**
+ * 场景类型
+ */
+ private final Integer status;
+ /**
+ * 场景名称
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java
new file mode 100644
index 000000000..67709e95b
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.crm.enums.common;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * CRM 的审批状态
+ *
+ * @author 赤焰
+ */
+@RequiredArgsConstructor
+@Getter
+public enum CrmAuditStatusEnum implements IntArrayValuable {
+
+ DRAFT(0, "未提交"),
+ PROCESS(10, "审批中"),
+ APPROVE(20, "审核通过"),
+ REJECT(30, "审核不通过"),
+ CANCEL(40, "已取消");
+
+ private final Integer status;
+ private final String name;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmAuditStatusEnum::getStatus).toArray();
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
new file mode 100644
index 000000000..f0784cab2
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.crm.enums.common;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * CRM 业务类型枚举
+ *
+ * @author HUIHUI
+ */
+@RequiredArgsConstructor
+@Getter
+public enum CrmBizTypeEnum implements IntArrayValuable {
+
+ CRM_LEADS(1, "线索"),
+ CRM_CUSTOMER(2, "客户"),
+ CRM_CONTACT(3, "联系人"),
+ CRM_BUSINESS(4, "商机"),
+ CRM_CONTRACT(5, "合同"),
+ CRM_PRODUCT(6, "产品"),
+ CRM_RECEIVABLE(7, "回款"),
+ CRM_RECEIVABLE_PLAN(8, "回款计划")
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizTypeEnum::getType).toArray();
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 名称
+ */
+ private final String name;
+
+ public static String getNameByType(Integer type) {
+ CrmBizTypeEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmBizTypeEnum.values()),
+ item -> ObjUtil.equal(item.type, type));
+ return typeEnum == null ? null : typeEnum.getName();
+ }
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java
new file mode 100644
index 000000000..945d7c6a3
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.crm.enums.common;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 列表检索场景
+ *
+ * @author HUIHUI
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmSceneTypeEnum implements IntArrayValuable {
+
+ OWNER(1, "我负责的"),
+ INVOLVED(2, "我参与的"),
+ SUBORDINATE(3, "下属负责的");
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmSceneTypeEnum::getType).toArray();
+
+ /**
+ * 场景类型
+ */
+ private final Integer type;
+ /**
+ * 场景名称
+ */
+ private final String name;
+
+ public static boolean isOwner(Integer type) {
+ return ObjUtil.equal(OWNER.getType(), type);
+ }
+
+ public static boolean isInvolved(Integer type) {
+ return ObjUtil.equal(INVOLVED.getType(), type);
+ }
+
+ public static boolean isSubordinate(Integer type) {
+ return ObjUtil.equal(SUBORDINATE.getType(), type);
+ }
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
new file mode 100644
index 000000000..aa06b05eb
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.crm.enums.customer;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 客户等级
+ *
+ * @author Wanwan
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmCustomerLevelEnum implements IntArrayValuable {
+
+ IMPORTANT(1, "A(重点客户)"),
+ GENERAL(2, "B(普通客户)"),
+ LOW_PRIORITY(3, "C(非优先客户)");
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLevelEnum::getLevel).toArray();
+
+ /**
+ * 状态
+ */
+ private final Integer level;
+ /**
+ * 状态名
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java
new file mode 100644
index 000000000..2cf8d7811
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.crm.enums.customer;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 客户限制配置规则类型
+ *
+ * @author Wanwan
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmCustomerLimitConfigTypeEnum implements IntArrayValuable {
+
+ /**
+ * 拥有客户数限制
+ */
+ CUSTOMER_OWNER_LIMIT(1, "拥有客户数限制"),
+ /**
+ * 锁定客户数限制
+ */
+ CUSTOMER_LOCK_LIMIT(2, "锁定客户数限制"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLimitConfigTypeEnum::getType).toArray();
+
+ /**
+ * 状态
+ */
+ private final Integer type;
+ /**
+ * 状态名
+ */
+ private final String name;
+
+ public static String getNameByType(Integer type) {
+ CrmCustomerLimitConfigTypeEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmCustomerLimitConfigTypeEnum.values()),
+ item -> ObjUtil.equal(item.type, type));
+ return typeEnum == null ? null : typeEnum.getName();
+ }
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java
new file mode 100644
index 000000000..56b0366aa
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.crm.enums.permission;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 数据权限级别枚举
+ *
+ * @author HUIHUI
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmPermissionLevelEnum implements IntArrayValuable {
+
+ OWNER(1, "负责人"),
+ READ(2, "读"),
+ WRITE(3, "写");
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmPermissionLevelEnum::getLevel).toArray();
+
+ /**
+ * 级别
+ */
+ private final Integer level;
+ /**
+ * 级别名称
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+ public static boolean isOwner(Integer level) {
+ return ObjUtil.equal(OWNER.level, level);
+ }
+
+ public static boolean isRead(Integer level) {
+ return ObjUtil.equal(READ.level, level);
+ }
+
+ public static boolean isWrite(Integer level) {
+ return ObjUtil.equal(WRITE.level, level);
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java
new file mode 100644
index 000000000..c9a51057b
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.crm.enums.permission;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Crm 数据权限角色枚举
+ *
+ * @author HUIHUI
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmPermissionRoleCodeEnum {
+
+ CRM_ADMIN("crm_admin", "CRM 管理员");
+
+ /**
+ * 角色标识
+ */
+ private String code;
+ /**
+ * 角色名称
+ */
+ private String name;
+
+}
+
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java
new file mode 100644
index 000000000..e82d5b5b8
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.crm.enums.product;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 商品状态
+ *
+ * @author ZanGe丶
+ * @since 2023-11-30 21:53
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmProductStatusEnum implements IntArrayValuable {
+
+ DISABLE(0, "下架"),
+ ENABLE(1, "上架");
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmProductStatusEnum::getStatus).toArray();
+
+ /**
+ * 状态
+ */
+ private final Integer status;
+ /**
+ * 状态名
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/pom.xml b/yudao-module-crm/yudao-module-crm-biz/pom.xml
new file mode 100644
index 000000000..323e873d9
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/pom.xml
@@ -0,0 +1,141 @@
+
+
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ *
+ * @author 芋道源码
+ */
+@SpringBootApplication
+public class CrmServerApplication {
+
+ public static void main(String[] args) {
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+
+ SpringApplication.run(CrmServerApplication.class, args);
+
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java
new file mode 100644
index 000000000..5c4e2493e
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * crm API 实现类,定义暴露给其它模块的 API
+ */
+package cn.iocoder.yudao.module.crm.api;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java
new file mode 100644
index 000000000..9b8841e2e
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.crm.controller.admin.backlog;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO;
+import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
+import cn.iocoder.yudao.module.crm.service.message.CrmBacklogService;
+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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - CRM待办消息")
+@RestController
+@RequestMapping("/crm/backlog")
+@Validated
+public class CrmBacklogController {
+
+ @Resource
+ private CrmBacklogService crmMessageService;
+
+ // TODO 芋艿:未来可能合并到 CrmCustomerController
+ @GetMapping("/today-customer-page")
+ @Operation(summary = "今日需联系客户")
+ @PreAuthorize("@ss.hasPermission('crm:customer:query')")
+ public CommonResult> getContractPriceRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getContractPriceRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-receivable-price-rank")
+ @Operation(summary = "获得回款金额排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getReceivablePriceRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getReceivablePriceRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-contract-count-rank")
+ @Operation(summary = "获得签约合同数量排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getContractCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getContractCountRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-product-sales-rank")
+ @Operation(summary = "获得产品销量排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getProductSalesRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getProductSalesRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-customer-count-rank")
+ @Operation(summary = "获得新增客户数排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getCustomerCountRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-contacts-count-rank")
+ @Operation(summary = "获得新增联系人数排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getContactsCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getContactsCountRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-follow-count-rank")
+ @Operation(summary = "获得跟进次数排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getFollowCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getFollowCountRank(rankingReqVO));
+ }
+
+ @GetMapping("/get-follow-customer-count-rank")
+ @Operation(summary = "获得跟进客户数排行榜")
+ @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
+ public CommonResult
> getFollowCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
+ return success(rankingService.getFollowCustomerCountRank(rankingReqVO));
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java
new file mode 100644
index 000000000..404ee3352
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.crm.controller.admin.bi.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@Schema(description = "管理后台 - CRM BI 排行榜 Response VO")
+@Data
+public class CrmBiRanKRespVO {
+
+ @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long ownerUserId;
+
+ @Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private String nickname;
+
+ @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private String deptName;
+
+ /**
+ * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义
+ *
+ * 1. 金额:合同金额排行、回款金额排行
+ * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行
+ */
+ @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer count;
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java
new file mode 100644
index 000000000..6d36f6d6f
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.crm.controller.admin.bi.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+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 BI 排行榜 Request VO")
+@Data
+public class CrmBiRankReqVO {
+
+ @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "部门 id 不能为空")
+ private Long deptId;
+
+ /**
+ * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
+ *
+ * 后续,可能会支持选择部分用户进行查询
+ */
+ @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2")
+ private List
> getContactListByIds(@RequestParam("ids") List
> getSimpleContactList() {
+ CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO();
+ reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
+ PageResult
> getBusinessStatusTypeList() {
+ CrmBusinessStatusTypeQueryVO queryVO = new CrmBusinessStatusTypeQueryVO();
+ queryVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
+ List
> getBusinessStatusListByTypeId(@RequestParam("typeId") Long typeId) {
+ CrmBusinessStatusQueryVO queryVO = new CrmBusinessStatusQueryVO();
+ queryVO.setTypeId(typeId);
+ List
> getContactListByIds(@RequestParam("ids") List
> getSimpleContactList() {
+ List
> getSimpleDeptList() {
+ CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO();
+ reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
+ List
> getPermissionList(@RequestParam("bizType") Integer bizType,
+ @RequestParam("bizId") Long bizId) {
+ List
> getProductCategoryList(@Valid CrmProductCategoryListReqVO listReqVO) {
+ List