同步最新代码到简化分支
This commit is contained in:
parent
8252abe3ef
commit
c2c7eb0084
|
|
@ -1,64 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.common.util.collection;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* {@link CollectionUtils} 的单元测试
|
||||
*/
|
||||
public class CollectionUtilsTest {
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
private static class Dog {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String code;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffList() {
|
||||
// 准备参数
|
||||
Collection<Dog> oldList = Arrays.asList(
|
||||
new Dog(1, "花花", "hh"),
|
||||
new Dog(2, "旺财", "wc")
|
||||
);
|
||||
Collection<Dog> newList = Arrays.asList(
|
||||
new Dog(null, "花花2", "hh"),
|
||||
new Dog(null, "小白", "xb")
|
||||
);
|
||||
BiFunction<Dog, Dog, Boolean> sameFunc = (oldObj, newObj) -> {
|
||||
boolean same = oldObj.getCode().equals(newObj.getCode());
|
||||
// 如果相等的情况下,需要设置下 id,后续好更新
|
||||
if (same) {
|
||||
newObj.setId(oldObj.getId());
|
||||
}
|
||||
return same;
|
||||
};
|
||||
|
||||
// 调用
|
||||
List<List<Dog>> result = CollectionUtils.diffList(oldList, newList, sameFunc);
|
||||
// 断言
|
||||
assertEquals(result.size(), 3);
|
||||
// 断言 create
|
||||
assertEquals(result.get(0).size(), 1);
|
||||
assertEquals(result.get(0).get(0), new Dog(null, "小白", "xb"));
|
||||
// 断言 update
|
||||
assertEquals(result.get(1).size(), 1);
|
||||
assertEquals(result.get(1).get(0), new Dog(1, "花花2", "hh"));
|
||||
// 断言 delete
|
||||
assertEquals(result.get(2).size(), 1);
|
||||
assertEquals(result.get(2).get(0), new Dog(2, "旺财", "wc"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程表达式 {@link BpmTaskCandidateStrategy} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.EXPRESSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// do nothing 因为它基本做不了校验
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
|
||||
Object result = FlowableUtils.getExpressionValue(execution, param);
|
||||
return Convert.toSet(Long.class, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
||||
spring:
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
mybatis-plus:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao.module
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.enums;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
|
||||
/**
|
||||
* CRM 错误码枚举类
|
||||
* <p>
|
||||
* 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_NOT_DRAFT = new ErrorCode(1_020_000_001, "合同更新失败,原因:合同不是草稿状态");
|
||||
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, "线索不存在");
|
||||
ErrorCode CLUE_TRANSFORM_FAIL_ALREADY = new ErrorCode(1_020_001_001, "线索已经转化过了,请勿重复转化");
|
||||
|
||||
// ========== 商机管理 1-020-002-000 ==========
|
||||
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");
|
||||
ErrorCode BUSINESS_DELETE_FAIL_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关联合同,不能删除");
|
||||
ErrorCode BUSINESS_UPDATE_STATUS_FAIL_END_STATUS = new ErrorCode(1_020_002_002, "更新商机状态失败,原因:已经是结束状态");
|
||||
ErrorCode BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS = new ErrorCode(1_020_002_003, "更新商机状态失败,原因:已经是该状态");
|
||||
|
||||
// ========== 联系人管理 1-020-003-000 ==========
|
||||
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
|
||||
ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除");
|
||||
ErrorCode CONTACT_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_003_003, "更新联系人负责人失败");
|
||||
|
||||
// ========== 回款 1-020-004-000 ==========
|
||||
ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在");
|
||||
ErrorCode RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_004_001, "更新回款失败,原因:禁止编辑");
|
||||
ErrorCode RECEIVABLE_DELETE_FAIL = new ErrorCode(1_020_004_002, "删除回款失败,原因: 被回款计划所使用,不允许删除");
|
||||
ErrorCode RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_004_003, "回款提交审核失败,原因:回款没处在未提交状态");
|
||||
ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, "更新回款审核状态失败,原因:回款不是审核中状态");
|
||||
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, "回款计划不存在");
|
||||
ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:已经有对应的还款");
|
||||
ErrorCode RECEIVABLE_PLAN_EXISTS_RECEIVABLE = new ErrorCode(1_020_006_001, "回款计划已经有对应的回款,不能使用");
|
||||
|
||||
// ========== 客户管理 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, "已存在名为【{}】的客户!");
|
||||
ErrorCode CUSTOMER_UPDATE_DEAL_STATUS_FAIL = new ErrorCode(1_020_006_015, "更新客户的成交状态失败,原因:已经是该状态,无需更新");
|
||||
|
||||
// ========== 权限管理 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_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_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, "产品不存在");
|
||||
ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在");
|
||||
ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_020_008_002, "产品【{}】已禁用");
|
||||
|
||||
// ========== 产品分类 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, "商机状态组的名称已存在");
|
||||
ErrorCode BUSINESS_STATUS_UPDATE_FAIL_USED = new ErrorCode(1_020_010_002, "已经被使用的商机状态组,无法进行更新");
|
||||
ErrorCode BUSINESS_STATUS_DELETE_FAIL_USED = new ErrorCode(1_020_010_002, "已经被使用的商机状态组,无法进行删除");
|
||||
ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_010_003, "商机状态不存在");
|
||||
|
||||
// ========== 客户公海规则设置 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 ==========
|
||||
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.enums;
|
||||
|
||||
/**
|
||||
* CRM 操作日志枚举
|
||||
* 目的:统一管理,也减少 Service 里各种“复杂”字符串
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface LogRecordConstants {
|
||||
|
||||
// ======================= CRM_CLUE 线索 =======================
|
||||
|
||||
String CRM_CLUE_TYPE = "CRM 线索";
|
||||
String CRM_CLUE_CREATE_SUB_TYPE = "创建线索";
|
||||
String CRM_CLUE_CREATE_SUCCESS = "创建了线索{{#clue.name}}";
|
||||
String CRM_CLUE_UPDATE_SUB_TYPE = "更新线索";
|
||||
String CRM_CLUE_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}";
|
||||
String CRM_CLUE_DELETE_SUB_TYPE = "删除线索";
|
||||
String CRM_CLUE_DELETE_SUCCESS = "删除了线索【{{#clueName}}】";
|
||||
String CRM_CLUE_TRANSFER_SUB_TYPE = "转移线索";
|
||||
String CRM_CLUE_TRANSFER_SUCCESS = "将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
|
||||
String CRM_CLUE_TRANSLATE_SUB_TYPE = "线索转化为客户";
|
||||
String CRM_CLUE_TRANSLATE_SUCCESS = "将线索【{{#clueName}}】转化为客户";
|
||||
String CRM_CLUE_FOLLOW_UP_SUB_TYPE = "线索跟进";
|
||||
String CRM_CLUE_FOLLOW_UP_SUCCESS = "线索跟进【{{#clueName}}】";
|
||||
|
||||
// ======================= 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 +'】'}}";
|
||||
String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE = "更新客户成交状态";
|
||||
String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS = "更新了客户【{{#customerName}}】的成交状态为【{{#dealStatus ? '已成交' : '未成交'}}】";
|
||||
String CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE = "客户跟进";
|
||||
String CRM_CUSTOMER_FOLLOW_UP_SUCCESS = "客户跟进【{{#customerName}}】";
|
||||
|
||||
// ======================= 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}}】";
|
||||
String CRM_CONTACT_FOLLOW_UP_SUB_TYPE = "联系人跟进";
|
||||
String CRM_CONTACT_FOLLOW_UP_SUCCESS = "联系人跟进【{{#contactName}}】";
|
||||
String CRM_CONTACT_UPDATE_OWNER_USER_SUB_TYPE = "更新联系人负责人";
|
||||
String CRM_CONTACT_UPDATE_OWNER_USER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#ownerUserId}}】";
|
||||
|
||||
// ======================= 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}}】";
|
||||
String CRM_BUSINESS_FOLLOW_UP_SUB_TYPE = "商机跟进";
|
||||
String CRM_BUSINESS_FOLLOW_UP_SUCCESS = "商机跟进【{{#businessName}}】";
|
||||
String CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE = "更新商机状态";
|
||||
String CRM_BUSINESS_UPDATE_STATUS_SUCCESS = "更新了商机【{{#businessName}}】的状态从【{{#oldStatusName}}】变更为了【{{#newStatusName}}】";
|
||||
|
||||
// ======================= CRM_CONTRACT_CONFIG 合同配置 =======================
|
||||
|
||||
String CRM_CONTRACT_CONFIG_TYPE = "CRM 合同配置";
|
||||
String CRM_CONTRACT_CONFIG_SUB_TYPE = "{{#isPoolConfigUpdate ? '更新合同配置' : '创建合同配置'}}";
|
||||
String CRM_CONTRACT_CONFIG_SUCCESS = "{{#isPoolConfigUpdate ? '更新了合同配置' : '创建了合同配置'}}";
|
||||
|
||||
// ======================= 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}}】审批成功";
|
||||
String CRM_CONTRACT_FOLLOW_UP_SUB_TYPE = "合同跟进";
|
||||
String CRM_CONTRACT_FOLLOW_UP_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}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款";
|
||||
String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款";
|
||||
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}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款";
|
||||
String CRM_RECEIVABLE_SUBMIT_SUB_TYPE = "提交回款审批";
|
||||
String CRM_RECEIVABLE_SUBMIT_SUCCESS = "提交编号为【{{#receivableNo}}】的回款审批成功";
|
||||
|
||||
// ======================= 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}}】期回款计划";
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
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;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* CRM 数据权限级别枚举
|
||||
*
|
||||
* OWNER > WRITE > READ
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.business;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
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.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 商机")
|
||||
@RestController
|
||||
@RequestMapping("/crm/business")
|
||||
@Validated
|
||||
public class CrmBusinessController {
|
||||
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmBusinessStatusService businessStatusTypeService;
|
||||
@Resource
|
||||
private CrmBusinessStatusService businessStatusService;
|
||||
@Resource
|
||||
private CrmProductService productService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建商机")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:create')")
|
||||
public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessSaveReqVO createReqVO) {
|
||||
return success(businessService.createBusiness(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新商机")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:update')")
|
||||
public CommonResult<Boolean> updateBusiness(@Valid @RequestBody CrmBusinessSaveReqVO updateReqVO) {
|
||||
businessService.updateBusiness(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新商机状态")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:update')")
|
||||
public CommonResult<Boolean> updateBusinessStatus(@Valid @RequestBody CrmBusinessUpdateStatusReqVO updateStatusReqVO) {
|
||||
businessService.updateBusinessStatus(updateStatusReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除商机")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:delete')")
|
||||
public CommonResult<Boolean> deleteBusiness(@RequestParam("id") Long id) {
|
||||
businessService.deleteBusiness(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得商机")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||
public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
|
||||
CrmBusinessDO business = businessService.getBusiness(id);
|
||||
return success(buildBusinessDetail(business));
|
||||
}
|
||||
|
||||
private CrmBusinessRespVO buildBusinessDetail(CrmBusinessDO business) {
|
||||
if (business == null) {
|
||||
return null;
|
||||
}
|
||||
CrmBusinessRespVO businessVO = buildBusinessDetailList(Collections.singletonList(business)).get(0);
|
||||
// 拼接产品项
|
||||
List<CrmBusinessProductDO> businessProducts = businessService.getBusinessProductListByBusinessId(businessVO.getId());
|
||||
Map<Long, CrmProductDO> productMap = productService.getProductMap(
|
||||
convertSet(businessProducts, CrmBusinessProductDO::getProductId));
|
||||
businessVO.setProducts(BeanUtils.toBean(businessProducts, CrmBusinessRespVO.Product.class, businessProductVO ->
|
||||
MapUtils.findAndThen(productMap, businessProductVO.getProductId(),
|
||||
product -> businessProductVO.setProductName(product.getName())
|
||||
.setProductNo(product.getNo()).setProductUnit(product.getUnit()))));
|
||||
return businessVO;
|
||||
}
|
||||
|
||||
@GetMapping("/simple-all-list")
|
||||
@Operation(summary = "获得联系人的精简列表")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||
public CommonResult<List<CrmBusinessRespVO>> getSimpleContactList() {
|
||||
CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO();
|
||||
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
|
||||
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(reqVO, getLoginUserId());
|
||||
return success(convertList(pageResult.getList(), business -> // 只返回 id、name 字段
|
||||
new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())
|
||||
.setCustomerId(business.getCustomerId())));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商机分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
|
||||
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-customer")
|
||||
@Operation(summary = "获得商机分页,基于指定客户")
|
||||
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) {
|
||||
if (pageReqVO.getCustomerId() == null) {
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
}
|
||||
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);
|
||||
return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-contact")
|
||||
@Operation(summary = "获得联系人的商机分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) {
|
||||
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByContact(pageReqVO);
|
||||
return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出商机 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PAGE_SIZE_NONE);
|
||||
List<CrmBusinessDO> list = businessService.getBusinessPage(exportReqVO, getLoginUserId()).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessRespVO.class,
|
||||
buildBusinessDetailList(list));
|
||||
}
|
||||
|
||||
private List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户列表
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(list, CrmBusinessDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 1.3 获得商机状态组
|
||||
Map<Long, CrmBusinessStatusTypeDO> statusTypeMap = businessStatusTypeService.getBusinessStatusTypeMap(
|
||||
convertSet(list, CrmBusinessDO::getStatusTypeId));
|
||||
Map<Long, CrmBusinessStatusDO> statusMap = businessStatusService.getBusinessStatusMap(
|
||||
convertSet(list, CrmBusinessDO::getStatusId));
|
||||
// 2. 拼接数据
|
||||
return BeanUtils.toBean(list, CrmBusinessRespVO.class, businessVO -> {
|
||||
// 2.1 设置客户名称
|
||||
MapUtils.findAndThen(customerMap, businessVO.getCustomerId(), customer -> businessVO.setCustomerName(customer.getName()));
|
||||
// 2.2 设置创建人、负责人名称
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(businessVO.getCreator()),
|
||||
user -> businessVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, businessVO.getOwnerUserId(), user -> {
|
||||
businessVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> businessVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
// 2.3 设置商机状态
|
||||
MapUtils.findAndThen(statusTypeMap, businessVO.getStatusTypeId(), statusType -> businessVO.setStatusTypeName(statusType.getName()));
|
||||
MapUtils.findAndThen(statusMap, businessVO.getStatusId(), status -> businessVO.setStatusName(
|
||||
businessService.getBusinessStatusName(businessVO.getEndStatus(), status)));
|
||||
});
|
||||
}
|
||||
|
||||
@PutMapping("/transfer")
|
||||
@Operation(summary = "商机转移")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:update')")
|
||||
public CommonResult<Boolean> transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {
|
||||
businessService.transferBusiness(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;
|
||||
import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
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 CrmBusinessSaveReqVO {
|
||||
|
||||
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
@DiffLogField(name = "商机名称")
|
||||
@NotNull(message = "商机名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
|
||||
@DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
|
||||
@NotNull(message = "客户不能为空")
|
||||
private Long customerId;
|
||||
|
||||
@Schema(description = "下次联系时间")
|
||||
@DiffLogField(name = "下次联系时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime contactNextTime;
|
||||
|
||||
@Schema(description = "负责人用户编号", example = "14334")
|
||||
@NotNull(message = "负责人不能为空")
|
||||
@DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME)
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "商机状态组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
|
||||
@DiffLogField(name = "商机状态组")
|
||||
@NotNull(message = "商机状态组不能为空")
|
||||
private Long statusTypeId;
|
||||
|
||||
@Schema(description = "预计成交日期")
|
||||
@DiffLogField(name = "预计成交日期")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime dealTime;
|
||||
|
||||
@Schema(description = "整单折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "55.00")
|
||||
@DiffLogField(name = "整单折扣")
|
||||
@NotNull(message = "整单折扣不能为空")
|
||||
private BigDecimal discountPercent;
|
||||
|
||||
@Schema(description = "备注", example = "随便")
|
||||
@DiffLogField(name = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "联系人编号", example = "110")
|
||||
private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
private List<BusinessProduct> businessProducts;
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class BusinessProduct {
|
||||
|
||||
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
|
||||
@NotNull(message = "产品编号不能为空")
|
||||
private Long productId;
|
||||
|
||||
@Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
|
||||
@NotNull(message = "产品单价不能为空")
|
||||
private BigDecimal productPrice;
|
||||
|
||||
@Schema(description = "商机价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
|
||||
@NotNull(message = "商机价格不能为空")
|
||||
private BigDecimal businessPrice;
|
||||
|
||||
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
|
||||
@NotNull(message = "产品数量不能为空")
|
||||
private Integer count;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
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")
|
||||
@NotNull(message = "商机编号不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 新负责人的用户编号
|
||||
*/
|
||||
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
@NotNull(message = "新负责人的用户编号不能为空")
|
||||
private Long newOwnerUserId;
|
||||
|
||||
/**
|
||||
* 老负责人加入团队后的权限级别。如果 null 说明移除
|
||||
*
|
||||
* 关联 {@link CrmPermissionLevelEnum}
|
||||
*/
|
||||
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer oldOwnerPermissionLevel;
|
||||
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.clue;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.service.clue.CrmClueService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
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.convertListByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - 线索")
|
||||
@RestController
|
||||
@RequestMapping("/crm/clue")
|
||||
@Validated
|
||||
public class CrmClueController {
|
||||
|
||||
@Resource
|
||||
private CrmClueService clueService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建线索")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:create')")
|
||||
public CommonResult<Long> createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) {
|
||||
return success(clueService.createClue(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新线索")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:update')")
|
||||
public CommonResult<Boolean> updateClue(@Valid @RequestBody CrmClueSaveReqVO updateReqVO) {
|
||||
clueService.updateClue(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除线索")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:delete')")
|
||||
public CommonResult<Boolean> deleteClue(@RequestParam("id") Long id) {
|
||||
clueService.deleteClue(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得线索")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:query')")
|
||||
public CommonResult<CrmClueRespVO> getClue(@RequestParam("id") Long id) {
|
||||
CrmClueDO clue = clueService.getClue(id);
|
||||
return success(buildClueDetail(clue));
|
||||
}
|
||||
|
||||
private CrmClueRespVO buildClueDetail(CrmClueDO clue) {
|
||||
if (clue == null) {
|
||||
return null;
|
||||
}
|
||||
return buildClueDetailList(singletonList(clue)).get(0);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得线索分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:query')")
|
||||
public CommonResult<PageResult<CrmClueRespVO>> getCluePage(@Valid CrmCluePageReqVO pageVO) {
|
||||
PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildClueDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出线索 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportClueExcel(@Valid CrmCluePageReqVO pageReqVO, HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PAGE_SIZE_NONE);
|
||||
List<CrmClueDO> list = clueService.getCluePage(pageReqVO, getLoginUserId()).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "线索.xls", "数据", CrmClueRespVO.class, buildClueDetailList(list));
|
||||
}
|
||||
|
||||
private List<CrmClueRespVO> buildClueDetailList(List<CrmClueDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户列表
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(list, CrmClueDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 2. 转换成 VO
|
||||
return BeanUtils.toBean(list, CrmClueRespVO.class, clueVO -> {
|
||||
clueVO.setAreaName(AreaUtils.format(clueVO.getAreaId()));
|
||||
// 2.1 设置客户名称
|
||||
MapUtils.findAndThen(customerMap, clueVO.getCustomerId(), customer -> clueVO.setCustomerName(customer.getName()));
|
||||
// 2.2 设置创建人、负责人名称
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(clueVO.getCreator()),
|
||||
user -> clueVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, clueVO.getOwnerUserId(), user -> {
|
||||
clueVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> clueVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@PutMapping("/transfer")
|
||||
@Operation(summary = "线索转移")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:update')")
|
||||
public CommonResult<Boolean> transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) {
|
||||
clueService.transferClue(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/transform")
|
||||
@Operation(summary = "线索转化为客户")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:update')")
|
||||
public CommonResult<Boolean> transformClue(@RequestParam("id") Long id) {
|
||||
clueService.transformClue(id, getLoginUserId());
|
||||
return success(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@GetMapping("/follow-count")
|
||||
@Operation(summary = "获得分配给我的、待跟进的线索数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:clue:query')")
|
||||
public CommonResult<Long> getFollowClueCount() {
|
||||
return success(clueService.getFollowClueCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.contact;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
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.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 联系人")
|
||||
@RestController
|
||||
@RequestMapping("/crm/contact")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class CrmContactController {
|
||||
|
||||
@Resource
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmContactBusinessService contactBusinessLinkService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建联系人")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:create')")
|
||||
public CommonResult<Long> createContact(@Valid @RequestBody CrmContactSaveReqVO createReqVO) {
|
||||
return success(contactService.createContact(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新联系人")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:update')")
|
||||
public CommonResult<Boolean> updateContact(@Valid @RequestBody CrmContactSaveReqVO updateReqVO) {
|
||||
contactService.updateContact(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除联系人")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:delete')")
|
||||
public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) {
|
||||
contactService.deleteContact(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得联系人")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||
public CommonResult<CrmContactRespVO> getContact(@RequestParam("id") Long id) {
|
||||
CrmContactDO contact = contactService.getContact(id);
|
||||
return success(buildContactDetail(contact));
|
||||
}
|
||||
|
||||
private CrmContactRespVO buildContactDetail(CrmContactDO contact) {
|
||||
if (contact == null) {
|
||||
return null;
|
||||
}
|
||||
return buildContactDetailList(singletonList(contact)).get(0);
|
||||
}
|
||||
|
||||
@GetMapping("/simple-all-list")
|
||||
@Operation(summary = "获得联系人的精简列表")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||
public CommonResult<List<CrmContactRespVO>> getSimpleContactList() {
|
||||
List<CrmContactDO> list = contactService.getContactList(getLoginUserId());
|
||||
return success(convertList(list, contact -> // 只返回 id、name 字段
|
||||
new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())
|
||||
.setCustomerId(contact.getCustomerId())));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得联系人分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||
public CommonResult<PageResult<CrmContactRespVO>> getContactPage(@Valid CrmContactPageReqVO pageVO) {
|
||||
PageResult<CrmContactDO> pageResult = contactService.getContactPage(pageVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildContactDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-customer")
|
||||
@Operation(summary = "获得联系人分页,基于指定客户")
|
||||
public CommonResult<PageResult<CrmContactRespVO>> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) {
|
||||
Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
|
||||
PageResult<CrmContactDO> pageResult = contactService.getContactPageByCustomerId(pageVO);
|
||||
return success(new PageResult<>(buildContactDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-business")
|
||||
@Operation(summary = "获得联系人分页,基于指定商机")
|
||||
public CommonResult<PageResult<CrmContactRespVO>> getContactPageByBusiness(@Valid CrmContactPageReqVO pageVO) {
|
||||
Assert.notNull(pageVO.getBusinessId(), "商机编号不能为空");
|
||||
PageResult<CrmContactDO> pageResult = contactService.getContactPageByBusinessId(pageVO);
|
||||
return success(new PageResult<>(buildContactDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出联系人 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportContactExcel(@Valid CrmContactPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageNo(PAGE_SIZE_NONE);
|
||||
List<CrmContactDO> list = contactService.getContactPage(exportReqVO, getLoginUserId()).getList();
|
||||
ExcelUtils.write(response, "联系人.xls", "数据", CrmContactRespVO.class, buildContactDetailList(list));
|
||||
}
|
||||
|
||||
private List<CrmContactRespVO> buildContactDetailList(List<CrmContactDO> contactList) {
|
||||
if (CollUtil.isEmpty(contactList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户列表
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(contactList, CrmContactDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 1.3 直属上级 Map
|
||||
Map<Long, CrmContactDO> parentContactMap = contactService.getContactMap(
|
||||
convertSet(contactList, CrmContactDO::getParentId));
|
||||
// 2. 转换成 VO
|
||||
return BeanUtils.toBean(contactList, CrmContactRespVO.class, contactVO -> {
|
||||
contactVO.setAreaName(AreaUtils.format(contactVO.getAreaId()));
|
||||
// 2.1 设置客户名称
|
||||
MapUtils.findAndThen(customerMap, contactVO.getCustomerId(), customer -> contactVO.setCustomerName(customer.getName()));
|
||||
// 2.2 设置创建人、负责人名称
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(contactVO.getCreator()),
|
||||
user -> contactVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, contactVO.getOwnerUserId(), user -> {
|
||||
contactVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> contactVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
// 2.3 设置直属上级名称
|
||||
findAndThen(parentContactMap, contactVO.getParentId(), contact -> contactVO.setParentName(contact.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
@PutMapping("/transfer")
|
||||
@Operation(summary = "联系人转移")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:update')")
|
||||
public CommonResult<Boolean> transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
|
||||
contactService.transferContact(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ================== 关联/取关商机 ===================
|
||||
|
||||
@PostMapping("/create-business-list")
|
||||
@Operation(summary = "创建联系人与商机的关联")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:create-business')")
|
||||
public CommonResult<Boolean> createContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO createReqVO) {
|
||||
contactBusinessLinkService.createContactBusinessList(createReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/create-business-list2")
|
||||
@Operation(summary = "创建联系人与商机的关联")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:create-business')")
|
||||
public CommonResult<Boolean> createContactBusinessList2(@Valid @RequestBody CrmContactBusiness2ReqVO createReqVO) {
|
||||
contactBusinessLinkService.createContactBusinessList2(createReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-business-list")
|
||||
@Operation(summary = "删除联系人与联系人的关联")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:delete-business')")
|
||||
public CommonResult<Boolean> deleteContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO deleteReqVO) {
|
||||
contactBusinessLinkService.deleteContactBusinessList(deleteReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-business-list2")
|
||||
@Operation(summary = "删除联系人与联系人的关联")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contact:delete-business')")
|
||||
public CommonResult<Boolean> deleteContactBusinessList(@Valid @RequestBody CrmContactBusiness2ReqVO deleteReqVO) {
|
||||
contactBusinessLinkService.deleteContactBusinessList2(deleteReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
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")
|
||||
@NotNull(message = "联系人编号不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 新负责人的用户编号
|
||||
*/
|
||||
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
@NotNull(message = "新负责人的用户编号不能为空")
|
||||
private Long newOwnerUserId;
|
||||
|
||||
/**
|
||||
* 老负责人加入团队后的权限级别。如果 null 说明移除
|
||||
*
|
||||
* 关联 {@link CrmPermissionLevelEnum}
|
||||
*/
|
||||
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer oldOwnerPermissionLevel;
|
||||
|
||||
}
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.contract;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
|
||||
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.contract.CrmContractProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
|
||||
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.customer.CrmCustomerService;
|
||||
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.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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 合同")
|
||||
@RestController
|
||||
@RequestMapping("/crm/contract")
|
||||
@Validated
|
||||
public class CrmContractController {
|
||||
|
||||
@Resource
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
private CrmProductService productService;
|
||||
@Resource
|
||||
private CrmReceivableService receivableService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建合同")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:create')")
|
||||
public CommonResult<Long> createContract(@Valid @RequestBody CrmContractSaveReqVO createReqVO) {
|
||||
return success(contractService.createContract(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新合同")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
|
||||
public CommonResult<Boolean> updateContract(@Valid @RequestBody CrmContractSaveReqVO updateReqVO) {
|
||||
contractService.updateContract(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除合同")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:delete')")
|
||||
public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) {
|
||||
contractService.deleteContract(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得合同")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||
public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) {
|
||||
CrmContractDO contract = contractService.getContract(id);
|
||||
return success(buildContractDetail(contract));
|
||||
}
|
||||
|
||||
private CrmContractRespVO buildContractDetail(CrmContractDO contract) {
|
||||
if (contract == null) {
|
||||
return null;
|
||||
}
|
||||
CrmContractRespVO contractVO = buildContractDetailList(singletonList(contract)).get(0);
|
||||
// 拼接产品项
|
||||
List<CrmContractProductDO> businessProducts = contractService.getContractProductListByContractId(contractVO.getId());
|
||||
Map<Long, CrmProductDO> productMap = productService.getProductMap(
|
||||
convertSet(businessProducts, CrmContractProductDO::getProductId));
|
||||
contractVO.setProducts(BeanUtils.toBean(businessProducts, CrmContractRespVO.Product.class, businessProductVO ->
|
||||
MapUtils.findAndThen(productMap, businessProductVO.getProductId(),
|
||||
product -> businessProductVO.setProductName(product.getName())
|
||||
.setProductNo(product.getNo()).setProductUnit(product.getUnit()))));
|
||||
return contractVO;
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得合同分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||
public CommonResult<PageResult<CrmContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {
|
||||
PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId());
|
||||
return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-customer")
|
||||
@Operation(summary = "获得合同分页,基于指定客户")
|
||||
public CommonResult<PageResult<CrmContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
|
||||
Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
|
||||
PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-business")
|
||||
@Operation(summary = "获得合同分页,基于指定商机")
|
||||
public CommonResult<PageResult<CrmContractRespVO>> getContractPageByBusiness(@Valid CrmContractPageReqVO pageVO) {
|
||||
Assert.notNull(pageVO.getBusinessId(), "商机编号不能为空");
|
||||
PageResult<CrmContractDO> pageResult = contractService.getContractPageByBusinessId(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出合同 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportContractExcel(@Valid CrmContractPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
PageResult<CrmContractDO> pageResult = contractService.getContractPage(exportReqVO, getLoginUserId());
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "合同.xls", "数据", CrmContractRespVO.class,
|
||||
BeanUtils.toBean(pageResult.getList(), CrmContractRespVO.class));
|
||||
}
|
||||
|
||||
@PutMapping("/transfer")
|
||||
@Operation(summary = "合同转移")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
|
||||
public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
|
||||
contractService.transferContract(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/submit")
|
||||
@Operation(summary = "提交合同审批")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
|
||||
public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
|
||||
contractService.submitContract(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
private List<CrmContractRespVO> buildContractDetailList(List<CrmContractDO> contractList) {
|
||||
if (CollUtil.isEmpty(contractList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户列表
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(contractList, CrmContractDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 1.3 获取联系人
|
||||
Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList(convertSet(contractList,
|
||||
CrmContractDO::getSignContactId)), CrmContactDO::getId);
|
||||
// 1.4 获取商机
|
||||
Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(
|
||||
convertSet(contractList, CrmContractDO::getBusinessId));
|
||||
// 1.5 获得已回款金额
|
||||
Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId(
|
||||
convertSet(contractList, CrmContractDO::getId));
|
||||
// 2. 拼接数据
|
||||
return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> {
|
||||
// 2.1 设置客户信息
|
||||
findAndThen(customerMap, contractVO.getCustomerId(), customer -> contractVO.setCustomerName(customer.getName()));
|
||||
// 2.2 设置用户信息
|
||||
findAndThen(userMap, Long.parseLong(contractVO.getCreator()), user -> contractVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, contractVO.getOwnerUserId(), user -> {
|
||||
contractVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> contractVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
findAndThen(userMap, contractVO.getSignUserId(), user -> contractVO.setSignUserName(user.getNickname()));
|
||||
// 2.3 设置联系人信息
|
||||
findAndThen(contactMap, contractVO.getSignContactId(), contact -> contractVO.setSignContactName(contact.getName()));
|
||||
// 2.4 设置商机信息
|
||||
findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName()));
|
||||
// 2.5 设置已回款金额
|
||||
contractVO.setTotalReceivablePrice(receivablePriceMap.getOrDefault(contractVO.getId(), BigDecimal.ZERO));
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping("/audit-count")
|
||||
@Operation(summary = "获得待审核合同数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||
public CommonResult<Long> getAuditContractCount() {
|
||||
return success(contractService.getAuditContractCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/remind-count")
|
||||
@Operation(summary = "获得即将到期(提醒)的合同数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||
public CommonResult<Long> getRemindContractCount() {
|
||||
return success(contractService.getRemindContractCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得合同精简列表", description = "只包含的合同,主要用于前端的下拉选项")
|
||||
@Parameter(name = "customerId", description = "客户编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||
public CommonResult<List<CrmContractRespVO>> getContractSimpleList(@RequestParam("customerId") Long customerId) {
|
||||
CrmContractPageReqVO pageReqVO = new CrmContractPageReqVO().setCustomerId(customerId);
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); // 不分页
|
||||
PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
// 拼接数据
|
||||
Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId(
|
||||
convertSet(pageResult.getList(), CrmContractDO::getId));
|
||||
return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // 只返回 id、name 等精简字段
|
||||
.setId(contract.getId()).setName(contract.getName()).setAuditStatus(contract.getAuditStatus())
|
||||
.setTotalPrice(contract.getTotalPrice())
|
||||
.setTotalReceivablePrice(receivablePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO))));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
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")
|
||||
@NotNull(message = "联系人编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
@NotNull(message = "新负责人的用户编号不能为空")
|
||||
private Long newOwnerUserId;
|
||||
|
||||
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@InEnum(value = CrmPermissionLevelEnum.class)
|
||||
private Integer oldOwnerPermissionLevel;
|
||||
|
||||
}
|
||||
|
|
@ -1,316 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.customer;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
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.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.util.ExcelUtils;
|
||||
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;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
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 java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 客户")
|
||||
@RestController
|
||||
@RequestMapping("/crm/customer")
|
||||
@Validated
|
||||
public class CrmCustomerController {
|
||||
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmCustomerPoolConfigService customerPoolConfigService;
|
||||
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:create')")
|
||||
public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerSaveReqVO createReqVO) {
|
||||
return success(customerService.createCustomer(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||
public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerSaveReqVO updateReqVO) {
|
||||
customerService.updateCustomer(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-deal-status")
|
||||
@Operation(summary = "更新客户的成交状态")
|
||||
@Parameters({
|
||||
@Parameter(name = "id", description = "客户编号", required = true),
|
||||
@Parameter(name = "dealStatus", description = "成交状态", required = true)
|
||||
})
|
||||
public CommonResult<Boolean> updateCustomerDealStatus(@RequestParam("id") Long id,
|
||||
@RequestParam("dealStatus") Boolean dealStatus) {
|
||||
customerService.updateCustomerDealStatus(id, dealStatus);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除客户")
|
||||
@Parameter(name = "id", description = "客户编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:delete')")
|
||||
public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) {
|
||||
customerService.deleteCustomer(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得客户")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam("id") Long id) {
|
||||
// 1. 获取客户
|
||||
CrmCustomerDO customer = customerService.getCustomer(id);
|
||||
// 2. 拼接数据
|
||||
return success(buildCustomerDetail(customer));
|
||||
}
|
||||
|
||||
public CrmCustomerRespVO buildCustomerDetail(CrmCustomerDO customer) {
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
return buildCustomerDetailList(singletonList(customer)).get(0);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得客户分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||
// 1. 查询客户分页
|
||||
PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId());
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
}
|
||||
// 2. 拼接数据
|
||||
return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
public List<CrmCustomerRespVO> buildCustomerDetailList(List<CrmCustomerDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取创建人、负责人列表
|
||||
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 获取距离进入公海的时间
|
||||
Map<Long, Long> poolDayMap = getPoolDayMap(list);
|
||||
// 2. 转换成 VO
|
||||
return BeanUtils.toBean(list, CrmCustomerRespVO.class, customerVO -> {
|
||||
customerVO.setAreaName(AreaUtils.format(customerVO.getAreaId()));
|
||||
// 2.1 设置创建人、负责人名称
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(customerVO.getCreator()),
|
||||
user -> customerVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, customerVO.getOwnerUserId(), user -> {
|
||||
customerVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> customerVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
// 2.2 设置距离进入公海的时间
|
||||
if (customerVO.getOwnerUserId() != null) {
|
||||
customerVO.setPoolDay(poolDayMap.get(customerVO.getId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping("/put-pool-remind-page")
|
||||
@Operation(summary = "获得待进入公海客户分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<PageResult<CrmCustomerRespVO>> getPutPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||
// 1. 查询客户分页
|
||||
PageResult<CrmCustomerDO> pageResult = customerService.getPutPoolRemindCustomerPage(pageVO, getLoginUserId());
|
||||
// 2. 拼接数据
|
||||
return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/put-pool-remind-count")
|
||||
@Operation(summary = "获得待进入公海客户数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<Long> getPutPoolRemindCustomerCount() {
|
||||
return success(customerService.getPutPoolRemindCustomerCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/today-contact-count")
|
||||
@Operation(summary = "获得今日需联系客户数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<Long> getTodayContactCustomerCount() {
|
||||
return success(customerService.getTodayContactCustomerCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/follow-count")
|
||||
@Operation(summary = "获得分配给我、待跟进的线索数量的客户数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<Long> getFollowCustomerCount() {
|
||||
return success(customerService.getFollowCustomerCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取距离进入公海的时间 Map
|
||||
*
|
||||
* @param list 客户列表
|
||||
* @return key 客户编号, value 距离进入公海的时间
|
||||
*/
|
||||
private Map<Long, Long> getPoolDayMap(List<CrmCustomerDO> list) {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (poolConfig == null || !poolConfig.getEnabled()) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
list = CollectionUtils.filterList(list, customer -> {
|
||||
// 特殊:如果没负责人,则说明已经在公海,不用计算
|
||||
if (customer.getOwnerUserId() == null) {
|
||||
return false;
|
||||
}
|
||||
// 已成交 or 已锁定,不进入公海
|
||||
return !customer.getDealStatus() && !customer.getLockStatus();
|
||||
});
|
||||
return convertMap(list, CrmCustomerDO::getId, customer -> {
|
||||
// 1.1 未成交放入公海天数
|
||||
long dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getOwnerTime());
|
||||
// 1.2 未跟进放入公海天数
|
||||
LocalDateTime lastTime = customer.getOwnerTime();
|
||||
if (customer.getContactLastTime() != null && customer.getContactLastTime().isAfter(lastTime)) {
|
||||
lastTime = customer.getContactLastTime();
|
||||
}
|
||||
long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
|
||||
// 2. 返回最小的天数
|
||||
long poolDay = Math.min(dealExpireDay, contactExpireDay);
|
||||
return poolDay > 0 ? poolDay : 0;
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping(value = "/simple-list")
|
||||
@Operation(summary = "获取客户精简信息列表", description = "只包含有读权限的客户,主要用于前端的下拉选项")
|
||||
public CommonResult<List<CrmCustomerRespVO>> getCustomerSimpleList() {
|
||||
CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO();
|
||||
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
|
||||
List<CrmCustomerDO> list = customerService.getCustomerPage(reqVO, getLoginUserId()).getList();
|
||||
return success(convertList(list, customer -> // 只返回 id、name 精简字段
|
||||
new CrmCustomerRespVO().setId(customer.getId()).setName(customer.getName())));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出客户 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportCustomerExcel(@Valid CrmCustomerPageReqVO pageVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageVO.setPageSize(PAGE_SIZE_NONE); // 不分页
|
||||
List<CrmCustomerDO> list = customerService.getCustomerPage(pageVO, getLoginUserId()).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "客户.xls", "数据", CrmCustomerRespVO.class,
|
||||
buildCustomerDetailList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/get-import-template")
|
||||
@Operation(summary = "获得导入客户模板")
|
||||
public void importTemplate(HttpServletResponse response) throws IOException {
|
||||
// 手动创建导出 demo
|
||||
List<CrmCustomerImportExcelVO> list = Arrays.asList(
|
||||
CrmCustomerImportExcelVO.builder().name("芋道").industryId(1).level(1).source(1)
|
||||
.mobile("15601691300").telephone("").qq("").wechat("").email("yunai@iocoder.cn")
|
||||
.areaId(null).detailAddress("").remark("").build(),
|
||||
CrmCustomerImportExcelVO.builder().name("源码").industryId(1).level(1).source(1)
|
||||
.mobile("15601691300").telephone("").qq("").wechat("").email("yunai@iocoder.cn")
|
||||
.areaId(null).detailAddress("").remark("").build()
|
||||
);
|
||||
// 输出
|
||||
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:import')")
|
||||
public CommonResult<CrmCustomerImportRespVO> importExcel(@Valid CrmCustomerImportReqVO importReqVO)
|
||||
throws Exception {
|
||||
List<CrmCustomerImportExcelVO> list = ExcelUtils.read(importReqVO.getFile(), CrmCustomerImportExcelVO.class);
|
||||
return success(customerService.importCustomerList(list, importReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/transfer")
|
||||
@Operation(summary = "转移客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||
public CommonResult<Boolean> transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
|
||||
customerService.transferCustomer(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/lock")
|
||||
@Operation(summary = "锁定/解锁客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||
public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerLockReqVO lockReqVO) {
|
||||
customerService.lockCustomer(lockReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 公海相关操作 ====================
|
||||
|
||||
@PutMapping("/put-pool")
|
||||
@Operation(summary = "数据放入公海")
|
||||
@Parameter(name = "id", description = "客户编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||
public CommonResult<Boolean> putCustomerPool(@RequestParam("id") Long id) {
|
||||
customerService.putCustomerPool(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/receive")
|
||||
@Operation(summary = "领取公海客户")
|
||||
@Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:receive')")
|
||||
public CommonResult<Boolean> receiveCustomer(@RequestParam(value = "ids") List<Long> ids) {
|
||||
customerService.receiveCustomer(ids, getLoginUserId(), Boolean.TRUE);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/distribute")
|
||||
@Operation(summary = "分配公海给对应负责人")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:distribute')")
|
||||
public CommonResult<Boolean> distributeCustomer(@Valid @RequestBody CrmCustomerDistributeReqVO distributeReqVO) {
|
||||
customerService.receiveCustomer(distributeReqVO.getIds(), distributeReqVO.getOwnerUserId(), Boolean.FALSE);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
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;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
|
||||
|
||||
/**
|
||||
* 客户 Excel 导入 VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
|
||||
public class CrmCustomerImportExcelVO {
|
||||
|
||||
@ExcelProperty("客户名称")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("手机")
|
||||
private String mobile;
|
||||
|
||||
@ExcelProperty("电话")
|
||||
private String telephone;
|
||||
|
||||
@ExcelProperty("QQ")
|
||||
private String qq;
|
||||
|
||||
@ExcelProperty("微信")
|
||||
private String wechat;
|
||||
|
||||
@ExcelProperty("邮箱")
|
||||
private String email;
|
||||
|
||||
@ExcelProperty(value = "地区", converter = AreaConvert.class)
|
||||
@ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME)
|
||||
private Integer areaId;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
private String detailAddress;
|
||||
|
||||
@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("备注")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
|
||||
|
||||
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.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 客户转移 Request VO")
|
||||
@Data
|
||||
public class CrmCustomerTransferReqVO {
|
||||
|
||||
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
@NotNull(message = "客户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 新负责人的用户编号
|
||||
*/
|
||||
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
@NotNull(message = "新负责人的用户编号不能为空")
|
||||
private Long newOwnerUserId;
|
||||
|
||||
/**
|
||||
* 老负责人加入团队后的权限级别。如果 null 说明移除
|
||||
*
|
||||
* 关联 {@link CrmPermissionLevelEnum}
|
||||
*/
|
||||
@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,100 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.followup;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
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.business.vo.business.CrmBusinessRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
|
||||
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.followup.CrmFollowUpRecordDO;
|
||||
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.followup.CrmFollowUpRecordService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
|
||||
@Tag(name = "管理后台 - 跟进记录")
|
||||
@RestController
|
||||
@RequestMapping("/crm/follow-up-record")
|
||||
@Validated
|
||||
public class CrmFollowUpRecordController {
|
||||
|
||||
@Resource
|
||||
private CrmFollowUpRecordService followUpRecordService;
|
||||
@Resource
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建跟进记录")
|
||||
public CommonResult<Long> createFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO createReqVO) {
|
||||
return success(followUpRecordService.createFollowUpRecord(createReqVO));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除跟进记录")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public CommonResult<Boolean> deleteFollowUpRecord(@RequestParam("id") Long id) {
|
||||
followUpRecordService.deleteFollowUpRecord(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得跟进记录")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long id) {
|
||||
CrmFollowUpRecordDO followUpRecord = followUpRecordService.getFollowUpRecord(id);
|
||||
return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得跟进记录分页")
|
||||
public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
|
||||
PageResult<CrmFollowUpRecordDO> pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO);
|
||||
// 1.1 查询联系人和商机
|
||||
Map<Long, CrmContactDO> contactMap = contactService.getContactMap(
|
||||
convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream()));
|
||||
Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(
|
||||
convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream()));
|
||||
// 1.2 查询用户
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(pageResult.getList(), item -> Long.valueOf(item.getCreator())));
|
||||
// 2. 拼接数据
|
||||
PageResult<CrmFollowUpRecordRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class, record -> {
|
||||
// 2.1 设置联系人和商机信息
|
||||
record.setBusinesses(new ArrayList<>()).setContacts(new ArrayList<>());
|
||||
record.getContactIds().forEach(id -> MapUtils.findAndThen(contactMap, id, contact ->
|
||||
record.getContacts().add(new CrmBusinessRespVO().setId(contact.getId()).setName(contact.getName()))));
|
||||
record.getBusinessIds().forEach(id -> MapUtils.findAndThen(businessMap, id, business ->
|
||||
record.getBusinesses().add(new CrmBusinessRespVO().setId(business.getId()).setName(business.getName()))));
|
||||
// 2.2 设置用户信息
|
||||
MapUtils.findAndThen(userMap, Long.valueOf(record.getCreator()), user -> record.setCreatorName(user.getNickname()));
|
||||
});
|
||||
return success(voPageResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.operatelog;
|
||||
|
||||
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.operatelog.vo.CrmOperateLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogRespVO;
|
||||
import cn.iocoder.yudao.module.crm.enums.LogRecordConstants;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
|
||||
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO;
|
||||
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.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.module.crm.enums.LogRecordConstants.*;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 操作日志")
|
||||
@RestController
|
||||
@RequestMapping("/crm/operate-log")
|
||||
@Validated
|
||||
public class CrmOperateLogController {
|
||||
|
||||
@Resource
|
||||
private OperateLogApi operateLogApi;
|
||||
|
||||
/**
|
||||
* {@link CrmBizTypeEnum} 与 {@link LogRecordConstants} 的映射关系
|
||||
*/
|
||||
private static final Map<Integer, String> BIZ_TYPE_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CLUE.getType(), CRM_CLUE_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CUSTOMER.getType(), CRM_CUSTOMER_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTACT.getType(), CRM_CONTACT_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_BUSINESS.getType(), CRM_BUSINESS_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTRACT.getType(), CRM_CONTRACT_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_PRODUCT.getType(), CRM_PRODUCT_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), CRM_RECEIVABLE_TYPE);
|
||||
BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得操作日志")
|
||||
public CommonResult<PageResult<CrmOperateLogRespVO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {
|
||||
OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO();
|
||||
reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释
|
||||
reqDTO.setType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId());
|
||||
return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
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.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.permission.CrmPermissionDO;
|
||||
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.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
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.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 数据权限")
|
||||
@RestController
|
||||
@RequestMapping("/crm/permission")
|
||||
@Validated
|
||||
public class CrmPermissionController {
|
||||
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private PostApi postApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据权限")
|
||||
public CommonResult<Boolean> create(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {
|
||||
permissionService.createPermission(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "编辑数据权限")
|
||||
@CrmPermission(bizTypeValue = "#updateReqVO.bizType", bizId = "#updateReqVO.bizId"
|
||||
, level = CrmPermissionLevelEnum.OWNER)
|
||||
public CommonResult<Boolean> updatePermission(@Valid @RequestBody CrmPermissionUpdateReqVO updateReqVO) {
|
||||
permissionService.updatePermission(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除数据权限")
|
||||
@Parameter(name = "ids", description = "数据权限编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deletePermission(@RequestParam("ids") Collection<Long> ids) {
|
||||
permissionService.deletePermissionBatch(ids, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-self")
|
||||
@Operation(summary = "删除自己的数据权限")
|
||||
@Parameter(name = "id", description = "数据权限编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteSelfPermission(@RequestParam("id") Long id) {
|
||||
permissionService.deleteSelfPermission(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得数据权限列表")
|
||||
@Parameters({
|
||||
@Parameter(name = "bizType", description = "CRM 类型", required = true, example = "2"),
|
||||
@Parameter(name = "bizId", description = "CRM 类型数据编号", required = true, example = "1024")
|
||||
})
|
||||
public CommonResult<List<CrmPermissionRespVO>> getPermissionList(@RequestParam("bizType") Integer bizType,
|
||||
@RequestParam("bizId") Long bizId) {
|
||||
List<CrmPermissionDO> permissions = permissionService.getPermissionListByBiz(bizType, bizId);
|
||||
if (CollUtil.isEmpty(permissions)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// 查询相关数据
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(permissions, CrmPermissionDO::getUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
Map<Long, PostRespDTO> postMap = postApi.getPostMap(
|
||||
convertSetByFlatMap(userMap.values(), AdminUserRespDTO::getPostIds,
|
||||
item -> item != null ? item.stream() : Stream.empty()));
|
||||
// 拼接数据
|
||||
return success(CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> {
|
||||
findAndThen(userMap, item.getUserId(), user -> {
|
||||
item.setNickname(user.getNickname());
|
||||
findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName()));
|
||||
if (CollUtil.isEmpty(user.getPostIds())) {
|
||||
item.setPostNames(Collections.emptySet());
|
||||
return;
|
||||
}
|
||||
List<PostRespDTO> postList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds());
|
||||
item.setPostNames(CollectionUtils.convertSet(postList, PostRespDTO::getName));
|
||||
});
|
||||
return item;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
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;
|
||||
import java.util.Set;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 数据权限 Response VO")
|
||||
@Data
|
||||
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;
|
||||
|
||||
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "岗位名称数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[BOOS,经理]")
|
||||
private Set<String> postNames;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-01-01 00:00:00")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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 lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 数据权限创建/更新 Request VO")
|
||||
@Data
|
||||
public class CrmPermissionSaveReqVO {
|
||||
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。
|
||||
* 选中时,同时添加对应的权限
|
||||
*/
|
||||
@Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
private List<Integer> toBizTypes;
|
||||
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.translate.core.TranslateUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;
|
||||
import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 产品")
|
||||
@RestController
|
||||
@RequestMapping("/crm/product")
|
||||
@Validated
|
||||
public class CrmProductController {
|
||||
|
||||
@Resource
|
||||
private CrmProductService productService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建产品")
|
||||
@PreAuthorize("@ss.hasPermission('crm:product:create')")
|
||||
public CommonResult<Long> createProduct(@Valid @RequestBody CrmProductSaveReqVO createReqVO) {
|
||||
return success(productService.createProduct(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新产品")
|
||||
@PreAuthorize("@ss.hasPermission('crm:product:update')")
|
||||
public CommonResult<Boolean> updateProduct(@Valid @RequestBody CrmProductSaveReqVO updateReqVO) {
|
||||
productService.updateProduct(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除产品")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:product:delete')")
|
||||
public CommonResult<Boolean> deleteProduct(@RequestParam("id") Long id) {
|
||||
productService.deleteProduct(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得产品")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:product:query')")
|
||||
public CommonResult<CrmProductRespVO> getProduct(@RequestParam("id") Long id) {
|
||||
CrmProductDO product = productService.getProduct(id);
|
||||
return success(BeanUtils.toBean(product, CrmProductRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得产品精简列表", description = "只包含被开启的产品,主要用于前端的下拉选项")
|
||||
public CommonResult<List<CrmProductRespVO>> getProductSimpleList() {
|
||||
List<CrmProductDO> list = productService.getProductListByStatus(CrmProductStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(list, product -> new CrmProductRespVO().setId(product.getId()).setName(product.getName())
|
||||
.setUnit(product.getUnit()).setNo(product.getNo()).setPrice(product.getPrice())));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得产品分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:product:query')")
|
||||
public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) {
|
||||
PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, CrmProductRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出产品 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:product:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<CrmProductDO> list = productService.getProductPage(exportReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "产品.xls", "数据", CrmProductRespVO.class,
|
||||
TranslateUtils.translate(BeanUtils.toBean(list, CrmProductRespVO.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.receivable;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
|
||||
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.receivable.CrmReceivableDO;
|
||||
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
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.convertListByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 回款")
|
||||
@RestController
|
||||
@RequestMapping("/crm/receivable")
|
||||
@Validated
|
||||
public class CrmReceivableController {
|
||||
|
||||
@Resource
|
||||
private CrmReceivableService receivableService;
|
||||
@Resource
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建回款")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:create')")
|
||||
public CommonResult<Long> createReceivable(@Valid @RequestBody CrmReceivableSaveReqVO createReqVO) {
|
||||
return success(receivableService.createReceivable(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新回款")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:update')")
|
||||
public CommonResult<Boolean> updateReceivable(@Valid @RequestBody CrmReceivableSaveReqVO updateReqVO) {
|
||||
receivableService.updateReceivable(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除回款")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:delete')")
|
||||
public CommonResult<Boolean> deleteReceivable(@RequestParam("id") Long id) {
|
||||
receivableService.deleteReceivable(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得回款")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:query')")
|
||||
public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam("id") Long id) {
|
||||
CrmReceivableDO receivable = receivableService.getReceivable(id);
|
||||
return success(buildReceivableDetail(receivable));
|
||||
}
|
||||
|
||||
private CrmReceivableRespVO buildReceivableDetail(CrmReceivableDO receivable) {
|
||||
if (receivable == null) {
|
||||
return null;
|
||||
}
|
||||
return buildReceivableDetailList(Collections.singletonList(receivable)).get(0);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得回款分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:query')")
|
||||
public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) {
|
||||
PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-customer")
|
||||
@Operation(summary = "获得回款分页,基于指定客户")
|
||||
public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) {
|
||||
Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
|
||||
PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO);
|
||||
return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出回款 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PAGE_SIZE_NONE);
|
||||
List<CrmReceivableDO> list = receivableService.getReceivablePage(exportReqVO, getLoginUserId()).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class,
|
||||
buildReceivableDetailList(list));
|
||||
}
|
||||
|
||||
private List<CrmReceivableRespVO> buildReceivableDetailList(List<CrmReceivableDO> receivableList) {
|
||||
if (CollUtil.isEmpty(receivableList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户列表
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(receivableList, CrmReceivableDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 1.3 获得合同列表
|
||||
Map<Long, CrmContractDO> contractMap = contractService.getContractMap(
|
||||
convertSet(receivableList, CrmReceivableDO::getContractId));
|
||||
// 2. 拼接结果
|
||||
return BeanUtils.toBean(receivableList, CrmReceivableRespVO.class, (receivableVO) -> {
|
||||
// 2.1 拼接客户名称
|
||||
findAndThen(customerMap, receivableVO.getCustomerId(), customer -> receivableVO.setCustomerName(customer.getName()));
|
||||
// 2.2 拼接负责人、创建人名称
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(receivableVO.getCreator()),
|
||||
user -> receivableVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, receivableVO.getOwnerUserId(), user -> {
|
||||
receivableVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> receivableVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
// 2.3 拼接合同信息
|
||||
findAndThen(contractMap, receivableVO.getContractId(), contract ->
|
||||
receivableVO.setContract(BeanUtils.toBean(contract, CrmContractRespVO.class)));
|
||||
});
|
||||
}
|
||||
|
||||
@PutMapping("/submit")
|
||||
@Operation(summary = "提交回款审批")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:update')")
|
||||
public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
|
||||
receivableService.submitReceivable(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/audit-count")
|
||||
@Operation(summary = "获得待审核回款数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable:query')")
|
||||
public CommonResult<Long> getAuditReceivableCount() {
|
||||
return success(receivableService.getAuditReceivableCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.receivable;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
|
||||
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.receivable.CrmReceivableDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
|
||||
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivablePlanService;
|
||||
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
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.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 回款计划")
|
||||
@RestController
|
||||
@RequestMapping("/crm/receivable-plan")
|
||||
@Validated
|
||||
public class CrmReceivablePlanController {
|
||||
|
||||
@Resource
|
||||
private CrmReceivablePlanService receivablePlanService;
|
||||
@Resource
|
||||
private CrmReceivableService receivableService;
|
||||
@Resource
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建回款计划")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')")
|
||||
public CommonResult<Long> createReceivablePlan(@Valid @RequestBody CrmReceivablePlanSaveReqVO createReqVO) {
|
||||
return success(receivablePlanService.createReceivablePlan(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新回款计划")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')")
|
||||
public CommonResult<Boolean> updateReceivablePlan(@Valid @RequestBody CrmReceivablePlanSaveReqVO updateReqVO) {
|
||||
receivablePlanService.updateReceivablePlan(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除回款计划")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:delete')")
|
||||
public CommonResult<Boolean> deleteReceivablePlan(@RequestParam("id") Long id) {
|
||||
receivablePlanService.deleteReceivablePlan(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得回款计划")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
|
||||
public CommonResult<CrmReceivablePlanRespVO> getReceivablePlan(@RequestParam("id") Long id) {
|
||||
CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id);
|
||||
return success(buildReceivablePlanDetail(receivablePlan));
|
||||
}
|
||||
|
||||
private CrmReceivablePlanRespVO buildReceivablePlanDetail(CrmReceivablePlanDO receivablePlan) {
|
||||
if (receivablePlan == null) {
|
||||
return null;
|
||||
}
|
||||
return buildReceivableDetailList(Collections.singletonList(receivablePlan)).get(0);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得回款计划分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
|
||||
public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
|
||||
PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/page-by-customer")
|
||||
@Operation(summary = "获得回款计划分页,基于指定客户")
|
||||
public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
|
||||
Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
|
||||
PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
|
||||
return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出回款计划 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PAGE_SIZE_NONE);
|
||||
List<CrmReceivablePlanDO> list = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class,
|
||||
buildReceivableDetailList(list));
|
||||
}
|
||||
|
||||
private List<CrmReceivablePlanRespVO> buildReceivableDetailList(List<CrmReceivablePlanDO> receivablePlanList) {
|
||||
if (CollUtil.isEmpty(receivablePlanList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户 Map
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
// 1.3 获得合同 Map
|
||||
Map<Long, CrmContractDO> contractMap = contractService.getContractMap(
|
||||
convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId));
|
||||
// 1.4 获得回款 Map
|
||||
Map<Long, CrmReceivableDO> receivableMap = receivableService.getReceivableMap(
|
||||
convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId));
|
||||
// 2. 拼接数据
|
||||
return BeanUtils.toBean(receivablePlanList, CrmReceivablePlanRespVO.class, (receivablePlanVO) -> {
|
||||
// 2.1 拼接客户信息
|
||||
findAndThen(customerMap, receivablePlanVO.getCustomerId(), customer -> receivablePlanVO.setCustomerName(customer.getName()));
|
||||
// 2.2 拼接用户信息
|
||||
findAndThen(userMap, receivablePlanVO.getOwnerUserId(), user -> receivablePlanVO.setOwnerUserName(user.getNickname()));
|
||||
findAndThen(userMap, Long.parseLong(receivablePlanVO.getCreator()), user -> receivablePlanVO.setCreatorName(user.getNickname()));
|
||||
// 2.3 拼接合同信息
|
||||
findAndThen(contractMap, receivablePlanVO.getContractId(), contract -> receivablePlanVO.setContractNo(contract.getNo()));
|
||||
// 2.4 拼接回款信息
|
||||
receivablePlanVO.setReceivable(BeanUtils.toBean(receivableMap.get(receivablePlanVO.getReceivableId()), CrmReceivableRespVO.class));
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得回款计划精简列表", description = "获得回款计划精简列表,主要用于前端的下拉选项")
|
||||
@Parameters({
|
||||
@Parameter(name = "customerId", description = "客户编号", required = true),
|
||||
@Parameter(name = "contractId", description = "合同编号", required = true)
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
|
||||
public CommonResult<List<CrmReceivablePlanRespVO>> getReceivablePlanSimpleList(@RequestParam("customerId") Long customerId,
|
||||
@RequestParam("contractId") Long contractId) {
|
||||
CrmReceivablePlanPageReqVO pageReqVO = new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId);
|
||||
pageReqVO.setPageNo(PAGE_SIZE_NONE);
|
||||
PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
|
||||
return success(convertList(pageResult.getList(), receivablePlan -> new CrmReceivablePlanRespVO() // 只返回 id、period 等信息
|
||||
.setId(receivablePlan.getId()).setPeriod(receivablePlan.getPeriod()).setReceivableId(receivablePlan.getReceivableId())
|
||||
.setPrice(receivablePlan.getPrice()).setReturnType(receivablePlan.getReturnType())));
|
||||
}
|
||||
|
||||
@GetMapping("/remind-count")
|
||||
@Operation(summary = "获得待回款提醒数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
|
||||
public CommonResult<Long> getReceivablePlanRemindCount() {
|
||||
return success(receivablePlanService.getReceivablePlanRemindCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@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)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(description = "创建人", example = "1024")
|
||||
@ExcelProperty("创建人")
|
||||
private String creator;
|
||||
@Schema(description = "创建人名字", example = "芋道源码")
|
||||
@ExcelProperty("创建人名字")
|
||||
private String creatorName;
|
||||
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@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")
|
||||
@ExcelProperty("负责人的用户编号")
|
||||
private Long ownerUserId;
|
||||
@Schema(description = "负责人名字", example = "25682")
|
||||
@ExcelProperty("负责人名字")
|
||||
private String ownerUserName;
|
||||
@Schema(description = "负责人部门")
|
||||
@ExcelProperty("负责人部门")
|
||||
private String ownerUserDeptName;
|
||||
|
||||
@Schema(description = "工作流编号", example = "1043")
|
||||
@ExcelProperty("工作流编号")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@ExcelProperty("审批状态")
|
||||
private Integer auditStatus;
|
||||
|
||||
@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;
|
||||
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
# == 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}}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
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,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析;
|
||||
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
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,86 +0,0 @@
|
|||
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.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;
|
||||
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-rank")
|
||||
@Validated
|
||||
public class CrmStatisticsRankController {
|
||||
|
||||
@Resource
|
||||
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(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(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(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(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(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(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(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(rankService.getFollowCustomerCountRank(rankingReqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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,35 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank;
|
||||
|
||||
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 排行榜统计 Request VO")
|
||||
@Data
|
||||
public class CrmStatisticsRankReqVO {
|
||||
|
||||
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "部门 id 不能为空")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
|
||||
* <p>
|
||||
* 后续,可能会支持选择部分用户进行查询
|
||||
*/
|
||||
@Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2")
|
||||
private List<Long> userIds;
|
||||
|
||||
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@NotEmpty(message = "时间范围不能为空")
|
||||
private LocalDateTime[] times;
|
||||
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
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 排行榜统计 Response VO")
|
||||
@Data
|
||||
public class CrmStatisticsRankRespVO {
|
||||
|
||||
@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. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行
|
||||
*
|
||||
* 为什么使用 BigDecimal 的原因:
|
||||
*/
|
||||
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private BigDecimal count;
|
||||
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.dal.mysql.business;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
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
|
||||
*
|
||||
* @author ljlleo
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
||||
|
||||
default int updateOwnerUserIdById(Long id, Long ownerUserId) {
|
||||
return update(new LambdaUpdateWrapper<CrmBusinessDO>()
|
||||
.eq(CrmBusinessDO::getId, id)
|
||||
.set(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
||||
}
|
||||
|
||||
default PageResult<CrmBusinessDO> selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
|
||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||
.eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号
|
||||
.likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
|
||||
.orderByDesc(CrmBusinessDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmBusinessDO> selectPageByContactId(CrmBusinessPageReqVO pageReqVO, Collection<Long> businessIds) {
|
||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||
.in(CrmBusinessDO::getId, businessIds) // 指定商机编号
|
||||
.likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
|
||||
.orderByDesc(CrmBusinessDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
|
||||
MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
|
||||
CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmBusinessDO.class)
|
||||
.likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
|
||||
.orderByDesc(CrmBusinessDO::getId);
|
||||
return selectJoinPage(pageReqVO, CrmBusinessDO.class, query);
|
||||
}
|
||||
|
||||
default Long selectCountByStatusTypeId(Long statusTypeId) {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.dal.mysql.contact;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
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.contact.vo.CrmContactPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
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;
|
||||
|
||||
/**
|
||||
* CRM 联系人 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmContactMapper extends BaseMapperX<CrmContactDO> {
|
||||
|
||||
default int updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {
|
||||
return update(new LambdaUpdateWrapper<CrmContactDO>()
|
||||
.eq(CrmContactDO::getCustomerId, customerId)
|
||||
.set(CrmContactDO::getOwnerUserId, ownerUserId));
|
||||
}
|
||||
|
||||
default PageResult<CrmContactDO> selectPageByCustomerId(CrmContactPageReqVO pageVO) {
|
||||
return selectPage(pageVO, new LambdaQueryWrapperX<CrmContactDO>()
|
||||
.eq(CrmContactDO::getCustomerId, pageVO.getCustomerId()) // 指定客户编号
|
||||
.likeIfPresent(CrmContactDO::getName, pageVO.getName())
|
||||
.eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile())
|
||||
.eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone())
|
||||
.eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail())
|
||||
.eqIfPresent(CrmContactDO::getQq, pageVO.getQq())
|
||||
.eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat())
|
||||
.orderByDesc(CrmContactDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmContactDO> selectPageByBusinessId(CrmContactPageReqVO pageVO, Collection<Long> ids) {
|
||||
return selectPage(pageVO, new LambdaQueryWrapperX<CrmContactDO>()
|
||||
.in(CrmContactDO::getId, ids) // 指定联系人编号
|
||||
.likeIfPresent(CrmContactDO::getName, pageVO.getName())
|
||||
.eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile())
|
||||
.eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone())
|
||||
.eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail())
|
||||
.eqIfPresent(CrmContactDO::getQq, pageVO.getQq())
|
||||
.eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat())
|
||||
.orderByDesc(CrmContactDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) {
|
||||
MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),
|
||||
CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmContactDO.class)
|
||||
.likeIfPresent(CrmContactDO::getName, pageReqVO.getName())
|
||||
.eqIfPresent(CrmContactDO::getMobile, pageReqVO.getMobile())
|
||||
.eqIfPresent(CrmContactDO::getTelephone, pageReqVO.getTelephone())
|
||||
.eqIfPresent(CrmContactDO::getEmail, pageReqVO.getEmail())
|
||||
.eqIfPresent(CrmContactDO::getQq, pageReqVO.getQq())
|
||||
.eqIfPresent(CrmContactDO::getWechat, pageReqVO.getWechat())
|
||||
.orderByDesc(CrmContactDO::getId);
|
||||
return selectJoinPage(pageReqVO, CrmContactDO.class, query);
|
||||
}
|
||||
|
||||
default List<CrmContactDO> selectListByCustomerId(Long customerId) {
|
||||
return selectList(CrmContactDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
default List<CrmContactDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return selectList(CrmContactDO::getCustomerId, customerId,
|
||||
CrmContactDO::getOwnerUserId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.dal.mysql.contract;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
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.contract.vo.contract.CrmContractPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 合同 Mapper
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
|
||||
|
||||
default CrmContractDO selectByNo(String no) {
|
||||
return selectOne(CrmContractDO::getNo, no);
|
||||
}
|
||||
|
||||
default PageResult<CrmContractDO> selectPageByCustomerId(CrmContractPageReqVO pageReqVO) {
|
||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmContractDO>()
|
||||
.eq(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
|
||||
.likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
|
||||
.likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
|
||||
.eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
|
||||
.eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
|
||||
.orderByDesc(CrmContractDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmContractDO> selectPageByBusinessId(CrmContractPageReqVO pageReqVO) {
|
||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmContractDO>()
|
||||
.eq(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
|
||||
.likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
|
||||
.likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
|
||||
.eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
|
||||
.eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
|
||||
.orderByDesc(CrmContractDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId, CrmContractConfigDO config) {
|
||||
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
|
||||
CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmContractDO.class)
|
||||
.likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
|
||||
.likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
|
||||
.eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
|
||||
.eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
|
||||
.eqIfPresent(CrmContractDO::getAuditStatus, pageReqVO.getAuditStatus())
|
||||
.orderByDesc(CrmContractDO::getId);
|
||||
|
||||
// Backlog: 即将到期的合同
|
||||
LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
|
||||
LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
|
||||
if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) { // 即将到期
|
||||
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())
|
||||
.between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(config.getNotifyDays()));
|
||||
} else if (CrmContractPageReqVO.EXPIRY_TYPE_EXPIRED.equals(pageReqVO.getExpiryType())) { // 已到期
|
||||
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())
|
||||
.lt(CrmContractDO::getEndTime, endOfToday);
|
||||
}
|
||||
return selectJoinPage(pageReqVO, CrmContractDO.class, query);
|
||||
}
|
||||
|
||||
default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) {
|
||||
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
|
||||
// 构建数据权限连表条件
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), ids, userId);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId);
|
||||
return selectJoinList(CrmContractDO.class, query);
|
||||
}
|
||||
|
||||
default Long selectCountByContactId(Long contactId) {
|
||||
return selectCount(CrmContractDO::getSignContactId, contactId);
|
||||
}
|
||||
|
||||
default Long selectCountByBusinessId(Long businessId) {
|
||||
return selectCount(CrmContractDO::getBusinessId, businessId);
|
||||
}
|
||||
|
||||
default Long selectCountByAudit(Long userId) {
|
||||
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
|
||||
// 我负责的 + 非公海
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
|
||||
CrmContractDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
|
||||
// 未审核
|
||||
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());
|
||||
return selectCount(query);
|
||||
}
|
||||
|
||||
default Long selectCountByRemind(Long userId, CrmContractConfigDO config) {
|
||||
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
|
||||
// 我负责的 + 非公海
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
|
||||
CrmContractDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
|
||||
// 即将到期
|
||||
LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
|
||||
LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
|
||||
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) // 必须审批通过!
|
||||
.between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(config.getNotifyDays()));
|
||||
return selectCount(query);
|
||||
}
|
||||
|
||||
default List<CrmContractDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmContractDO>()
|
||||
.eq(CrmContractDO::getCustomerId, customerId)
|
||||
.eq(CrmContractDO::getOwnerUserId, ownerUserId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.dal.mysql.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* crm 数据权限 mapper
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmPermissionMapper extends BaseMapperX<CrmPermissionDO> {
|
||||
|
||||
default CrmPermissionDO selectByBizTypeAndBizIdByUserId(Integer bizType, Long bizId, Long userId) {
|
||||
return selectOne(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.eq(CrmPermissionDO::getBizId, bizId)
|
||||
.eq(CrmPermissionDO::getUserId, userId));
|
||||
}
|
||||
|
||||
default List<CrmPermissionDO> selectByBizTypeAndBizId(Integer bizType, Long bizId) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.eq(CrmPermissionDO::getBizId, bizId));
|
||||
}
|
||||
|
||||
default List<CrmPermissionDO> selectByBizTypeAndBizIds(Integer bizType, Collection<Long> bizIds) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.in(CrmPermissionDO::getBizId, bizIds));
|
||||
}
|
||||
|
||||
default List<CrmPermissionDO> selectListByBizTypeAndUserId(Integer bizType, Long userId) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.eq(CrmPermissionDO::getUserId, userId));
|
||||
}
|
||||
|
||||
default List<CrmPermissionDO> selectListByBizTypeAndBizIdAndLevel(Integer bizType, Long bizId, Integer level) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.eq(CrmPermissionDO::getBizId, bizId)
|
||||
.eq(CrmPermissionDO::getLevel, level));
|
||||
}
|
||||
|
||||
default CrmPermissionDO selectByIdAndUserId(Long id, Long userId) {
|
||||
return selectOne(CrmPermissionDO::getId, id,
|
||||
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) {
|
||||
return delete(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.eq(CrmPermissionDO::getBizType, bizType)
|
||||
.eq(CrmPermissionDO::getBizId, bizId));
|
||||
}
|
||||
|
||||
default Long selectListByBiz(Collection<Integer> bizTypes, Collection<Long> bizIds, Collection<Long> userIds) {
|
||||
return selectCount(new LambdaQueryWrapperX<CrmPermissionDO>()
|
||||
.in(CrmPermissionDO::getBizType, bizTypes)
|
||||
.in(CrmPermissionDO::getBizId, bizIds)
|
||||
.in(CrmPermissionDO::getUserId, userIds));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.dal.mysql.receivable;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
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.receivable.vo.receivable.CrmReceivablePageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 回款 Mapper
|
||||
*
|
||||
* @author 赤焰
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
|
||||
|
||||
default CrmReceivableDO selectByNo(String no) {
|
||||
return selectOne(CrmReceivableDO::getNo, no);
|
||||
}
|
||||
|
||||
default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()
|
||||
.eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递
|
||||
.eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(CrmReceivableDO::getContractId, reqVO.getContractId())
|
||||
.eqIfPresent(CrmReceivableDO::getPlanId, reqVO.getPlanId())
|
||||
.orderByDesc(CrmReceivableDO::getId));
|
||||
}
|
||||
|
||||
default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) {
|
||||
MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
|
||||
CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmReceivableDO.class)
|
||||
.eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo())
|
||||
.eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId())
|
||||
.eqIfPresent(CrmReceivableDO::getContractId, pageReqVO.getContractId())
|
||||
.eqIfPresent(CrmReceivableDO::getAuditStatus, pageReqVO.getAuditStatus())
|
||||
.orderByDesc(CrmReceivableDO::getId);
|
||||
return selectJoinPage(pageReqVO, CrmReceivableDO.class, query);
|
||||
}
|
||||
|
||||
default List<CrmReceivableDO> selectBatchIds(Collection<Long> ids, Long userId) {
|
||||
MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), ids, userId);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmReceivableDO.class).in(CrmReceivableDO::getId, ids).orderByDesc(CrmReceivableDO::getId);
|
||||
return selectJoinList(CrmReceivableDO.class, query);
|
||||
}
|
||||
|
||||
default Long selectCountByAudit(Long userId) {
|
||||
MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
|
||||
// 我负责的 + 非公海
|
||||
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
|
||||
CrmReceivableDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
|
||||
// 未审核
|
||||
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());
|
||||
return selectCount(query);
|
||||
}
|
||||
|
||||
default List<CrmReceivableDO> selectListByContractIdAndStatus(Long contractId, Collection<Integer> auditStatuses) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmReceivableDO>()
|
||||
.eq(CrmReceivableDO::getContractId, contractId)
|
||||
.in(CrmReceivableDO::getAuditStatus, auditStatuses));
|
||||
}
|
||||
|
||||
default Map<Long, BigDecimal> selectReceivablePriceMapByContractId(Collection<Long> contractIds) {
|
||||
if (CollUtil.isEmpty(contractIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>()
|
||||
.select("contract_id, SUM(price) AS total_price")
|
||||
.in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过
|
||||
CrmAuditStatusEnum.PROCESS, CrmAuditStatusEnum.APPROVE.getStatus())
|
||||
.groupBy("contract_id")
|
||||
.in("contract_id", contractIds));
|
||||
// 获得金额
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
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);
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
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);
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
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,81 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* CRM 排行榜统计 Mapper
|
||||
*
|
||||
* @author anhaohao
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsRankMapper {
|
||||
|
||||
/**
|
||||
* 查询合同金额排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 合同金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询回款金额排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 回款金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询签约合同数量排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 签约合同数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询产品销量排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 产品销量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询新增客户数排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 新增客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询联系人数量排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 联系人数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询跟进次数排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 跟进次数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 查询跟进客户数排行榜
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @return 跟进客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* crm 模块的 excel 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.excel;
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* crm 模块的 operatelog 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.operatelog;
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* crm 模块的 permission 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.permission;
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* crm 模块的 web 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.web;
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.business;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 商机 Service 接口
|
||||
*
|
||||
* @author ljlleo
|
||||
*/
|
||||
public interface CrmBusinessService {
|
||||
|
||||
/**
|
||||
* 创建商机
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @param userId 用户编号
|
||||
* @return 编号
|
||||
*/
|
||||
Long createBusiness(@Valid CrmBusinessSaveReqVO createReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新商机
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateBusiness(@Valid CrmBusinessSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新商机相关跟进信息
|
||||
*
|
||||
* @param id 编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param contactLastContent 最后联系内容
|
||||
*/
|
||||
void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||
|
||||
/**
|
||||
* 更新商机的下次联系时间
|
||||
*
|
||||
* @param ids 编号数组
|
||||
* @param contactNextTime 下次联系时间
|
||||
*/
|
||||
void updateBusinessContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime);
|
||||
|
||||
/**
|
||||
* 更新商机的状态
|
||||
*
|
||||
* @param reqVO 更新请求
|
||||
*/
|
||||
void updateBusinessStatus(CrmBusinessUpdateStatusReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 删除商机
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteBusiness(Long id);
|
||||
|
||||
/**
|
||||
* 商机转移
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得商机
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 商机
|
||||
*/
|
||||
CrmBusinessDO getBusiness(Long id);
|
||||
|
||||
/**
|
||||
* 校验商机是否有效
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 商机
|
||||
*/
|
||||
CrmBusinessDO validateBusiness(Long id);
|
||||
|
||||
/**
|
||||
* 获得商机列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 商机列表
|
||||
*/
|
||||
List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得商机 Map
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 商机 Map
|
||||
*/
|
||||
default Map<Long, CrmBusinessDO> getBusinessMap(Collection<Long> ids) {
|
||||
return convertMap(getBusinessList(ids), CrmBusinessDO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定商机编号的产品列表
|
||||
*
|
||||
* @param businessId 商机编号
|
||||
* @return 商机产品列表
|
||||
*/
|
||||
List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);
|
||||
|
||||
/**
|
||||
* 获得商机分页
|
||||
*
|
||||
* 数据权限:基于 {@link CrmBusinessDO}
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @param userId 用户编号
|
||||
* @return 商机分页
|
||||
*/
|
||||
PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得商机分页,基于指定客户
|
||||
*
|
||||
* 数据权限:基于 {@link CrmCustomerDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 商机分页
|
||||
*/
|
||||
PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得商机分页,基于指定联系人
|
||||
*
|
||||
* 数据权限:基于 {@link CrmContactDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页参数
|
||||
* @return 商机分页
|
||||
*/
|
||||
PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获取关联客户的商机数量
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @return 数量
|
||||
*/
|
||||
Long getBusinessCountByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 获得使用指定商机状态组的商机数量
|
||||
*
|
||||
* @param statusTypeId 商机状态组编号
|
||||
* @return 数量
|
||||
*/
|
||||
Long getBusinessCountByStatusTypeId(Long statusTypeId);
|
||||
|
||||
/**
|
||||
* 获得商机状态名称
|
||||
*
|
||||
* @param endStatus 结束状态
|
||||
* @param status 商机状态
|
||||
* @return 商机状态名称
|
||||
*/
|
||||
default String getBusinessStatusName(Integer endStatus, CrmBusinessStatusDO status) {
|
||||
if (endStatus != null) {
|
||||
return CrmBusinessEndStatusEnum.fromStatus(endStatus).getName();
|
||||
}
|
||||
return status.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得商机列表
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 商机列表
|
||||
*/
|
||||
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||
|
||||
}
|
||||
|
|
@ -1,378 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.business;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
|
||||
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.contact.CrmContactBusinessService;
|
||||
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.customer.CrmCustomerService;
|
||||
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.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 org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
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.LogRecordConstants.*;
|
||||
|
||||
/**
|
||||
* 商机 Service 实现类
|
||||
*
|
||||
* @author ljlleo
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
|
||||
@Resource
|
||||
private CrmBusinessMapper businessMapper;
|
||||
@Resource
|
||||
private CrmBusinessProductMapper businessProductMapper;
|
||||
|
||||
@Resource
|
||||
private CrmBusinessStatusService businessStatusService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
@Resource
|
||||
private CrmContactBusinessService contactBusinessService;
|
||||
@Resource
|
||||
private CrmProductService productService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}",
|
||||
success = CRM_BUSINESS_CREATE_SUCCESS)
|
||||
public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
|
||||
// 1.1 校验产品项的有效性
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getBusinessProducts());
|
||||
// 1.2 校验关联字段
|
||||
validateRelationDataExists(createReqVO);
|
||||
|
||||
// 2.1 插入商机
|
||||
CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class);
|
||||
business.setStatusId(businessStatusService.getBusinessStatusListByTypeId(createReqVO.getStatusTypeId()).get(0).getId()); // 默认状态
|
||||
calculateTotalPrice(business, businessProducts);
|
||||
businessMapper.insert(business);
|
||||
// 2.2 插入商机关联商品
|
||||
if (CollUtil.isNotEmpty(businessProducts)) {
|
||||
businessProducts.forEach(item -> item.setBusinessId(business.getId()));
|
||||
businessProductMapper.insertBatch(businessProducts);
|
||||
}
|
||||
|
||||
// 3. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(business.getOwnerUserId())
|
||||
.setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()).setBizId(business.getId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
|
||||
// 4. 在联系人的详情页,如果直接【新建商机】,则需要关联下
|
||||
if (createReqVO.getContactId() != null) {
|
||||
contactBusinessService.createContactBusinessList(new CrmContactBusinessReqVO().setContactId(createReqVO.getContactId())
|
||||
.setBusinessIds(Collections.singletonList(business.getId())));
|
||||
}
|
||||
|
||||
// 5. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("business", business);
|
||||
return business.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_BUSINESS_UPDATE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateBusiness(CrmBusinessSaveReqVO updateReqVO) {
|
||||
updateReqVO.setOwnerUserId(null).setStatusTypeId(null); // 不允许更新的字段
|
||||
// 1.1 校验存在
|
||||
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
|
||||
// 1.2 校验产品项的有效性
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getBusinessProducts());
|
||||
// 1.3 校验关联字段
|
||||
validateRelationDataExists(updateReqVO);
|
||||
|
||||
// 2.1 更新商机
|
||||
CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
|
||||
calculateTotalPrice(updateObj, businessProducts);
|
||||
businessMapper.updateById(updateObj);
|
||||
// 2.2 更新商机关联商品
|
||||
updateBusinessProduct(updateObj.getId(), businessProducts);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class));
|
||||
LogRecordContext.putVariable("businessName", oldBusiness.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
|
||||
success = CRM_BUSINESS_FOLLOW_UP_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
|
||||
// 1. 校验存在
|
||||
CrmBusinessDO business = validateBusinessExists(id);
|
||||
|
||||
// 2. 更新联系人的跟进信息
|
||||
businessMapper.updateById(new CrmBusinessDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
|
||||
.setContactLastTime(LocalDateTime.now()));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("businessName", business.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#ids", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateBusinessContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime) {
|
||||
businessMapper.updateBatch(convertList(ids, id -> new CrmBusinessDO().setId(id).setContactNextTime(contactNextTime)));
|
||||
}
|
||||
|
||||
private void updateBusinessProduct(Long id, List<CrmBusinessProductDO> newList) {
|
||||
List<CrmBusinessProductDO> oldList = businessProductMapper.selectListByBusinessId(id);
|
||||
List<List<CrmBusinessProductDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录
|
||||
(oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
|
||||
if (CollUtil.isNotEmpty(diffList.get(0))) {
|
||||
diffList.get(0).forEach(o -> o.setBusinessId(id));
|
||||
businessProductMapper.insertBatch(diffList.get(0));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(diffList.get(1))) {
|
||||
businessProductMapper.updateBatch(diffList.get(1));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(diffList.get(2))) {
|
||||
businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRelationDataExists(CrmBusinessSaveReqVO saveReqVO) {
|
||||
// 校验商机状态
|
||||
if (saveReqVO.getStatusTypeId() != null) {
|
||||
businessStatusService.validateBusinessStatusType(saveReqVO.getStatusTypeId());
|
||||
}
|
||||
// 校验客户
|
||||
if (saveReqVO.getCustomerId() != null) {
|
||||
customerService.validateCustomer(saveReqVO.getCustomerId());
|
||||
}
|
||||
// 校验联系人
|
||||
if (saveReqVO.getContactId() != null) {
|
||||
contactService.validateContact(saveReqVO.getContactId());
|
||||
}
|
||||
// 校验负责人
|
||||
if (saveReqVO.getOwnerUserId() != null) {
|
||||
adminUserApi.validateUser(saveReqVO.getOwnerUserId());
|
||||
}
|
||||
}
|
||||
|
||||
private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list) {
|
||||
// 1. 校验产品存在
|
||||
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()))));
|
||||
}
|
||||
|
||||
private void calculateTotalPrice(CrmBusinessDO business, List<CrmBusinessProductDO> businessProducts) {
|
||||
business.setTotalProductPrice(getSumValue(businessProducts, CrmBusinessProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
|
||||
BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(business.getTotalProductPrice(), business.getDiscountPercent());
|
||||
business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE, bizNo = "{{#reqVO.id}}",
|
||||
success = CRM_BUSINESS_UPDATE_STATUS_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateBusinessStatus(CrmBusinessUpdateStatusReqVO reqVO) {
|
||||
// 1.1 校验存在
|
||||
CrmBusinessDO business = validateBusinessExists(reqVO.getId());
|
||||
// 1.2 校验商机未结束
|
||||
if (business.getEndStatus() != null) {
|
||||
throw exception(BUSINESS_UPDATE_STATUS_FAIL_END_STATUS);
|
||||
}
|
||||
// 1.3 校验商机状态
|
||||
CrmBusinessStatusDO status = null;
|
||||
if (reqVO.getStatusId() != null) {
|
||||
status = businessStatusService.validateBusinessStatus(business.getStatusTypeId(), reqVO.getStatusId());
|
||||
}
|
||||
// 1.4 校验是不是状态没变更
|
||||
if ((reqVO.getStatusId() != null && reqVO.getStatusId().equals(business.getStatusId()))
|
||||
|| (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) {
|
||||
throw exception(BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS);
|
||||
}
|
||||
|
||||
// 2. 更新商机状态
|
||||
businessMapper.updateById(new CrmBusinessDO().setId(reqVO.getId()).setStatusId(reqVO.getStatusId())
|
||||
.setEndStatus(reqVO.getEndStatus()));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("businessName", business.getName());
|
||||
LogRecordContext.putVariable("oldStatusName", getBusinessStatusName(business.getEndStatus(),
|
||||
businessStatusService.getBusinessStatus(business.getStatusId())));
|
||||
LogRecordContext.putVariable("newStatusName", getBusinessStatusName(reqVO.getEndStatus(), status));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_BUSINESS_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteBusiness(Long id) {
|
||||
// 1.1 校验存在
|
||||
CrmBusinessDO business = validateBusinessExists(id);
|
||||
// 1.2 校验是否关联合同
|
||||
validateContractExists(id);
|
||||
|
||||
// 删除商机
|
||||
businessMapper.deleteById(id);
|
||||
// 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);
|
||||
|
||||
// 记录操作日志上下文
|
||||
LogRecordContext.putVariable("businessName", business.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除校验合同是关联合同
|
||||
*
|
||||
* @param businessId 商机id
|
||||
* @author lzxhqs
|
||||
*/
|
||||
private void validateContractExists(Long businessId) {
|
||||
if (contractService.getContractCountByBusinessId(businessId) > 0) {
|
||||
throw exception(BUSINESS_DELETE_FAIL_CONTRACT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private CrmBusinessDO validateBusinessExists(Long id) {
|
||||
CrmBusinessDO crmBusiness = businessMapper.selectById(id);
|
||||
if (crmBusiness == null) {
|
||||
throw exception(BUSINESS_NOT_EXISTS);
|
||||
}
|
||||
return crmBusiness;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
|
||||
success = CRM_BUSINESS_TRANSFER_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) {
|
||||
// 1 校验商机是否存在
|
||||
CrmBusinessDO business = validateBusinessExists(reqVO.getId());
|
||||
|
||||
// 2.1 数据权限转移
|
||||
permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_BUSINESS.getType(),
|
||||
reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
|
||||
// 2.2 设置新的负责人
|
||||
businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
|
||||
|
||||
// 记录操作日志上下文
|
||||
LogRecordContext.putVariable("business", business);
|
||||
}
|
||||
|
||||
//======================= 查询相关 =======================
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmBusinessDO getBusiness(Long id) {
|
||||
return businessMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmBusinessDO validateBusiness(Long id) {
|
||||
return validateBusinessExists(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmBusinessDO> getBusinessList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return businessMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {
|
||||
return businessProductMapper.selectListByBusinessId(businessId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
|
||||
return businessMapper.selectPage(pageReqVO, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
|
||||
return businessMapper.selectPageByCustomerId(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#pageReqVO.contactId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO) {
|
||||
// 1. 查询关联的商机编号
|
||||
List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByContactId(
|
||||
pageReqVO.getContactId());
|
||||
if (CollUtil.isEmpty(contactBusinessList)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
// 2. 查询商机分页
|
||||
return businessMapper.selectPageByContactId(pageReqVO,
|
||||
convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getBusinessCountByCustomerId(Long customerId) {
|
||||
return businessMapper.selectCount(CrmBusinessDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getBusinessCountByStatusTypeId(Long statusTypeId) {
|
||||
return businessMapper.selectCountByStatusTypeId(statusTypeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.contact;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
|
||||
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.customer.CrmCustomerDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* CRM 联系人 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CrmContactService {
|
||||
|
||||
/**
|
||||
* 创建联系人
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @param userId 用户编号
|
||||
* @return 编号
|
||||
*/
|
||||
Long createContact(@Valid CrmContactSaveReqVO createReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新联系人
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateContact(@Valid CrmContactSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除联系人
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteContact(Long id);
|
||||
|
||||
/**
|
||||
* 联系人转移
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void transferContact(CrmContactTransferReqVO reqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新指定客户的联系人的负责人
|
||||
* 数据权限基于 【客户】
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 用户编号
|
||||
*/
|
||||
void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId);
|
||||
|
||||
/**
|
||||
* 更新联系人相关跟进信息
|
||||
*
|
||||
* @param id 编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param contactLastContent 最后联系内容
|
||||
*/
|
||||
void updateContactFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||
|
||||
/**
|
||||
* 更新联系人的下次联系时间
|
||||
*
|
||||
* @param ids 编号数组
|
||||
* @param contactNextTime 下次联系时间
|
||||
*/
|
||||
void updateContactContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime);
|
||||
|
||||
/**
|
||||
* 获得联系人
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 联系人
|
||||
*/
|
||||
CrmContactDO getContact(Long id);
|
||||
|
||||
/**
|
||||
* 校验联系人
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void validateContact(Long id);
|
||||
|
||||
/**
|
||||
* 获得联系人列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 联系人列表
|
||||
*/
|
||||
List<CrmContactDO> getContactList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得联系人 Map
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 联系人 Map
|
||||
*/
|
||||
default Map<Long, CrmContactDO> getContactMap(Collection<Long> ids) {
|
||||
return convertMap(getContactList(ids), CrmContactDO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取联系人列表(校验权限)
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 联系人列表
|
||||
*/
|
||||
List<CrmContactDO> getContactList(Long userId);
|
||||
|
||||
/**
|
||||
* 获得联系人分页
|
||||
*
|
||||
* 数据权限:基于 {@link CrmContactDO}
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @param userId 用户编号
|
||||
* @return 联系人分页
|
||||
*/
|
||||
PageResult<CrmContactDO> getContactPage(CrmContactPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得联系人分页
|
||||
*
|
||||
* 数据权限:基于 {@link CrmCustomerDO}
|
||||
*
|
||||
* @param pageVO 分页查询
|
||||
* @return 联系人分页
|
||||
*/
|
||||
PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO);
|
||||
|
||||
/**
|
||||
* 获得联系人分页
|
||||
*
|
||||
* 数据权限:基于 {@link CrmBusinessDO}
|
||||
*
|
||||
* @param pageVO 分页查询
|
||||
* @return 联系人分页
|
||||
*/
|
||||
PageResult<CrmContactDO> getContactPageByBusinessId(CrmContactPageReqVO pageVO);
|
||||
|
||||
/**
|
||||
* 获取关联客户的联系人数量
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @return 数量
|
||||
*/
|
||||
Long getContactCountByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 获得联系人列表
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 联系人列表
|
||||
*/
|
||||
List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||
|
||||
}
|
||||
|
|
@ -1,306 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.contact;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.contact.vo.CrmContactBusinessReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper;
|
||||
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.contract.CrmContractService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
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.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 org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* CRM 联系人 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmContactServiceImpl implements CrmContactService {
|
||||
|
||||
@Resource
|
||||
private CrmContactMapper contactMapper;
|
||||
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
@Resource
|
||||
@Lazy
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private CrmContactBusinessService contactBusinessService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_CREATE_SUB_TYPE, bizNo = "{{#contact.id}}",
|
||||
success = CRM_CONTACT_CREATE_SUCCESS)
|
||||
public Long createContact(CrmContactSaveReqVO createReqVO, Long userId) {
|
||||
createReqVO.setId(null);
|
||||
// 1. 校验关联数据
|
||||
validateRelationDataExists(createReqVO);
|
||||
|
||||
// 2. 插入联系人
|
||||
CrmContactDO contact = BeanUtils.toBean(createReqVO, CrmContactDO.class);
|
||||
contactMapper.insert(contact);
|
||||
|
||||
// 3. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
|
||||
.setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()).setBizId(contact.getId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
|
||||
// 4. 如果有关联商机,则需要创建关联
|
||||
if (createReqVO.getBusinessId() != null) {
|
||||
contactBusinessService.createContactBusinessList(new CrmContactBusinessReqVO()
|
||||
.setContactId(contact.getId()).setBusinessIds(singletonList(createReqVO.getBusinessId())));
|
||||
}
|
||||
|
||||
// 5. 记录操作日志
|
||||
LogRecordContext.putVariable("contact", contact);
|
||||
return contact.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_CONTACT_UPDATE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateContact(CrmContactSaveReqVO updateReqVO) {
|
||||
// 1.1 校验存在
|
||||
CrmContactDO oldContact = validateContactExists(updateReqVO.getId());
|
||||
// 1.2 校验关联数据
|
||||
validateRelationDataExists(updateReqVO);
|
||||
|
||||
// 2. 更新联系人
|
||||
CrmContactDO updateObj = BeanUtils.toBean(updateReqVO, CrmContactDO.class);
|
||||
contactMapper.updateById(updateObj);
|
||||
|
||||
// 3. 记录操作日志
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class));
|
||||
LogRecordContext.putVariable("contactName", oldContact.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验关联的数据都存在
|
||||
*
|
||||
* @param saveReqVO 新增/修改请求 VO
|
||||
*/
|
||||
private void validateRelationDataExists(CrmContactSaveReqVO saveReqVO) {
|
||||
// 1. 校验客户
|
||||
if (saveReqVO.getCustomerId() != null && customerService.getCustomer(saveReqVO.getCustomerId()) == null) {
|
||||
customerService.validateCustomer(saveReqVO.getCustomerId());
|
||||
}
|
||||
// 2. 校验负责人
|
||||
if (saveReqVO.getOwnerUserId() != null) {
|
||||
adminUserApi.validateUser(saveReqVO.getOwnerUserId());
|
||||
}
|
||||
// 3. 直属上级
|
||||
if (saveReqVO.getParentId() != null) {
|
||||
validateContactExists(saveReqVO.getParentId());
|
||||
}
|
||||
// 4. 如果有关联商机,则需要校验存在
|
||||
if (saveReqVO.getBusinessId() != null && businessService.getBusiness(saveReqVO.getBusinessId()) == null) {
|
||||
throw exception(BUSINESS_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_CONTACT_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteContact(Long id) {
|
||||
// 1.1 校验存在
|
||||
CrmContactDO contact = validateContactExists(id);
|
||||
// 1.2 校验是否关联合同
|
||||
if (contractService.getContractCountByContactId(id) > 0) {
|
||||
throw exception(CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 删除联系人
|
||||
contactMapper.deleteById(id);
|
||||
|
||||
// 4.1 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id);
|
||||
// 4.2 删除商机关联
|
||||
contactBusinessService.deleteContactBusinessByContactId(id);
|
||||
|
||||
// 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contactName", contact.getName());
|
||||
}
|
||||
|
||||
private CrmContactDO validateContactExists(Long id) {
|
||||
CrmContactDO contact = contactMapper.selectById(id);
|
||||
if (contact == null) {
|
||||
throw exception(CONTACT_NOT_EXISTS);
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
|
||||
success = CRM_CONTACT_TRANSFER_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
|
||||
// 1 校验联系人是否存在
|
||||
CrmContactDO contact = validateContactExists(reqVO.getId());
|
||||
|
||||
// 2.1 数据权限转移
|
||||
permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTACT.getType(),
|
||||
reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
|
||||
// 2.2 设置新的负责人
|
||||
contactMapper.updateById(new CrmContactDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));
|
||||
|
||||
// 3. 记录转移日志
|
||||
LogRecordContext.putVariable("contact", contact);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {
|
||||
// 1. 校验存在
|
||||
List<CrmContactDO> contacts = contactMapper.selectListByCustomerId(customerId);
|
||||
if (CollUtil.isEmpty(contacts)) {
|
||||
return;
|
||||
}
|
||||
int count = contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId);
|
||||
if (count == 0) {
|
||||
throw exception(CONTACT_UPDATE_OWNER_USER_FAIL);
|
||||
}
|
||||
|
||||
// 2. 记录操作日志
|
||||
for (CrmContactDO contact : contacts) {
|
||||
receiveContactLog(contact, ownerUserId);
|
||||
}
|
||||
}
|
||||
|
||||
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_OWNER_USER_SUB_TYPE, bizNo = "{{#contact.id}",
|
||||
success = CRM_CONTACT_UPDATE_OWNER_USER_SUCCESS)
|
||||
public void receiveContactLog(CrmContactDO contact, Long ownerUserId) {
|
||||
// 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contact", contact);
|
||||
LogRecordContext.putVariable("ownerUserId", ownerUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
|
||||
success = CRM_CONTACT_FOLLOW_UP_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateContactFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
|
||||
// 1. 校验存在
|
||||
CrmContactDO contact = validateContactExists(id);
|
||||
|
||||
// 2. 更新联系人的跟进信息
|
||||
contactMapper.updateById(new CrmContactDO().setId(id).setContactNextTime(contactNextTime)
|
||||
.setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contactName", contact.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#ids", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateContactContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime) {
|
||||
contactMapper.updateBatch(convertList(ids, id -> new CrmContactDO().setId(id).setContactNextTime(contactNextTime)));
|
||||
}
|
||||
|
||||
//======================= 查询相关 =======================
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmContactDO getContact(Long id) {
|
||||
return contactMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateContact(Long id) {
|
||||
validateContactExists(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContactDO> getContactList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return contactMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContactDO> getContactList(Long userId) {
|
||||
CrmContactPageReqVO reqVO = new CrmContactPageReqVO();
|
||||
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
|
||||
return contactMapper.selectPage(reqVO, userId).getList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmContactDO> getContactPage(CrmContactPageReqVO pageReqVO, Long userId) {
|
||||
return contactMapper.selectPage(pageReqVO, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageVO.customerId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO) {
|
||||
return contactMapper.selectPageByCustomerId(pageVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#pageVO.businessId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmContactDO> getContactPageByBusinessId(CrmContactPageReqVO pageVO) {
|
||||
List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByBusinessId(pageVO.getBusinessId());
|
||||
if (CollUtil.isEmpty(contactBusinessList)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
return contactMapper.selectPageByBusinessId(pageVO, convertSet(contactBusinessList, CrmContactBusinessDO::getContactId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getContactCountByCustomerId(Long customerId) {
|
||||
return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.contract;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
|
||||
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.dal.dataobject.contract.CrmContractProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* CRM 合同 Service 接口
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
public interface CrmContractService {
|
||||
|
||||
/**
|
||||
* 创建合同
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @param userId 用户编号
|
||||
* @return 编号
|
||||
*/
|
||||
Long createContract(@Valid CrmContractSaveReqVO createReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新合同
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateContract(@Valid CrmContractSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除合同
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteContract(Long id);
|
||||
|
||||
/**
|
||||
* 合同转移
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void transferContract(CrmContractTransferReqVO reqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新合同相关的更进信息
|
||||
*
|
||||
* @param id 合同编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param contactLastContent 最后联系内容
|
||||
*/
|
||||
void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||
|
||||
/**
|
||||
* 发起合同审批流程
|
||||
*
|
||||
* @param id 合同编号
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void submitContract(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 更新合同流程审批结果
|
||||
*
|
||||
* @param id 合同编号
|
||||
* @param bpmResult BPM 审批结果
|
||||
*/
|
||||
void updateContractAuditStatus(Long id, Integer bpmResult);
|
||||
|
||||
/**
|
||||
* 获得合同
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 合同
|
||||
*/
|
||||
CrmContractDO getContract(Long id);
|
||||
|
||||
/**
|
||||
* 校验合同是否合法
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 合同
|
||||
*/
|
||||
CrmContractDO validateContract(Long id);
|
||||
|
||||
/**
|
||||
* 获得合同列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 合同列表
|
||||
*/
|
||||
List<CrmContractDO> getContractList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得合同 Map
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 合同 Map
|
||||
*/
|
||||
default Map<Long, CrmContractDO> getContractMap(Collection<Long> ids) {
|
||||
return convertMap(getContractList(ids), CrmContractDO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得合同分页
|
||||
*
|
||||
* 数据权限:基于 {@link CrmContractDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @param userId 用户编号
|
||||
* @return 合同分页
|
||||
*/
|
||||
PageResult<CrmContractDO> getContractPage(CrmContractPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得合同分页,基于指定客户
|
||||
*
|
||||
* 数据权限:基于 {@link CrmCustomerDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 合同分页
|
||||
*/
|
||||
PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得合同分页,基于指定商机
|
||||
*
|
||||
* 数据权限:基于 {@link CrmBusinessDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 合同分页
|
||||
*/
|
||||
PageResult<CrmContractDO> getContractPageByBusinessId(CrmContractPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 查询属于某个联系人的合同数量
|
||||
*
|
||||
* @param contactId 联系人ID
|
||||
* @return 合同
|
||||
*/
|
||||
Long getContractCountByContactId(Long contactId);
|
||||
|
||||
/**
|
||||
* 获取关联客户的合同数量
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @return 数量
|
||||
*/
|
||||
Long getContractCountByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 根据商机编号,获取关联客户的合同数量
|
||||
*
|
||||
* @param businessId 商机编号
|
||||
* @return 数量
|
||||
*/
|
||||
Long getContractCountByBusinessId(Long businessId);
|
||||
|
||||
/**
|
||||
* 根据合同编号,获得合同的产品列表
|
||||
*
|
||||
* @param contactId 合同编号
|
||||
* @return 产品列表
|
||||
*/
|
||||
List<CrmContractProductDO> getContractProductListByContractId(Long contactId);
|
||||
|
||||
/**
|
||||
* 获得待审核合同数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 提醒数量
|
||||
*/
|
||||
Long getAuditContractCount(Long userId);
|
||||
|
||||
/**
|
||||
* 获得即将到期(提醒)的合同数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 提醒数量
|
||||
*/
|
||||
Long getRemindContractCount(Long userId);
|
||||
|
||||
/**
|
||||
* 获得合同列表
|
||||
*
|
||||
* @param customerId 客户编号
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 合同列表
|
||||
*/
|
||||
List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||
|
||||
}
|
||||
|
|
@ -1,415 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.contract;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper;
|
||||
import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
|
||||
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.customer.CrmCustomerService;
|
||||
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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
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.LogRecordConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
|
||||
|
||||
/**
|
||||
* CRM 合同 Service 实现类
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class CrmContractServiceImpl implements CrmContractService {
|
||||
|
||||
/**
|
||||
* BPM 合同审批流程标识
|
||||
*/
|
||||
public static final String BPM_PROCESS_DEFINITION_KEY = "crm-contract-audit";
|
||||
|
||||
@Resource
|
||||
private CrmContractMapper contractMapper;
|
||||
@Resource
|
||||
private CrmContractProductMapper contractProductMapper;
|
||||
|
||||
@Resource
|
||||
private CrmNoRedisDAO noRedisDAO;
|
||||
|
||||
@Resource
|
||||
private CrmPermissionService crmPermissionService;
|
||||
@Resource
|
||||
private CrmProductService productService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmContractConfigService contractConfigService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private CrmReceivableService receivableService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private BpmProcessInstanceApi bpmProcessInstanceApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
|
||||
success = CRM_CONTRACT_CREATE_SUCCESS)
|
||||
public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
|
||||
// 1.1 校验产品项的有效性
|
||||
List<CrmContractProductDO> contractProducts = validateContractProducts(createReqVO.getProducts());
|
||||
// 1.2 校验关联字段
|
||||
validateRelationDataExists(createReqVO);
|
||||
// 1.3 生成序号
|
||||
String no = noRedisDAO.generate(CrmNoRedisDAO.CONTRACT_NO_PREFIX);
|
||||
if (contractMapper.selectByNo(no) != null) {
|
||||
throw exception(CONTRACT_NO_EXISTS);
|
||||
}
|
||||
|
||||
// 2.1 插入合同
|
||||
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setNo(no);
|
||||
calculateTotalPrice(contract, contractProducts);
|
||||
contractMapper.insert(contract);
|
||||
// 2.2 插入合同关联商品
|
||||
if (CollUtil.isNotEmpty(contractProducts)) {
|
||||
contractProducts.forEach(item -> item.setContractId(contract.getId()));
|
||||
contractProductMapper.insertBatch(contractProducts);
|
||||
}
|
||||
|
||||
// 3. 创建数据权限
|
||||
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(contract.getOwnerUserId())
|
||||
.setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contract", contract);
|
||||
return contract.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_CONTRACT_UPDATE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateContract(CrmContractSaveReqVO updateReqVO) {
|
||||
Assert.notNull(updateReqVO.getId(), "合同编号不能为空");
|
||||
updateReqVO.setOwnerUserId(null); // 不允许更新的字段
|
||||
// 1.1 校验存在
|
||||
CrmContractDO contract = validateContractExists(updateReqVO.getId());
|
||||
// 1.2 只有草稿、审批中,可以编辑;
|
||||
if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
|
||||
CrmAuditStatusEnum.PROCESS.getStatus())) {
|
||||
throw exception(CONTRACT_UPDATE_FAIL_NOT_DRAFT);
|
||||
}
|
||||
// 1.3 校验产品项的有效性
|
||||
List<CrmContractProductDO> contractProducts = validateContractProducts(updateReqVO.getProducts());
|
||||
// 1.4 校验关联字段
|
||||
validateRelationDataExists(updateReqVO);
|
||||
|
||||
// 2.1 更新合同
|
||||
CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
|
||||
calculateTotalPrice(updateObj, contractProducts);
|
||||
contractMapper.updateById(updateObj);
|
||||
// 2.2 更新合同关联商品
|
||||
updateContractProduct(updateReqVO.getId(), contractProducts);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contract, CrmContractSaveReqVO.class));
|
||||
LogRecordContext.putVariable("contractName", contract.getName());
|
||||
}
|
||||
|
||||
private void updateContractProduct(Long id, List<CrmContractProductDO> newList) {
|
||||
List<CrmContractProductDO> oldList = contractProductMapper.selectListByContractId(id);
|
||||
List<List<CrmContractProductDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录
|
||||
(oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
|
||||
if (CollUtil.isNotEmpty(diffList.get(0))) {
|
||||
diffList.get(0).forEach(o -> o.setContractId(id));
|
||||
contractProductMapper.insertBatch(diffList.get(0));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(diffList.get(1))) {
|
||||
contractProductMapper.updateBatch(diffList.get(1));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(diffList.get(2))) {
|
||||
contractProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmContractProductDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验关联数据是否存在
|
||||
*
|
||||
* @param reqVO 请求
|
||||
*/
|
||||
private void validateRelationDataExists(CrmContractSaveReqVO reqVO) {
|
||||
// 1. 校验客户
|
||||
if (reqVO.getCustomerId() != null) {
|
||||
customerService.validateCustomer(reqVO.getCustomerId());
|
||||
}
|
||||
// 2. 校验负责人
|
||||
if (reqVO.getOwnerUserId() != null) {
|
||||
adminUserApi.validateUser(reqVO.getOwnerUserId());
|
||||
}
|
||||
// 3. 如果有关联商机,则需要校验存在
|
||||
if (reqVO.getBusinessId() != null) {
|
||||
businessService.validateBusiness(reqVO.getBusinessId());
|
||||
}
|
||||
// 4. 校验签约相关字段
|
||||
if (reqVO.getSignContactId() != null) {
|
||||
contactService.validateContact(reqVO.getSignContactId());
|
||||
}
|
||||
if (reqVO.getSignUserId() != null) {
|
||||
adminUserApi.validateUser(reqVO.getSignUserId());
|
||||
}
|
||||
}
|
||||
|
||||
private List<CrmContractProductDO> validateContractProducts(List<CrmContractSaveReqVO.Product> list) {
|
||||
// 1. 校验产品存在
|
||||
productService.validProductList(convertSet(list, CrmContractSaveReqVO.Product::getProductId));
|
||||
// 2. 转化为 CrmContractProductDO 列表
|
||||
return convertList(list, o -> BeanUtils.toBean(o, CrmContractProductDO.class,
|
||||
item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getContractPrice(), item.getCount()))));
|
||||
}
|
||||
|
||||
private void calculateTotalPrice(CrmContractDO contract, List<CrmContractProductDO> contractProducts) {
|
||||
contract.setTotalProductPrice(getSumValue(contractProducts, CrmContractProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
|
||||
BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(contract.getTotalProductPrice(), contract.getDiscountPercent());
|
||||
contract.setTotalPrice(contract.getTotalProductPrice().subtract(discountPrice));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_CONTRACT_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteContract(Long id) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
private CrmContractDO validateContractExists(Long id) {
|
||||
CrmContractDO contract = contractMapper.selectById(id);
|
||||
if (contract == null) {
|
||||
throw exception(CONTRACT_NOT_EXISTS);
|
||||
}
|
||||
return contract;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
|
||||
success = CRM_CONTRACT_TRANSFER_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
|
||||
// 1. 校验合同是否存在
|
||||
CrmContractDO contract = validateContractExists(reqVO.getId());
|
||||
|
||||
// 2.1 数据权限转移
|
||||
crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTRACT.getType(),
|
||||
reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
|
||||
// 2.2 设置负责人
|
||||
contractMapper.updateById(new CrmContractDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));
|
||||
|
||||
// 3. 记录转移日志
|
||||
LogRecordContext.putVariable("contract", contract);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
|
||||
success = CRM_CONTRACT_FOLLOW_UP_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
|
||||
// 1. 校验存在
|
||||
CrmContractDO contract = validateContractExists(id);
|
||||
|
||||
// 2. 更新联系人的跟进信息
|
||||
contractMapper.updateById(new CrmContractDO().setId(id).setContactLastTime(LocalDateTime.now()));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contractName", contract.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_CONTRACT_SUBMIT_SUCCESS)
|
||||
public void submitContract(Long id, Long userId) {
|
||||
// 1. 校验合同是否在审批
|
||||
CrmContractDO contract = validateContractExists(id);
|
||||
if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
|
||||
throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT);
|
||||
}
|
||||
|
||||
// 2. 创建合同审批流程实例
|
||||
String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
|
||||
.setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
|
||||
|
||||
// 3. 更新合同工作流编号
|
||||
contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId)
|
||||
.setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
|
||||
|
||||
// 3. 记录日志
|
||||
LogRecordContext.putVariable("contractName", contract.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContractAuditStatus(Long id, Integer bpmResult) {
|
||||
// 1.1 校验合同是否存在
|
||||
CrmContractDO contract = validateContractExists(id);
|
||||
// 1.2 只有审批中,可以更新审批结果
|
||||
if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
|
||||
log.error("[updateContractAuditStatus][contract({}) 不处于审批中,无法更新审批结果({})]",
|
||||
contract.getId(), bpmResult);
|
||||
throw exception(CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
|
||||
}
|
||||
|
||||
// 2. 更新合同审批结果
|
||||
Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
|
||||
contractMapper.updateById(new CrmContractDO().setId(id).setAuditStatus(auditStatus));
|
||||
}
|
||||
|
||||
// ======================= 查询相关 =======================
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmContractDO getContract(Long id) {
|
||||
return contractMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmContractDO validateContract(Long id) {
|
||||
return validateContractExists(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContractDO> getContractList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return contractMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmContractDO> getContractPage(CrmContractPageReqVO pageReqVO, Long userId) {
|
||||
// 1. 即将到期,需要查询合同配置
|
||||
CrmContractConfigDO config = null;
|
||||
if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) {
|
||||
config = contractConfigService.getContractConfig();
|
||||
if (config != null && Boolean.FALSE.equals(config.getNotifyEnabled())) {
|
||||
config = null;
|
||||
}
|
||||
if (config == null) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
}
|
||||
// 2. 查询分页
|
||||
return contractMapper.selectPage(pageReqVO, userId, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO) {
|
||||
return contractMapper.selectPageByCustomerId(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#pageReqVO.businessId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmContractDO> getContractPageByBusinessId(CrmContractPageReqVO pageReqVO) {
|
||||
return contractMapper.selectPageByBusinessId(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getContractCountByContactId(Long contactId) {
|
||||
return contractMapper.selectCountByContactId(contactId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getContractCountByCustomerId(Long customerId) {
|
||||
return contractMapper.selectCount(CrmContractDO::getCustomerId, customerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getContractCountByBusinessId(Long businessId) {
|
||||
return contractMapper.selectCountByBusinessId(businessId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContractProductDO> getContractProductListByContractId(Long contactId) {
|
||||
return contractProductMapper.selectListByContractId(contactId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAuditContractCount(Long userId) {
|
||||
return contractMapper.selectCountByAudit(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getRemindContractCount(Long userId) {
|
||||
CrmContractConfigDO config = contractConfigService.getContractConfig();
|
||||
if (config == null || Boolean.FALSE.equals(config.getNotifyEnabled())) {
|
||||
return 0L;
|
||||
}
|
||||
return contractMapper.selectCountByRemind(userId, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
return contractMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,662 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.customer;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
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;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
|
||||
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.customer.bo.CrmCustomerCreateReqBO;
|
||||
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.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
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;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
|
||||
import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* 客户 Service 实现类
|
||||
*
|
||||
* @author Wanwan
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Validated
|
||||
public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
|
||||
@Resource
|
||||
private CrmCustomerMapper customerMapper;
|
||||
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
@Resource
|
||||
private CrmCustomerLimitConfigService customerLimitConfigService;
|
||||
@Resource
|
||||
@Lazy
|
||||
private CrmCustomerPoolConfigService customerPoolConfigService;
|
||||
@Resource
|
||||
@Lazy
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
@Lazy
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
@Lazy
|
||||
private CrmContractService contractService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
|
||||
success = CRM_CUSTOMER_CREATE_SUCCESS)
|
||||
public Long createCustomer(CrmCustomerSaveReqVO createReqVO, Long userId) {
|
||||
createReqVO.setId(null);
|
||||
// 1. 校验拥有客户是否到达上限
|
||||
validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
|
||||
|
||||
// 2. 插入客户
|
||||
CrmCustomerDO customer = initCustomer(createReqVO, userId);
|
||||
customerMapper.insert(customer);
|
||||
|
||||
// 3. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
return customer.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化客户的通用字段
|
||||
*
|
||||
* @param customer 客户信息
|
||||
* @param ownerUserId 负责人编号
|
||||
* @return 客户信息 DO
|
||||
*/
|
||||
private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) {
|
||||
return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId)
|
||||
.setOwnerTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_CUSTOMER_UPDATE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateCustomer(CrmCustomerSaveReqVO updateReqVO) {
|
||||
Assert.notNull(updateReqVO.getId(), "客户编号不能为空");
|
||||
updateReqVO.setOwnerUserId(null); // 更新的时候,要把 updateReqVO 负责人设置为空,避免修改
|
||||
// 1. 校验存在
|
||||
CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId());
|
||||
|
||||
// 2. 更新客户
|
||||
CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);
|
||||
customerMapper.updateById(updateObj);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerSaveReqVO.class));
|
||||
LogRecordContext.putVariable("customerName", oldCustomer.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateCustomerDealStatus(Long id, Boolean dealStatus) {
|
||||
// 1.1 校验存在
|
||||
CrmCustomerDO customer = validateCustomerExists(id);
|
||||
// 1.2 校验是否重复操作
|
||||
if (Objects.equals(customer.getDealStatus(), dealStatus)) {
|
||||
throw exception(CUSTOMER_UPDATE_DEAL_STATUS_FAIL);
|
||||
}
|
||||
|
||||
// 2. 更新客户的成交状态
|
||||
customerMapper.updateById(new CrmCustomerDO().setId(id).setDealStatus(dealStatus));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customerName", customer.getName());
|
||||
LogRecordContext.putVariable("dealStatus", dealStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
|
||||
success = CRM_CUSTOMER_FOLLOW_UP_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
|
||||
// 1.1 校验存在
|
||||
CrmCustomerDO customer = validateCustomerExists(id);
|
||||
|
||||
// 2. 更新客户的跟进信息
|
||||
customerMapper.updateById(new CrmCustomerDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
|
||||
.setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customerName", customer.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_CUSTOMER_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteCustomer(Long id) {
|
||||
// 1.1 校验存在
|
||||
CrmCustomerDO customer = validateCustomerExists(id);
|
||||
// 1.2 检查引用
|
||||
validateCustomerReference(id);
|
||||
|
||||
// 2. 删除客户
|
||||
customerMapper.deleteById(id);
|
||||
// 3. 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customerName", customer.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
|
||||
success = CRM_CUSTOMER_TRANSFER_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
|
||||
// 1.1 校验客户是否存在
|
||||
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()));
|
||||
// 2.2 转移后重新设置负责人
|
||||
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)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#lockReqVO.id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void lockCustomer(CrmCustomerLockReqVO lockReqVO, Long userId) {
|
||||
// 1.1 校验当前客户是否存在
|
||||
CrmCustomerDO customer = validateCustomerExists(lockReqVO.getId());
|
||||
// 1.2 校验当前是否重复操作锁定/解锁状态
|
||||
if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) {
|
||||
throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK);
|
||||
}
|
||||
// 1.3 校验锁定上限
|
||||
if (lockReqVO.getLockStatus()) {
|
||||
validateCustomerExceedLockLimit(userId);
|
||||
}
|
||||
|
||||
// 2. 更新锁定状态
|
||||
customerMapper.updateById(BeanUtils.toBean(lockReqVO, CrmCustomerDO.class));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
// tips: 因为这里使用的是老的状态所以记录时反着记录,也就是 lockStatus 为 true 那么就是解锁反之为锁定
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
|
||||
success = CRM_CUSTOMER_CREATE_SUCCESS)
|
||||
public Long createCustomer(CrmCustomerCreateReqBO createReqBO, Long userId) {
|
||||
// 1. 插入客户
|
||||
CrmCustomerDO customer = initCustomer(createReqBO, userId);
|
||||
customerMapper.insert(customer);
|
||||
|
||||
// 2. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
return customer.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
|
||||
CrmCustomerImportReqVO importReqVO) {
|
||||
// 校验非空
|
||||
importCustomers = filterList(importCustomers, item -> Objects.nonNull(item.getName()));
|
||||
if (CollUtil.isEmpty(importCustomers)) {
|
||||
throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
|
||||
}
|
||||
|
||||
// 逐条处理
|
||||
CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>())
|
||||
.updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build();
|
||||
importCustomers.forEach(importCustomer -> {
|
||||
// 校验,判断是否有不符合的原因
|
||||
try {
|
||||
validateCustomerForCreate(importCustomer);
|
||||
} catch (ServiceException ex) {
|
||||
respVO.getFailureCustomerNames().put(importCustomer.getName(), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
// 情况一:判断如果不存在,在进行插入
|
||||
CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());
|
||||
if (existCustomer == null) {
|
||||
// 1.1 插入客户信息
|
||||
CrmCustomerDO customer = initCustomer(importCustomer, importReqVO.getOwnerUserId());
|
||||
customerMapper.insert(customer);
|
||||
respVO.getCreateCustomerNames().add(importCustomer.getName());
|
||||
// 1.2 创建数据权限
|
||||
if (importReqVO.getOwnerUserId() != null) {
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId()).setUserId(importReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
}
|
||||
// 1.3 记录操作日志
|
||||
getSelf().importCustomerLog(customer, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况二:如果存在,判断是否允许更新
|
||||
if (!importReqVO.getUpdateSupport()) {
|
||||
respVO.getFailureCustomerNames().put(importCustomer.getName(),
|
||||
StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName()));
|
||||
return;
|
||||
}
|
||||
// 2.1 更新客户信息
|
||||
CrmCustomerDO updateCustomer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class)
|
||||
.setId(existCustomer.getId());
|
||||
customerMapper.updateById(updateCustomer);
|
||||
respVO.getUpdateCustomerNames().add(importCustomer.getName());
|
||||
// 2.2 记录操作日志
|
||||
getSelf().importCustomerLog(updateCustomer, true);
|
||||
});
|
||||
return respVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录导入客户时的操作日志
|
||||
*
|
||||
* @param customer 客户信息
|
||||
* @param isUpdate 是否更新;true - 更新,false - 新增
|
||||
*/
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_IMPORT_SUB_TYPE, bizNo = "{{#customer.id}}",
|
||||
success = CRM_CUSTOMER_IMPORT_SUCCESS)
|
||||
public void importCustomerLog(CrmCustomerDO customer, boolean isUpdate) {
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
LogRecordContext.putVariable("isUpdate", isUpdate);
|
||||
}
|
||||
|
||||
// ==================== 公海相关操作 ====================
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_POOL_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_CUSTOMER_POOL_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void putCustomerPool(Long id) {
|
||||
// 1. 校验存在
|
||||
CrmCustomerDO customer = customerMapper.selectById(id);
|
||||
if (customer == null) {
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
}
|
||||
// 1.2. 校验是否为公海数据
|
||||
validateCustomerOwnerExists(customer, true);
|
||||
// 1.3. 校验客户是否锁定
|
||||
validateCustomerIsLocked(customer, true);
|
||||
|
||||
// 2. 客户放入公海
|
||||
putCustomerPool(customer);
|
||||
|
||||
// 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customerName", customer.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive) {
|
||||
// 1.1 校验存在
|
||||
List<CrmCustomerDO> customers = customerMapper.selectBatchIds(ids);
|
||||
if (customers.size() != ids.size()) {
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
}
|
||||
// 1.2 校验负责人是否存在
|
||||
adminUserApi.validateUserList(singletonList(ownerUserId));
|
||||
// 1.3 校验状态
|
||||
customers.forEach(customer -> {
|
||||
// 校验是否已有负责人
|
||||
validateCustomerOwnerExists(customer, false);
|
||||
// 校验是否锁定
|
||||
validateCustomerIsLocked(customer, false);
|
||||
// 校验成交状态
|
||||
validateCustomerDeal(customer);
|
||||
});
|
||||
// 1.4 校验负责人是否到达上限
|
||||
validateCustomerExceedOwnerLimit(ownerUserId, customers.size());
|
||||
|
||||
// 2. 领取公海数据
|
||||
List<CrmCustomerDO> updateCustomers = new ArrayList<>();
|
||||
List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
|
||||
customers.forEach(customer -> {
|
||||
// 2.1. 设置负责人
|
||||
updateCustomers.add(new CrmCustomerDO().setId(customer.getId())
|
||||
.setOwnerUserId(ownerUserId).setOwnerTime(LocalDateTime.now()));
|
||||
// 2.2. 创建负责人数据权限
|
||||
createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
});
|
||||
// 2.2 更新客户负责人
|
||||
customerMapper.updateBatch(updateCustomers);
|
||||
// 2.3 创建负责人数据权限
|
||||
permissionService.createPermissionBatch(createPermissions);
|
||||
// TODO @芋艿:要不要处理关联的联系人???
|
||||
|
||||
// 3. 记录操作日志
|
||||
AdminUserRespDTO user = null;
|
||||
if (!isReceive) {
|
||||
user = adminUserApi.getUser(ownerUserId);
|
||||
}
|
||||
for (CrmCustomerDO customer : customers) {
|
||||
getSelf().receiveCustomerLog(customer, user == null ? null : user.getNickname());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int autoPutCustomerPool() {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (poolConfig == null || !poolConfig.getEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
// 1. 获得需要放到的客户列表
|
||||
List<CrmCustomerDO> customerList = customerMapper.selectListByAutoPool(poolConfig);
|
||||
// 2. 逐个放入公海
|
||||
int count = 0;
|
||||
for (CrmCustomerDO customer : customerList) {
|
||||
try {
|
||||
getSelf().putCustomerPool(customer);
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[autoPutCustomerPool][客户({}) 放入公海异常]", customer.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class) // 需要 protected 修饰,因为需要在事务中调用
|
||||
protected void putCustomerPool(CrmCustomerDO customer) {
|
||||
// 1. 设置负责人为 NULL
|
||||
int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
|
||||
if (updateOwnerUserIncr == 0) {
|
||||
throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
|
||||
}
|
||||
|
||||
// 2. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的
|
||||
contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
|
||||
|
||||
// 3. 删除负责人数据权限
|
||||
// 注意:需要放在 contactService 后面,不然【客户】数据权限已经被删除,无法操作!
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
|
||||
CrmPermissionLevelEnum.OWNER.getLevel());
|
||||
}
|
||||
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}",
|
||||
success = CRM_CUSTOMER_RECEIVE_SUCCESS)
|
||||
public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) {
|
||||
// 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
LogRecordContext.putVariable("ownerUserName", ownerUserName);
|
||||
}
|
||||
|
||||
//======================= 查询相关 =======================
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmCustomerDO getCustomer(Long id) {
|
||||
return customerMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmCustomerDO> getCustomerList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return customerMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
|
||||
return customerMapper.selectPage(pageReqVO, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId) {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (ObjUtil.isNull(poolConfig)
|
||||
|| Boolean.FALSE.equals(poolConfig.getEnabled())
|
||||
|| Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
return customerMapper.selectPutPoolRemindCustomerPage(pageVO, poolConfig, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getPutPoolRemindCustomerCount(Long userId) {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (ObjUtil.isNull(poolConfig)
|
||||
|| Boolean.FALSE.equals(poolConfig.getEnabled())
|
||||
|| Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {
|
||||
return 0L;
|
||||
}
|
||||
CrmCustomerPageReqVO pageVO = new CrmCustomerPageReqVO()
|
||||
.setPool(null)
|
||||
.setContactStatus(CrmCustomerPageReqVO.CONTACT_TODAY)
|
||||
.setSceneType(CrmSceneTypeEnum.OWNER.getType());
|
||||
return customerMapper.selectPutPoolRemindCustomerCount(pageVO, poolConfig, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getTodayContactCustomerCount(Long userId) {
|
||||
return customerMapper.selectCountByTodayContact(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getFollowCustomerCount(Long userId) {
|
||||
return customerMapper.selectCountByFollow(userId);
|
||||
}
|
||||
|
||||
// ======================= 校验相关 =======================
|
||||
|
||||
private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) {
|
||||
// 校验客户名称不能为空
|
||||
if (StrUtil.isEmptyIfStr(importCustomer.getName())) {
|
||||
throw exception(CUSTOMER_CREATE_NAME_NOT_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验客户是否被引用
|
||||
*
|
||||
* @param id 客户编号
|
||||
*/
|
||||
private void validateCustomerReference(Long id) {
|
||||
if (contactService.getContactCountByCustomerId(id) > 0) {
|
||||
throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName());
|
||||
}
|
||||
if (businessService.getBusinessCountByCustomerId(id) > 0) {
|
||||
throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_BUSINESS.getName());
|
||||
}
|
||||
if (contractService.getContractCountByCustomerId(id) > 0) {
|
||||
throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTRACT.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验客户是否存在
|
||||
*
|
||||
* @param id 客户 id
|
||||
*/
|
||||
@Override
|
||||
public void validateCustomer(Long id) {
|
||||
validateCustomerExists(id);
|
||||
}
|
||||
|
||||
private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) {
|
||||
if (customer == null) { // 防御一下
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
}
|
||||
// 校验是否为公海数据
|
||||
if (pool && customer.getOwnerUserId() == null) {
|
||||
throw exception(CUSTOMER_IN_POOL, customer.getName());
|
||||
}
|
||||
// 负责人已存在
|
||||
if (!pool && customer.getOwnerUserId() != null) {
|
||||
throw exception(CUSTOMER_OWNER_EXISTS, customer.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private CrmCustomerDO validateCustomerExists(Long id) {
|
||||
CrmCustomerDO customerDO = customerMapper.selectById(id);
|
||||
if (customerDO == null) {
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
}
|
||||
return customerDO;
|
||||
}
|
||||
|
||||
private void validateCustomerIsLocked(CrmCustomerDO customer, Boolean pool) {
|
||||
if (customer.getLockStatus()) {
|
||||
throw exception(pool ? CUSTOMER_LOCKED_PUT_POOL_FAIL : CUSTOMER_LOCKED, customer.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCustomerDeal(CrmCustomerDO customer) {
|
||||
if (customer.getDealStatus()) {
|
||||
throw exception(CUSTOMER_ALREADY_DEAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验用户拥有的客户数量,是否到达上限
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param newCount 附加数量
|
||||
*/
|
||||
private void validateCustomerExceedOwnerLimit(Long userId, int newCount) {
|
||||
List<CrmCustomerLimitConfigDO> limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId(
|
||||
CUSTOMER_OWNER_LIMIT.getType(), userId);
|
||||
if (CollUtil.isEmpty(limitConfigs)) {
|
||||
return;
|
||||
}
|
||||
Long ownerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(null, userId);
|
||||
Long dealOwnerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(true, userId);
|
||||
limitConfigs.forEach(limitConfig -> {
|
||||
long nowCount = limitConfig.getDealCountEnabled() ? ownerCount : ownerCount - dealOwnerCount;
|
||||
if (nowCount + newCount > limitConfig.getMaxCount()) {
|
||||
throw exception(CUSTOMER_OWNER_EXCEED_LIMIT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验用户锁定的客户数量,是否到达上限
|
||||
*
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
private void validateCustomerExceedLockLimit(Long userId) {
|
||||
List<CrmCustomerLimitConfigDO> limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId(
|
||||
CUSTOMER_LOCK_LIMIT.getType(), userId);
|
||||
if (CollUtil.isEmpty(limitConfigs)) {
|
||||
return;
|
||||
}
|
||||
Long lockCount = customerMapper.selectCountByLockStatusAndOwnerUserId(true, userId);
|
||||
Integer maxCount = CollectionUtils.getMaxValue(limitConfigs, CrmCustomerLimitConfigDO::getMaxCount);
|
||||
assert maxCount != null;
|
||||
if (lockCount >= maxCount) {
|
||||
throw exception(CUSTOMER_LOCK_EXCEED_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
* @return 自己
|
||||
*/
|
||||
private CrmCustomerServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
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;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* crm 数据权限 Service 接口
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface CrmPermissionService {
|
||||
|
||||
/**
|
||||
* 创建数据权限
|
||||
*
|
||||
* @param reqVO 创建信息
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void createPermission(CrmPermissionSaveReqVO reqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 创建数据权限
|
||||
*
|
||||
* @param createReqBO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createPermission(@Valid CrmPermissionCreateReqBO createReqBO);
|
||||
|
||||
/**
|
||||
* 创建数据权限
|
||||
*
|
||||
* @param createReqBOs 创建信息
|
||||
*/
|
||||
void createPermissionBatch(@Valid List<CrmPermissionCreateReqBO> createReqBOs);
|
||||
|
||||
/**
|
||||
* 更新数据权限
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updatePermission(CrmPermissionUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 数据权限转移
|
||||
*
|
||||
* @param crmPermissionTransferReqBO 数据权限转移请求
|
||||
*/
|
||||
void transferPermission(@Valid CrmPermissionTransferReqBO crmPermissionTransferReqBO);
|
||||
|
||||
/**
|
||||
* 删除数据权限
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @param level 数据权限级别,关联 {@link CrmPermissionLevelEnum}
|
||||
*/
|
||||
void deletePermission(Integer bizType, Long bizId, Integer level);
|
||||
|
||||
/**
|
||||
* 删除数据权限
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
*/
|
||||
void deletePermission(Integer bizType, Long bizId);
|
||||
|
||||
/**
|
||||
* 批量删除数据权限
|
||||
*
|
||||
* @param ids 权限编号
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void deletePermissionBatch(Collection<Long> ids, Long userId);
|
||||
|
||||
/**
|
||||
* 删除指定用户数据权限
|
||||
*
|
||||
* @param id 权限编号
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void deleteSelfPermission(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 获取数据权限列表,通过 数据类型 x 某个数据
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @return Crm 数据权限列表
|
||||
*/
|
||||
List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Long bizId);
|
||||
|
||||
/**
|
||||
* 获取数据权限列表,通过 数据类型 x 某个数据
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizIds 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @return Crm 数据权限列表
|
||||
*/
|
||||
List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Collection<Long> bizIds);
|
||||
|
||||
/**
|
||||
* 获取用户参与的模块数据列表
|
||||
*
|
||||
* @param bizType 模块类型
|
||||
* @param userId 用户编号
|
||||
* @return 模块数据列表
|
||||
*/
|
||||
List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId);
|
||||
|
||||
/**
|
||||
* 校验是否有指定数据的操作权限
|
||||
*
|
||||
* @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);
|
||||
|
||||
}
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.permission;
|
||||
|
||||
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.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
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;
|
||||
|
||||
/**
|
||||
* CRM 数据权限 Service 接口实现类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
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. 插入权限
|
||||
CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class);
|
||||
permissionMapper.insert(permission);
|
||||
return permission.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createPermissionBatch(List<CrmPermissionCreateReqBO> createReqBOs) {
|
||||
validatePermissionNotExists(createReqBOs);
|
||||
// 1. 校验用户是否存在
|
||||
adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId));
|
||||
|
||||
// 2. 创建
|
||||
List<CrmPermissionDO> permissions = BeanUtils.toBean(createReqBOs, CrmPermissionDO.class);
|
||||
permissionMapper.insertBatch(permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePermission(CrmPermissionUpdateReqVO updateReqVO) {
|
||||
// 1. 校验存在
|
||||
validatePermissionExists(updateReqVO.getIds());
|
||||
// 2. 更新
|
||||
List<CrmPermissionDO> updateList = CollectionUtils.convertList(updateReqVO.getIds(),
|
||||
id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel()));
|
||||
permissionMapper.updateBatch(updateList);
|
||||
}
|
||||
|
||||
private void validatePermissionExists(Collection<Long> ids) {
|
||||
List<CrmPermissionDO> permissionList = permissionMapper.selectBatchIds(ids);
|
||||
if (ObjUtil.notEqual(permissionList.size(), ids.size())) {
|
||||
throw exception(CRM_PERMISSION_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePermissionNotExists(Collection<CrmPermissionCreateReqBO> createReqBOs) {
|
||||
Set<Integer> bizTypes = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizType);
|
||||
Set<Long> bizIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizId);
|
||||
Set<Long> userIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId);
|
||||
Long count = permissionMapper.selectListByBiz(bizTypes, bizIds, userIds);
|
||||
if (count > 0) {
|
||||
throw exception(CRM_PERMISSION_CREATE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void transferPermission(CrmPermissionTransferReqBO transferReqBO) {
|
||||
// 1. 校验数据权限:是否是负责人,只有负责人才可以转移
|
||||
CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId(
|
||||
transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId());
|
||||
String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType());
|
||||
if (oldPermission == null // 不是拥有者,并且不是超管
|
||||
|| (!isOwner(oldPermission.getLevel()) && !CrmPermissionUtils.isCrmAdmin())) {
|
||||
throw exception(CRM_PERMISSION_DENIED, bizTypeName);
|
||||
}
|
||||
// 1.1 校验转移对象是否已经是该负责人
|
||||
if (ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldPermission.getUserId())) {
|
||||
throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName);
|
||||
}
|
||||
// 1.2 校验新负责人是否存在
|
||||
adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId()));
|
||||
|
||||
// 2. 修改新负责人的权限
|
||||
List<CrmPermissionDO> permissions = permissionMapper.selectByBizTypeAndBizId(
|
||||
transferReqBO.getBizType(), transferReqBO.getBizId()); // 获得所有数据权限
|
||||
CrmPermissionDO permission = CollUtil.findOne(permissions,
|
||||
item -> ObjUtil.equal(item.getUserId(), transferReqBO.getNewOwnerUserId()));
|
||||
if (permission == null) {
|
||||
permissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType())
|
||||
.setBizId(transferReqBO.getBizId()).setUserId(transferReqBO.getNewOwnerUserId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
} else {
|
||||
permissionMapper.updateById(new CrmPermissionDO().setId(permission.getId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
}
|
||||
|
||||
// 3. 修改老负责人的权限
|
||||
if (transferReqBO.getOldOwnerPermissionLevel() != null) {
|
||||
permissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId())
|
||||
.setLevel(transferReqBO.getOldOwnerPermissionLevel()));
|
||||
} else {
|
||||
permissionMapper.deleteById(oldPermission.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deletePermission(Integer bizType, Long bizId, Integer level) {
|
||||
// 校验存在
|
||||
List<CrmPermissionDO> permissions = permissionMapper.selectListByBizTypeAndBizIdAndLevel(
|
||||
bizType, bizId, level);
|
||||
if (CollUtil.isEmpty(permissions)) {
|
||||
throw exception(CRM_PERMISSION_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 删除数据权限
|
||||
permissionMapper.deleteBatchIds(convertSet(permissions, CrmPermissionDO::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePermission(Integer bizType, Long bizId) {
|
||||
int deletedCount = permissionMapper.deletePermission(bizType, bizId);
|
||||
if (deletedCount == 0) {
|
||||
throw exception(CRM_PERMISSION_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePermissionBatch(Collection<Long> ids, Long userId) {
|
||||
List<CrmPermissionDO> permissions = permissionMapper.selectBatchIds(ids);
|
||||
if (CollUtil.isEmpty(permissions)) {
|
||||
throw exception(CRM_PERMISSION_NOT_EXISTS);
|
||||
}
|
||||
// 校验:数据权限的模块数据编号是一致的不可能存在两个
|
||||
if (convertSet(permissions, CrmPermissionDO::getBizId).size() > 1) {
|
||||
throw exception(CRM_PERMISSION_DELETE_FAIL);
|
||||
}
|
||||
// 校验操作人是否为负责人
|
||||
CrmPermissionDO permission = permissionMapper.selectByBizAndUserId(permissions.get(0).getBizType(), permissions.get(0).getBizId(), userId);
|
||||
if (permission == null) {
|
||||
throw exception(CRM_PERMISSION_DELETE_DENIED);
|
||||
}
|
||||
if (!CrmPermissionLevelEnum.isOwner(permission.getLevel())) {
|
||||
throw exception(CRM_PERMISSION_DELETE_DENIED);
|
||||
}
|
||||
|
||||
// 删除数据权限
|
||||
permissionMapper.deleteBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSelfPermission(Long id, Long userId) {
|
||||
// 校验数据存在且是自己
|
||||
CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(id, userId);
|
||||
if (permission == null) {
|
||||
throw exception(CRM_PERMISSION_NOT_EXISTS);
|
||||
}
|
||||
// 校验是否是负责人
|
||||
if (CrmPermissionLevelEnum.isOwner(permission.getLevel())) {
|
||||
throw exception(CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER);
|
||||
}
|
||||
|
||||
// 删除
|
||||
permissionMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Long bizId) {
|
||||
return permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Collection<Long> bizIds) {
|
||||
return permissionMapper.selectByBizTypeAndBizIds(bizType, bizIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId) {
|
||||
return permissionMapper.selectListByBizTypeAndUserId(bizType, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level) {
|
||||
List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
|
||||
return anyMatch(permissionList, permission ->
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductCategoryMapper;
|
||||
import com.mzt.logapi.context.LogRecordContext;
|
||||
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.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO.PARENT_ID_NULL;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
|
||||
/**
|
||||
* CRM 产品分类 Service 实现类
|
||||
*
|
||||
* @author ZanGe丶
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmProductCategoryServiceImpl implements CrmProductCategoryService {
|
||||
|
||||
@Resource(name = "crmProductCategoryMapper")
|
||||
private CrmProductCategoryMapper productCategoryMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,解决循环依赖问题
|
||||
private CrmProductService crmProductService;
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_CREATE_SUB_TYPE, bizNo = "{{#productCategoryId}}",
|
||||
success = CRM_PRODUCT_CATEGORY_CREATE_SUCCESS)
|
||||
public Long createProductCategory(CrmProductCategoryCreateReqVO createReqVO) {
|
||||
// 1.1 校验父分类存在
|
||||
validateParentProductCategory(createReqVO.getParentId());
|
||||
// 1.2 分类名称是否存在
|
||||
validateProductNameExists(null, createReqVO.getParentId(), createReqVO.getName());
|
||||
|
||||
// 2. 插入分类
|
||||
CrmProductCategoryDO category = BeanUtils.toBean(createReqVO, CrmProductCategoryDO.class);
|
||||
productCategoryMapper.insert(category);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("productCategoryId", category.getId());
|
||||
return category.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_PRODUCT_CATEGORY_UPDATE_SUCCESS)
|
||||
public void updateProductCategory(CrmProductCategoryCreateReqVO updateReqVO) {
|
||||
// 1.1 校验存在
|
||||
validateProductCategoryExists(updateReqVO.getId());
|
||||
// 1.2 校验父分类存在
|
||||
validateParentProductCategory(updateReqVO.getParentId());
|
||||
// 1.3 分类名称是否存在
|
||||
validateProductNameExists(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
|
||||
|
||||
// 2. 更新分类
|
||||
CrmProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductCategoryDO.class);
|
||||
productCategoryMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
private void validateProductCategoryExists(Long id) {
|
||||
if (productCategoryMapper.selectById(id) == null) {
|
||||
throw exception(PRODUCT_CATEGORY_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateParentProductCategory(Long id) {
|
||||
// 如果是根分类,无需验证
|
||||
if (Objects.equals(id, PARENT_ID_NULL)) {
|
||||
return;
|
||||
}
|
||||
// 父分类不存在
|
||||
CrmProductCategoryDO category = productCategoryMapper.selectById(id);
|
||||
if (category == null) {
|
||||
throw exception(PRODUCT_CATEGORY_PARENT_NOT_EXISTS);
|
||||
}
|
||||
// 父分类不能是二级分类
|
||||
if (!Objects.equals(category.getParentId(), PARENT_ID_NULL)) {
|
||||
throw exception(PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateProductNameExists(Long id, Long parentId, String name) {
|
||||
CrmProductCategoryDO category = productCategoryMapper.selectByParentIdAndName(parentId, name);
|
||||
if (category == null
|
||||
|| category.getId().equals(id)) {
|
||||
return;
|
||||
}
|
||||
throw exception(PRODUCT_CATEGORY_EXISTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_PRODUCT_CATEGORY_DELETE_SUCCESS)
|
||||
public void deleteProductCategory(Long id) {
|
||||
// 1.1 校验存在
|
||||
validateProductCategoryExists(id);
|
||||
// 1.2 校验是否还有子分类
|
||||
if (productCategoryMapper.selectCountByParentId(id) > 0) {
|
||||
throw exception(PRODUCT_CATEGORY_EXISTS_CHILDREN);
|
||||
}
|
||||
// 1.3 校验是否被产品使用
|
||||
if (crmProductService.getProductByCategoryId(id) > 0) {
|
||||
throw exception(PRODUCT_CATEGORY_USED);
|
||||
}
|
||||
// 2. 删除
|
||||
productCategoryMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmProductCategoryDO getProductCategory(Long id) {
|
||||
return productCategoryMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmProductCategoryDO> getProductCategoryList(CrmProductCategoryListReqVO listReqVO) {
|
||||
return productCategoryMapper.selectList(listReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmProductCategoryDO> getProductCategoryList(Collection<Long> ids) {
|
||||
return productCategoryMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.receivable;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.receivable.vo.plan.CrmReceivablePlanPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;
|
||||
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.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.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 org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_UPDATE_FAIL;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
|
||||
/**
|
||||
* 回款计划 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
|
||||
|
||||
@Resource
|
||||
private CrmReceivablePlanMapper receivablePlanMapper;
|
||||
|
||||
@Resource
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}",
|
||||
success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
|
||||
public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {
|
||||
// 1. 校验关联数据是否存在
|
||||
validateRelationDataExists(createReqVO);
|
||||
|
||||
// 2. 插入回款计划
|
||||
CrmReceivablePlanDO maxPeriodReceivablePlan = receivablePlanMapper.selectMaxPeriodByContractId(createReqVO.getContractId());
|
||||
int period = maxPeriodReceivablePlan == null ? 1 : maxPeriodReceivablePlan.getPeriod() + 1;
|
||||
CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setPeriod(period);
|
||||
if (createReqVO.getReturnTime() != null && createReqVO.getRemindDays() != null) {
|
||||
receivablePlan.setRemindTime(createReqVO.getReturnTime().minusDays(createReqVO.getRemindDays()));
|
||||
}
|
||||
receivablePlanMapper.insert(receivablePlan);
|
||||
|
||||
// 3. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(createReqVO.getOwnerUserId())
|
||||
.setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivablePlan", receivablePlan);
|
||||
return receivablePlan.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {
|
||||
updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null); // 防止修改这些字段
|
||||
// 1.1 校验存在
|
||||
validateRelationDataExists(updateReqVO);
|
||||
// 1.2 校验关联数据是否存在
|
||||
CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
|
||||
// 1.3 如果已经有对应的回款,则不允许编辑
|
||||
if (Objects.nonNull(oldReceivablePlan.getReceivableId())) {
|
||||
throw exception(RECEIVABLE_PLAN_UPDATE_FAIL);
|
||||
}
|
||||
|
||||
// 2. 更新回款计划
|
||||
CrmReceivablePlanDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivablePlanDO.class);
|
||||
if (updateReqVO.getReturnTime() != null && updateReqVO.getRemindDays() != null) {
|
||||
updateObj.setRemindTime(updateReqVO.getReturnTime().minusDays(updateReqVO.getRemindDays()));
|
||||
}
|
||||
receivablePlanMapper.updateById(updateObj);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanSaveReqVO.class));
|
||||
LogRecordContext.putVariable("receivablePlan", oldReceivablePlan);
|
||||
}
|
||||
|
||||
private void validateRelationDataExists(CrmReceivablePlanSaveReqVO reqVO) {
|
||||
// 校验负责人存在
|
||||
if (reqVO.getOwnerUserId() != null) {
|
||||
adminUserApi.validateUser(reqVO.getOwnerUserId());
|
||||
}
|
||||
// 校验合同存在
|
||||
if (reqVO.getContractId() != null) {
|
||||
CrmContractDO contract = contractService.getContract(reqVO.getContractId());
|
||||
reqVO.setCustomerId(contract.getCustomerId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateReceivablePlanReceivableId(Long id, Long receivableId) {
|
||||
// 校验存在
|
||||
validateReceivablePlanExists(id);
|
||||
// 更新回款计划
|
||||
receivablePlanMapper.updateById(new CrmReceivablePlanDO().setId(id).setReceivableId(receivableId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteReceivablePlan(Long id) {
|
||||
// 1. 校验存在
|
||||
CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id);
|
||||
|
||||
// 2. 删除
|
||||
receivablePlanMapper.deleteById(id);
|
||||
// 3. 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), id);
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivablePlan", receivablePlan);
|
||||
}
|
||||
|
||||
private CrmReceivablePlanDO validateReceivablePlanExists(Long id) {
|
||||
CrmReceivablePlanDO receivablePlan = receivablePlanMapper.selectById(id);
|
||||
if (receivablePlan == null) {
|
||||
throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
|
||||
}
|
||||
return receivablePlan;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmReceivablePlanDO getReceivablePlan(Long id) {
|
||||
return receivablePlanMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmReceivablePlanDO> getReceivablePlanList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return receivablePlanMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
|
||||
return receivablePlanMapper.selectPage(pageReqVO, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO) {
|
||||
return receivablePlanMapper.selectPageByCustomerId(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getReceivablePlanRemindCount(Long userId) {
|
||||
return receivablePlanMapper.selectReceivablePlanCountByRemind(userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.receivable;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* CRM 回款 Service 接口
|
||||
*
|
||||
* @author 赤焰
|
||||
*/
|
||||
public interface CrmReceivableService {
|
||||
|
||||
/**
|
||||
* 创建回款
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createReceivable(@Valid CrmReceivableSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新回款
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateReceivable(@Valid CrmReceivableSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新回款流程审批结果
|
||||
*
|
||||
* @param id 回款编号
|
||||
* @param bpmResult BPM 审批结果
|
||||
*/
|
||||
void updateReceivableAuditStatus(Long id, Integer bpmResult);
|
||||
|
||||
/**
|
||||
* 删除回款
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteReceivable(Long id);
|
||||
|
||||
/**
|
||||
* 发起回款审批流程
|
||||
*
|
||||
* @param id 回款编号
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void submitReceivable(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 获得回款
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 回款
|
||||
*/
|
||||
CrmReceivableDO getReceivable(Long id);
|
||||
|
||||
/**
|
||||
* 获得回款列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 回款列表
|
||||
*/
|
||||
List<CrmReceivableDO> getReceivableList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得回款 Map
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 回款 Map
|
||||
*/
|
||||
default Map<Long, CrmReceivableDO> getReceivableMap(Collection<Long> ids) {
|
||||
return convertMap(getReceivableList(ids), CrmReceivableDO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得回款分页
|
||||
*
|
||||
* 数据权限:基于 {@link CrmReceivableDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @param userId 用户编号
|
||||
* @return 回款分页
|
||||
*/
|
||||
PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得回款分页,基于指定客户
|
||||
*
|
||||
* 数据权限:基于 {@link CrmCustomerDO} 读取
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 回款分页
|
||||
*/
|
||||
PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得待审核回款数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 待审批数量
|
||||
*/
|
||||
Long getAuditReceivableCount(Long userId);
|
||||
|
||||
/**
|
||||
* 获得合同已回款金额 Map
|
||||
*
|
||||
* @param contractIds 合同编号
|
||||
* @return 回款金额 Map
|
||||
*/
|
||||
Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds);
|
||||
|
||||
/**
|
||||
* 根据合同编号查询回款数量
|
||||
*
|
||||
* @param contractId 合同编号
|
||||
* @return 回款数量
|
||||
*/
|
||||
Long getReceivableCountByContractId(Long contractId);
|
||||
|
||||
}
|
||||
|
|
@ -1,309 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.receivable;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
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.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
|
||||
import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
|
||||
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.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.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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
|
||||
|
||||
/**
|
||||
* CRM 回款 Service 实现类
|
||||
*
|
||||
* @author 赤焰
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
|
||||
/**
|
||||
* BPM 合同审批流程标识
|
||||
*/
|
||||
public static final String BPM_PROCESS_DEFINITION_KEY = "crm-receivable-audit";
|
||||
|
||||
@Resource
|
||||
private CrmReceivableMapper receivableMapper;
|
||||
|
||||
@Resource
|
||||
private CrmNoRedisDAO noRedisDAO;
|
||||
|
||||
@Resource
|
||||
private CrmContractService contractService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private CrmReceivablePlanService receivablePlanService;
|
||||
@Resource
|
||||
private CrmPermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private BpmProcessInstanceApi bpmProcessInstanceApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
|
||||
success = CRM_RECEIVABLE_CREATE_SUCCESS)
|
||||
public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
|
||||
// 1.1 校验可回款金额超过上限
|
||||
validateReceivablePriceExceedsLimit(createReqVO);
|
||||
// 1.2 校验关联数据存在
|
||||
validateRelationDataExists(createReqVO);
|
||||
// 1.3 生成回款编号
|
||||
String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
|
||||
if (receivableMapper.selectByNo(no) != null) {
|
||||
throw exception(RECEIVABLE_NO_EXISTS);
|
||||
}
|
||||
|
||||
// 2.1 插入回款
|
||||
CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
|
||||
.setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
|
||||
receivableMapper.insert(receivable);
|
||||
// 2.2
|
||||
|
||||
// 3. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
|
||||
.setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
|
||||
|
||||
// 4. 更新关联的回款计划
|
||||
if (createReqVO.getPlanId() != null) {
|
||||
receivablePlanService.updateReceivablePlanReceivableId(receivable.getPlanId(), receivable.getId());
|
||||
}
|
||||
|
||||
// 5. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivable", receivable);
|
||||
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
|
||||
return receivable.getId();
|
||||
}
|
||||
|
||||
private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) {
|
||||
// 1. 计算剩余可退款金额,不包括 reqVO 自身
|
||||
CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
|
||||
List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
|
||||
Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
|
||||
if (reqVO.getId() != null) {
|
||||
receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId()));
|
||||
}
|
||||
BigDecimal notReceivablePrice = contract.getTotalPrice().subtract(
|
||||
CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
|
||||
// 2. 校验金额是否超过
|
||||
if (reqVO.getPrice().compareTo(notReceivablePrice) > 0) {
|
||||
throw exception(RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, notReceivablePrice);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
|
||||
if (reqVO.getOwnerUserId() != null) {
|
||||
adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
|
||||
}
|
||||
if (reqVO.getContractId() != null) {
|
||||
CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
|
||||
if (ObjectUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
|
||||
throw exception(RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE);
|
||||
}
|
||||
reqVO.setCustomerId(contract.getCustomerId()); // 设置客户编号
|
||||
}
|
||||
if (reqVO.getPlanId() != null) {
|
||||
CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());
|
||||
if (receivablePlan == null) {
|
||||
throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
|
||||
}
|
||||
if (receivablePlan.getReceivableId() != null) {
|
||||
throw exception(RECEIVABLE_PLAN_EXISTS_RECEIVABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
||||
success = CRM_RECEIVABLE_UPDATE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
|
||||
Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
|
||||
updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段
|
||||
// 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())) {
|
||||
throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
|
||||
}
|
||||
|
||||
// 2. 更新回款
|
||||
CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class);
|
||||
receivableMapper.updateById(updateObj);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
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
|
||||
public void updateReceivableAuditStatus(Long id, Integer bpmResult) {
|
||||
// 1.1 校验存在
|
||||
CrmReceivableDO receivable = validateReceivableExists(id);
|
||||
// 1.2 只有审批中,可以更新审批结果
|
||||
if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
|
||||
log.error("[updateReceivableAuditStatus][receivable({}) 不处于审批中,无法更新审批结果({})]",
|
||||
receivable.getId(), bpmResult);
|
||||
throw exception(RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
|
||||
}
|
||||
|
||||
// 2. 更新回款审批状态
|
||||
Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
|
||||
receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_RECEIVABLE_DELETE_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteReceivable(Long id) {
|
||||
// 1.1 校验存在
|
||||
CrmReceivableDO receivable = validateReceivableExists(id);
|
||||
// 1.2 如果被 CrmReceivablePlanDO 所使用,则不允许删除
|
||||
if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
|
||||
throw exception(RECEIVABLE_DELETE_FAIL);
|
||||
}
|
||||
// 1.3 审批通过时,不允许删除
|
||||
if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
|
||||
throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE);
|
||||
}
|
||||
|
||||
// 2.1 删除回款
|
||||
receivableMapper.deleteById(id);
|
||||
// 2.2 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("receivable", receivable);
|
||||
LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = CRM_RECEIVABLE_SUBMIT_SUCCESS)
|
||||
public void submitReceivable(Long id, Long userId) {
|
||||
// 1. 校验回款是否在审批
|
||||
CrmReceivableDO receivable = validateReceivableExists(id);
|
||||
if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
|
||||
throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
|
||||
}
|
||||
|
||||
// 2. 创建回款审批流程实例
|
||||
String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
|
||||
.setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
|
||||
|
||||
// 3. 更新回款工作流编号
|
||||
receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId)
|
||||
.setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
|
||||
|
||||
// 4. 记录日志
|
||||
LogRecordContext.putVariable("receivableNo", receivable.getNo());
|
||||
}
|
||||
|
||||
private CrmReceivableDO validateReceivableExists(Long id) {
|
||||
CrmReceivableDO receivable = receivableMapper.selectById(id);
|
||||
if (receivable == null) {
|
||||
throw exception(RECEIVABLE_NOT_EXISTS);
|
||||
}
|
||||
return receivable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmReceivableDO getReceivable(Long id) {
|
||||
return receivableMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmReceivableDO> getReceivableList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return receivableMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId) {
|
||||
return receivableMapper.selectPage(pageReqVO, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
|
||||
public PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) {
|
||||
return receivableMapper.selectPageByCustomerId(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAuditReceivableCount(Long userId) {
|
||||
return receivableMapper.selectCountByAudit(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds) {
|
||||
return receivableMapper.selectReceivablePriceMapByContractId(contractIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getReceivableCountByContractId(Long contractId) {
|
||||
return receivableMapper.selectCountByContractId(contractId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
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);
|
||||
|
||||
}
|
||||
|
|
@ -1,323 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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);
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
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);
|
||||
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
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,80 +0,0 @@
|
|||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* CRM 排行榜统计 Service 接口
|
||||
*
|
||||
* @author anhaohao
|
||||
*/
|
||||
public interface CrmStatisticsRankService {
|
||||
|
||||
/**
|
||||
* 获得合同金额排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 合同金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得回款金额排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 回款金额排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得签约合同数量排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 签约合同数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得产品销量排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 产品销量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得新增客户数排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 新增客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得联系人数量排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 联系人数量排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得跟进次数排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 跟进次数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
/**
|
||||
* 获得跟进客户数排行榜
|
||||
*
|
||||
* @param rankReqVO 排行参数
|
||||
* @return 跟进客户数排行榜
|
||||
*/
|
||||
List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
|
||||
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
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.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;
|
||||
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.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
/**
|
||||
* CRM 排行榜统计 Service 实现类
|
||||
*
|
||||
* @author anhaohao
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmStatisticsRankServiceImpl implements CrmStatisticsRankService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsRankMapper rankMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectContractPriceRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectReceivablePriceRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectContractCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectProductSalesRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectCustomerCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectContactsCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectFollowCountRank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
|
||||
return getRank(rankReqVO, rankMapper::selectFollowCustomerCountRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得排行版数据
|
||||
*
|
||||
* @param rankReqVO 参数
|
||||
* @param rankFunction 排行榜方法
|
||||
* @return 排行版数据
|
||||
*/
|
||||
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);
|
||||
if (CollUtil.isEmpty(ranks)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ranks.sort(Comparator.comparing(CrmStatisticsRankRespVO::getCount).reversed());
|
||||
// 3. 拼接用户信息
|
||||
appendUserInfo(ranks);
|
||||
return ranks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接用户信息(昵称、部门)
|
||||
*
|
||||
* @param ranks 排行榜数据
|
||||
*/
|
||||
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());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> rank.setDeptName(dept.getName()));
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得部门下的用户编号数组,包括子部门的
|
||||
*
|
||||
* @param deptId 部门编号
|
||||
* @return 用户编号数组
|
||||
*/
|
||||
public List<Long> getUserIds(Long deptId) {
|
||||
// 1. 获得部门列表
|
||||
List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
|
||||
deptIds.add(deptId);
|
||||
// 2. 获得用户编号
|
||||
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?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,118 +0,0 @@
|
|||
<?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.CrmStatisticsRankMapper">
|
||||
|
||||
<select id="selectContractPriceRank"
|
||||
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 = ${@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="selectReceivablePriceRank"
|
||||
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 = ${@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="selectContractCountRank"
|
||||
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 = ${@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="selectProductSalesRank"
|
||||
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.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}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectContactsCountRank"
|
||||
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}
|
||||
GROUP BY owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectFollowCountRank"
|
||||
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}
|
||||
GROUP BY cc.owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectFollowCustomerCountRank"
|
||||
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}
|
||||
GROUP BY cc.owner_user_id
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
||||
spring:
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 地址
|
||||
port: 16379 # 端口(单元测试,使用 16379 端口)
|
||||
database: 0 # 数据库索引
|
||||
|
||||
mybatis-plus:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.finance;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;
|
||||
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 结算账户")
|
||||
@RestController
|
||||
@RequestMapping("/erp/account")
|
||||
@Validated
|
||||
public class ErpAccountController {
|
||||
|
||||
@Resource
|
||||
private ErpAccountService accountService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建结算账户")
|
||||
@PreAuthorize("@ss.hasPermission('erp:account:create')")
|
||||
public CommonResult<Long> createAccount(@Valid @RequestBody ErpAccountSaveReqVO createReqVO) {
|
||||
return success(accountService.createAccount(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新结算账户")
|
||||
@PreAuthorize("@ss.hasPermission('erp:account:update')")
|
||||
public CommonResult<Boolean> updateAccount(@Valid @RequestBody ErpAccountSaveReqVO updateReqVO) {
|
||||
accountService.updateAccount(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-default-status")
|
||||
@Operation(summary = "更新结算账户默认状态")
|
||||
@Parameters({
|
||||
@Parameter(name = "id", description = "编号", required = true),
|
||||
@Parameter(name = "status", description = "状态", required = true)
|
||||
})
|
||||
public CommonResult<Boolean> updateAccountDefaultStatus(@RequestParam("id") Long id,
|
||||
@RequestParam("defaultStatus") Boolean defaultStatus) {
|
||||
accountService.updateAccountDefaultStatus(id, defaultStatus);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除结算账户")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:account:delete')")
|
||||
public CommonResult<Boolean> deleteAccount(@RequestParam("id") Long id) {
|
||||
accountService.deleteAccount(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得结算账户")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:account:query')")
|
||||
public CommonResult<ErpAccountRespVO> getAccount(@RequestParam("id") Long id) {
|
||||
ErpAccountDO account = accountService.getAccount(id);
|
||||
return success(BeanUtils.toBean(account, ErpAccountRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得结算账户精简列表", description = "只包含被开启的结算账户,主要用于前端的下拉选项")
|
||||
public CommonResult<List<ErpAccountRespVO>> getWarehouseSimpleList() {
|
||||
List<ErpAccountDO> list = accountService.getAccountListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(list, account -> new ErpAccountRespVO().setId(account.getId())
|
||||
.setName(account.getName()).setDefaultStatus(account.getDefaultStatus())));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得结算账户分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:account:query')")
|
||||
public CommonResult<PageResult<ErpAccountRespVO>> getAccountPage(@Valid ErpAccountPageReqVO pageReqVO) {
|
||||
PageResult<ErpAccountDO> pageResult = accountService.getAccountPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ErpAccountRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出结算账户 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:account:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportAccountExcel(@Valid ErpAccountPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ErpAccountDO> list = accountService.getAccountPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "结算账户.xls", "数据", ErpAccountRespVO.class,
|
||||
BeanUtils.toBean(list, ErpAccountRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.finance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;
|
||||
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
|
||||
import cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService;
|
||||
import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 付款单")
|
||||
@RestController
|
||||
@RequestMapping("/erp/finance-payment")
|
||||
@Validated
|
||||
public class ErpFinancePaymentController {
|
||||
|
||||
@Resource
|
||||
private ErpFinancePaymentService financePaymentService;
|
||||
@Resource
|
||||
private ErpSupplierService supplierService;
|
||||
@Resource
|
||||
private ErpAccountService accountService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建付款单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:create')")
|
||||
public CommonResult<Long> createFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO createReqVO) {
|
||||
return success(financePaymentService.createFinancePayment(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新付款单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:update')")
|
||||
public CommonResult<Boolean> updateFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO updateReqVO) {
|
||||
financePaymentService.updateFinancePayment(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新付款单的状态")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:update-status')")
|
||||
public CommonResult<Boolean> updateFinancePaymentStatus(@RequestParam("id") Long id,
|
||||
@RequestParam("status") Integer status) {
|
||||
financePaymentService.updateFinancePaymentStatus(id, status);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除付款单")
|
||||
@Parameter(name = "ids", description = "编号数组", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:delete')")
|
||||
public CommonResult<Boolean> deleteFinancePayment(@RequestParam("ids") List<Long> ids) {
|
||||
financePaymentService.deleteFinancePayment(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得付款单")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:query')")
|
||||
public CommonResult<ErpFinancePaymentRespVO> getFinancePayment(@RequestParam("id") Long id) {
|
||||
ErpFinancePaymentDO payment = financePaymentService.getFinancePayment(id);
|
||||
if (payment == null) {
|
||||
return success(null);
|
||||
}
|
||||
List<ErpFinancePaymentItemDO> paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentId(id);
|
||||
return success(BeanUtils.toBean(payment, ErpFinancePaymentRespVO.class, financePaymentVO ->
|
||||
financePaymentVO.setItems(BeanUtils.toBean(paymentItemList, ErpFinancePaymentRespVO.Item.class))));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得付款单分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:query')")
|
||||
public CommonResult<PageResult<ErpFinancePaymentRespVO>> getFinancePaymentPage(@Valid ErpFinancePaymentPageReqVO pageReqVO) {
|
||||
PageResult<ErpFinancePaymentDO> pageResult = financePaymentService.getFinancePaymentPage(pageReqVO);
|
||||
return success(buildFinancePaymentVOPageResult(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出付款单 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-payment:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportFinancePaymentExcel(@Valid ErpFinancePaymentPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ErpFinancePaymentRespVO> list = buildFinancePaymentVOPageResult(financePaymentService.getFinancePaymentPage(pageReqVO)).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "付款单.xls", "数据", ErpFinancePaymentRespVO.class, list);
|
||||
}
|
||||
|
||||
private PageResult<ErpFinancePaymentRespVO> buildFinancePaymentVOPageResult(PageResult<ErpFinancePaymentDO> pageResult) {
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return PageResult.empty(pageResult.getTotal());
|
||||
}
|
||||
// 1.1 付款项
|
||||
List<ErpFinancePaymentItemDO> paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentIds(
|
||||
convertSet(pageResult.getList(), ErpFinancePaymentDO::getId));
|
||||
Map<Long, List<ErpFinancePaymentItemDO>> financePaymentItemMap = convertMultiMap(paymentItemList, ErpFinancePaymentItemDO::getPaymentId);
|
||||
// 1.2 供应商信息
|
||||
Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(
|
||||
convertSet(pageResult.getList(), ErpFinancePaymentDO::getSupplierId));
|
||||
// 1.3 结算账户信息
|
||||
Map<Long, ErpAccountDO> accountMap = accountService.getAccountMap(
|
||||
convertSet(pageResult.getList(), ErpFinancePaymentDO::getAccountId));
|
||||
// 1.4 管理员信息
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId())));
|
||||
// 2. 开始拼接
|
||||
return BeanUtils.toBean(pageResult, ErpFinancePaymentRespVO.class, payment -> {
|
||||
payment.setItems(BeanUtils.toBean(financePaymentItemMap.get(payment.getId()), ErpFinancePaymentRespVO.Item.class));
|
||||
MapUtils.findAndThen(supplierMap, payment.getSupplierId(), supplier -> payment.setSupplierName(supplier.getName()));
|
||||
MapUtils.findAndThen(accountMap, payment.getAccountId(), account -> payment.setAccountName(account.getName()));
|
||||
MapUtils.findAndThen(userMap, Long.parseLong(payment.getCreator()), user -> payment.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, payment.getFinanceUserId(), user -> payment.setFinanceUserName(user.getNickname()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.finance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
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.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;
|
||||
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
|
||||
import cn.iocoder.yudao.module.erp.service.finance.ErpFinanceReceiptService;
|
||||
import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 收款单")
|
||||
@RestController
|
||||
@RequestMapping("/erp/finance-receipt")
|
||||
@Validated
|
||||
public class ErpFinanceReceiptController {
|
||||
|
||||
@Resource
|
||||
private ErpFinanceReceiptService financeReceiptService;
|
||||
@Resource
|
||||
private ErpCustomerService customerService;
|
||||
@Resource
|
||||
private ErpAccountService accountService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建收款单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:create')")
|
||||
public CommonResult<Long> createFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO createReqVO) {
|
||||
return success(financeReceiptService.createFinanceReceipt(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新收款单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:update')")
|
||||
public CommonResult<Boolean> updateFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO updateReqVO) {
|
||||
financeReceiptService.updateFinanceReceipt(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新收款单的状态")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:update-status')")
|
||||
public CommonResult<Boolean> updateFinanceReceiptStatus(@RequestParam("id") Long id,
|
||||
@RequestParam("status") Integer status) {
|
||||
financeReceiptService.updateFinanceReceiptStatus(id, status);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除收款单")
|
||||
@Parameter(name = "ids", description = "编号数组", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:delete')")
|
||||
public CommonResult<Boolean> deleteFinanceReceipt(@RequestParam("ids") List<Long> ids) {
|
||||
financeReceiptService.deleteFinanceReceipt(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得收款单")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:query')")
|
||||
public CommonResult<ErpFinanceReceiptRespVO> getFinanceReceipt(@RequestParam("id") Long id) {
|
||||
ErpFinanceReceiptDO receipt = financeReceiptService.getFinanceReceipt(id);
|
||||
if (receipt == null) {
|
||||
return success(null);
|
||||
}
|
||||
List<ErpFinanceReceiptItemDO> receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptId(id);
|
||||
return success(BeanUtils.toBean(receipt, ErpFinanceReceiptRespVO.class, financeReceiptVO ->
|
||||
financeReceiptVO.setItems(BeanUtils.toBean(receiptItemList, ErpFinanceReceiptRespVO.Item.class))));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得收款单分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:query')")
|
||||
public CommonResult<PageResult<ErpFinanceReceiptRespVO>> getFinanceReceiptPage(@Valid ErpFinanceReceiptPageReqVO pageReqVO) {
|
||||
PageResult<ErpFinanceReceiptDO> pageResult = financeReceiptService.getFinanceReceiptPage(pageReqVO);
|
||||
return success(buildFinanceReceiptVOPageResult(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出收款单 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportFinanceReceiptExcel(@Valid ErpFinanceReceiptPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ErpFinanceReceiptRespVO> list = buildFinanceReceiptVOPageResult(financeReceiptService.getFinanceReceiptPage(pageReqVO)).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "收款单.xls", "数据", ErpFinanceReceiptRespVO.class, list);
|
||||
}
|
||||
|
||||
private PageResult<ErpFinanceReceiptRespVO> buildFinanceReceiptVOPageResult(PageResult<ErpFinanceReceiptDO> pageResult) {
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return PageResult.empty(pageResult.getTotal());
|
||||
}
|
||||
// 1.1 收款项
|
||||
List<ErpFinanceReceiptItemDO> receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptIds(
|
||||
convertSet(pageResult.getList(), ErpFinanceReceiptDO::getId));
|
||||
Map<Long, List<ErpFinanceReceiptItemDO>> financeReceiptItemMap = convertMultiMap(receiptItemList, ErpFinanceReceiptItemDO::getReceiptId);
|
||||
// 1.2 客户信息
|
||||
Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(pageResult.getList(), ErpFinanceReceiptDO::getCustomerId));
|
||||
// 1.3 结算账户信息
|
||||
Map<Long, ErpAccountDO> accountMap = accountService.getAccountMap(
|
||||
convertSet(pageResult.getList(), ErpFinanceReceiptDO::getAccountId));
|
||||
// 1.4 管理员信息
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId())));
|
||||
// 2. 开始拼接
|
||||
return BeanUtils.toBean(pageResult, ErpFinanceReceiptRespVO.class, receipt -> {
|
||||
receipt.setItems(BeanUtils.toBean(financeReceiptItemMap.get(receipt.getId()), ErpFinanceReceiptRespVO.Item.class));
|
||||
MapUtils.findAndThen(customerMap, receipt.getCustomerId(), customer -> receipt.setCustomerName(customer.getName()));
|
||||
MapUtils.findAndThen(accountMap, receipt.getAccountId(), account -> receipt.setAccountName(account.getName()));
|
||||
MapUtils.findAndThen(userMap, Long.parseLong(receipt.getCreator()), user -> receipt.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, receipt.getFinanceUserId(), user -> receipt.setFinanceUserName(user.getNickname()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;
|
||||
import cn.iocoder.yudao.module.erp.service.product.ErpProductCategoryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 产品分类")
|
||||
@RestController
|
||||
@RequestMapping("/erp/product-category")
|
||||
@Validated
|
||||
public class ErpProductCategoryController {
|
||||
|
||||
@Resource
|
||||
private ErpProductCategoryService productCategoryService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建产品分类")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-category:create')")
|
||||
public CommonResult<Long> createProductCategory(@Valid @RequestBody ErpProductCategorySaveReqVO createReqVO) {
|
||||
return success(productCategoryService.createProductCategory(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新产品分类")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-category:update')")
|
||||
public CommonResult<Boolean> updateProductCategory(@Valid @RequestBody ErpProductCategorySaveReqVO updateReqVO) {
|
||||
productCategoryService.updateProductCategory(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除产品分类")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-category:delete')")
|
||||
public CommonResult<Boolean> deleteProductCategory(@RequestParam("id") Long id) {
|
||||
productCategoryService.deleteProductCategory(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得产品分类")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-category:query')")
|
||||
public CommonResult<ErpProductCategoryRespVO> getProductCategory(@RequestParam("id") Long id) {
|
||||
ErpProductCategoryDO category = productCategoryService.getProductCategory(id);
|
||||
return success(BeanUtils.toBean(category, ErpProductCategoryRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得产品分类列表")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-category:query')")
|
||||
public CommonResult<List<ErpProductCategoryRespVO>> getProductCategoryList(@Valid ErpProductCategoryListReqVO listReqVO) {
|
||||
List<ErpProductCategoryDO> list = productCategoryService.getProductCategoryList(listReqVO);
|
||||
return success(BeanUtils.toBean(list, ErpProductCategoryRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得产品分类精简列表", description = "只包含被开启的分类,主要用于前端的下拉选项")
|
||||
public CommonResult<List<ErpProductCategoryRespVO>> getProductCategorySimpleList() {
|
||||
List<ErpProductCategoryDO> list = productCategoryService.getProductCategoryList(
|
||||
new ErpProductCategoryListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
return success(convertList(list, category -> new ErpProductCategoryRespVO()
|
||||
.setId(category.getId()).setName(category.getName()).setParentId(category.getParentId())));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出产品分类 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-category:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportProductCategoryExcel(@Valid ErpProductCategoryListReqVO listReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<ErpProductCategoryDO> list = productCategoryService.getProductCategoryList(listReqVO);
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "产品分类.xls", "数据", ErpProductCategoryRespVO.class,
|
||||
BeanUtils.toBean(list, ErpProductCategoryRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
|
||||
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 产品")
|
||||
@RestController
|
||||
@RequestMapping("/erp/product")
|
||||
@Validated
|
||||
public class ErpProductController {
|
||||
|
||||
@Resource
|
||||
private ErpProductService productService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建产品")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product:create')")
|
||||
public CommonResult<Long> createProduct(@Valid @RequestBody ProductSaveReqVO createReqVO) {
|
||||
return success(productService.createProduct(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新产品")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product:update')")
|
||||
public CommonResult<Boolean> updateProduct(@Valid @RequestBody ProductSaveReqVO updateReqVO) {
|
||||
productService.updateProduct(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除产品")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:product:delete')")
|
||||
public CommonResult<Boolean> deleteProduct(@RequestParam("id") Long id) {
|
||||
productService.deleteProduct(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得产品")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product:query')")
|
||||
public CommonResult<ErpProductRespVO> getProduct(@RequestParam("id") Long id) {
|
||||
ErpProductDO product = productService.getProduct(id);
|
||||
return success(BeanUtils.toBean(product, ErpProductRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得产品分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product:query')")
|
||||
public CommonResult<PageResult<ErpProductRespVO>> getProductPage(@Valid ErpProductPageReqVO pageReqVO) {
|
||||
return success(productService.getProductVOPage(pageReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得产品精简列表", description = "只包含被开启的产品,主要用于前端的下拉选项")
|
||||
public CommonResult<List<ErpProductRespVO>> getProductSimpleList() {
|
||||
List<ErpProductRespVO> list = productService.getProductVOListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(list, product -> new ErpProductRespVO().setId(product.getId())
|
||||
.setName(product.getName()).setBarCode(product.getBarCode())
|
||||
.setCategoryId(product.getCategoryId()).setCategoryName(product.getCategoryName())
|
||||
.setUnitId(product.getUnitId()).setUnitName(product.getUnitName())
|
||||
.setPurchasePrice(product.getPurchasePrice()).setSalePrice(product.getSalePrice()).setMinPrice(product.getMinPrice())));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出产品 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportProductExcel(@Valid ErpProductPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
PageResult<ErpProductRespVO> pageResult = productService.getProductVOPage(pageReqVO);
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "产品.xls", "数据", ErpProductRespVO.class,
|
||||
pageResult.getList());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
|
||||
import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 产品单位")
|
||||
@RestController
|
||||
@RequestMapping("/erp/product-unit")
|
||||
@Validated
|
||||
public class ErpProductUnitController {
|
||||
|
||||
@Resource
|
||||
private ErpProductUnitService productUnitService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建产品单位")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-unit:create')")
|
||||
public CommonResult<Long> createProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO createReqVO) {
|
||||
return success(productUnitService.createProductUnit(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新产品单位")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-unit:update')")
|
||||
public CommonResult<Boolean> updateProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO updateReqVO) {
|
||||
productUnitService.updateProductUnit(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除产品单位")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-unit:delete')")
|
||||
public CommonResult<Boolean> deleteProductUnit(@RequestParam("id") Long id) {
|
||||
productUnitService.deleteProductUnit(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得产品单位")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-unit:query')")
|
||||
public CommonResult<ErpProductUnitRespVO> getProductUnit(@RequestParam("id") Long id) {
|
||||
ErpProductUnitDO productUnit = productUnitService.getProductUnit(id);
|
||||
return success(BeanUtils.toBean(productUnit, ErpProductUnitRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得产品单位分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-unit:query')")
|
||||
public CommonResult<PageResult<ErpProductUnitRespVO>> getProductUnitPage(@Valid ErpProductUnitPageReqVO pageReqVO) {
|
||||
PageResult<ErpProductUnitDO> pageResult = productUnitService.getProductUnitPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ErpProductUnitRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得产品单位精简列表", description = "只包含被开启的单位,主要用于前端的下拉选项")
|
||||
public CommonResult<List<ErpProductUnitRespVO>> getProductUnitSimpleList() {
|
||||
List<ErpProductUnitDO> list = productUnitService.getProductUnitListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(list, unit -> new ErpProductUnitRespVO().setId(unit.getId()).setName(unit.getName())));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出产品单位 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:product-unit:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportProductUnitExcel(@Valid ErpProductUnitPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ErpProductUnitDO> list = productUnitService.getProductUnitPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "产品单位.xls", "数据", ErpProductUnitRespVO.class,
|
||||
BeanUtils.toBean(list, ErpProductUnitRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
package cn.iocoder.yudao.module.erp.controller.admin.purchase;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInRespVO;
|
||||
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;
|
||||
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
|
||||
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
|
||||
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInService;
|
||||
import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
|
||||
import cn.iocoder.yudao.module.erp.service.stock.ErpStockService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - ERP 采购入库")
|
||||
@RestController
|
||||
@RequestMapping("/erp/purchase-in")
|
||||
@Validated
|
||||
public class ErpPurchaseInController {
|
||||
|
||||
@Resource
|
||||
private ErpPurchaseInService purchaseInService;
|
||||
@Resource
|
||||
private ErpStockService stockService;
|
||||
@Resource
|
||||
private ErpProductService productService;
|
||||
@Resource
|
||||
private ErpSupplierService supplierService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建采购入库")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:create')")
|
||||
public CommonResult<Long> createPurchaseIn(@Valid @RequestBody ErpPurchaseInSaveReqVO createReqVO) {
|
||||
return success(purchaseInService.createPurchaseIn(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新采购入库")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:update')")
|
||||
public CommonResult<Boolean> updatePurchaseIn(@Valid @RequestBody ErpPurchaseInSaveReqVO updateReqVO) {
|
||||
purchaseInService.updatePurchaseIn(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新采购入库的状态")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:update-status')")
|
||||
public CommonResult<Boolean> updatePurchaseInStatus(@RequestParam("id") Long id,
|
||||
@RequestParam("status") Integer status) {
|
||||
purchaseInService.updatePurchaseInStatus(id, status);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除采购入库")
|
||||
@Parameter(name = "ids", description = "编号数组", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:delete')")
|
||||
public CommonResult<Boolean> deletePurchaseIn(@RequestParam("ids") List<Long> ids) {
|
||||
purchaseInService.deletePurchaseIn(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得采购入库")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:query')")
|
||||
public CommonResult<ErpPurchaseInRespVO> getPurchaseIn(@RequestParam("id") Long id) {
|
||||
ErpPurchaseInDO purchaseIn = purchaseInService.getPurchaseIn(id);
|
||||
if (purchaseIn == null) {
|
||||
return success(null);
|
||||
}
|
||||
List<ErpPurchaseInItemDO> purchaseInItemList = purchaseInService.getPurchaseInItemListByInId(id);
|
||||
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(
|
||||
convertSet(purchaseInItemList, ErpPurchaseInItemDO::getProductId));
|
||||
return success(BeanUtils.toBean(purchaseIn, ErpPurchaseInRespVO.class, purchaseInVO ->
|
||||
purchaseInVO.setItems(BeanUtils.toBean(purchaseInItemList, ErpPurchaseInRespVO.Item.class, item -> {
|
||||
ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId());
|
||||
item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO);
|
||||
MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())
|
||||
.setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));
|
||||
}))));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得采购入库分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:query')")
|
||||
public CommonResult<PageResult<ErpPurchaseInRespVO>> getPurchaseInPage(@Valid ErpPurchaseInPageReqVO pageReqVO) {
|
||||
PageResult<ErpPurchaseInDO> pageResult = purchaseInService.getPurchaseInPage(pageReqVO);
|
||||
return success(buildPurchaseInVOPageResult(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出采购入库 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-in:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportPurchaseInExcel(@Valid ErpPurchaseInPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ErpPurchaseInRespVO> list = buildPurchaseInVOPageResult(purchaseInService.getPurchaseInPage(pageReqVO)).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "采购入库.xls", "数据", ErpPurchaseInRespVO.class, list);
|
||||
}
|
||||
|
||||
private PageResult<ErpPurchaseInRespVO> buildPurchaseInVOPageResult(PageResult<ErpPurchaseInDO> pageResult) {
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return PageResult.empty(pageResult.getTotal());
|
||||
}
|
||||
// 1.1 入库项
|
||||
List<ErpPurchaseInItemDO> purchaseInItemList = purchaseInService.getPurchaseInItemListByInIds(
|
||||
convertSet(pageResult.getList(), ErpPurchaseInDO::getId));
|
||||
Map<Long, List<ErpPurchaseInItemDO>> purchaseInItemMap = convertMultiMap(purchaseInItemList, ErpPurchaseInItemDO::getInId);
|
||||
// 1.2 产品信息
|
||||
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(
|
||||
convertSet(purchaseInItemList, ErpPurchaseInItemDO::getProductId));
|
||||
// 1.3 供应商信息
|
||||
Map<Long, ErpSupplierDO> supplierMap = supplierService.getSupplierMap(
|
||||
convertSet(pageResult.getList(), ErpPurchaseInDO::getSupplierId));
|
||||
// 1.4 管理员信息
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(pageResult.getList(), purchaseIn -> Long.parseLong(purchaseIn.getCreator())));
|
||||
// 2. 开始拼接
|
||||
return BeanUtils.toBean(pageResult, ErpPurchaseInRespVO.class, purchaseIn -> {
|
||||
purchaseIn.setItems(BeanUtils.toBean(purchaseInItemMap.get(purchaseIn.getId()), ErpPurchaseInRespVO.Item.class,
|
||||
item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())
|
||||
.setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));
|
||||
purchaseIn.setProductNames(CollUtil.join(purchaseIn.getItems(), ",", ErpPurchaseInRespVO.Item::getProductName));
|
||||
MapUtils.findAndThen(supplierMap, purchaseIn.getSupplierId(), supplier -> purchaseIn.setSupplierName(supplier.getName()));
|
||||
MapUtils.findAndThen(userMap, Long.parseLong(purchaseIn.getCreator()), user -> purchaseIn.setCreatorName(user.getNickname()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue