Merge branch 'develop' of https://gitee.com/puhui999/ruoyi-vue-pro into develop

# Conflicts:
#	yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java
This commit is contained in:
YunaiV 2024-01-13 09:23:56 +08:00
commit d80406acbb
56 changed files with 955 additions and 313 deletions

View File

@ -122,7 +122,12 @@ public class LocalDateTimeUtils {
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
}
// TODO @puhui999加下注释哈
/**
* 获取指定日期到现在过了几天如果指定日期在当前日期之后获取结果为负
*
* @param dateTime 日期
* @return 相差天数
*/
public static Long between(LocalDateTime dateTime) {
return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);
}

View File

@ -28,6 +28,12 @@
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-api</artifactId>
<version>${revision}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -44,6 +44,7 @@ public interface ErrorCodeConstants {
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, "删除客户失败,有关联{}");
// ========== 权限管理 1_020_007_000 ==========
ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
@ -54,6 +55,7 @@ public interface ErrorCodeConstants {
ErrorCode CRM_PERMISSION_DELETE_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_005, "删除数据权限失败,原因:存在负责人");
ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限");
ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人");
ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限");
// ========== 产品 1_020_008_000 ==========
ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
@ -77,4 +79,7 @@ public interface ErrorCodeConstants {
// ========== 客户公海规则设置 1_020_012_000 ==========
ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_000, "客户限制配置不存在");
// ========== 跟进记录 1_020_013_000 ==========
ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在");
}

View File

@ -1,5 +1,8 @@
package cn.iocoder.yudao.module.crm.enums;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTRACT_BY_ID;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
/**
* CRM 操作日志枚举
* 目的统一管理也减少 Service 里各种复杂字符串
@ -22,9 +25,9 @@ public interface LogRecordConstants {
String CRM_CUSTOMER_DELETE_SUB_TYPE = "删除客户";
String CRM_CUSTOMER_DELETE_SUCCESS = "删除了客户【{{#customerName}}】";
String CRM_CUSTOMER_TRANSFER_SUB_TYPE = "转移客户";
String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#crmCustomer.lockStatus ? '解锁客户' : '锁定客户'}}";
String CRM_CUSTOMER_LOCK_SUCCESS = "{{#crmCustomer.lockStatus ? '将客户【' + #crmCustomer.name + '】解锁' : '将客户【' + #crmCustomer.name + '】锁定'}}";
String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#customer.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#customer.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#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 ? '分配客户' : '领取客户'}}";
@ -49,14 +52,38 @@ public interface LogRecordConstants {
// ======================= 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}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#contact.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
// ======================= CRM_BUSINESS 商机 =======================
String CRM_BUSINESS_TYPE = "CRM 商机";
String CRM_BUSINESS_CREATE_SUB_TYPE = "创建商机";
String CRM_BUSINESS_CREATE_SUCCESS = "创建了商机{{#business.name}}";
String CRM_BUSINESS_UPDATE_SUB_TYPE = "更新商机";
String CRM_BUSINESS_UPDATE_SUCCESS = "更新了商机【{{#businessName}}】: {_DIFF{#updateReqVO}}";
String CRM_BUSINESS_DELETE_SUB_TYPE = "删除商机";
String CRM_BUSINESS_DELETE_SUCCESS = "删除了商机【{{#businessName}}】";
String CRM_BUSINESS_TRANSFER_SUB_TYPE = "转移商机";
String CRM_BUSINESS_TRANSFER_SUCCESS = "将商机【{{#business.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#business.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
// ======================= CRM_CONTRACT 合同 =======================
String CRM_CONTRACT_TYPE = "CRM 合同";
String CRM_CONTRACT_CREATE_SUB_TYPE = "创建合同";
String CRM_CONTRACT_CREATE_SUCCESS = "创建了合同{{#contract.name}}";
String CRM_CONTRACT_UPDATE_SUB_TYPE = "更新合同";
String CRM_CONTRACT_UPDATE_SUCCESS = "更新了合同【{{#contractName}}】: {_DIFF{#updateReqVO}}";
String CRM_CONTRACT_DELETE_SUB_TYPE = "删除合同";
String CRM_CONTRACT_DELETE_SUCCESS = "删除了合同【{{#contractName}}】";
String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移合同";
String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#contract.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
// ======================= CRM_PRODUCT 产品 =======================
@ -79,9 +106,21 @@ public interface LogRecordConstants {
// ======================= CRM_RECEIVABLE 回款 =======================
String CRM_RECEIVABLE_TYPE = "CRM 回款";
String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款";
String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款";
String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
// ======================= CRM_RECEIVABLE_PLAN 回款计划 =======================
String CRM_RECEIVABLE_PLAN_TYPE = "CRM 回款计划";
String CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE = "创建回款计划";
String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = "创建了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
String CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE = "更新回款计划";
String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = "更新了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}";
String CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE = "删除回款计划";
String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = "删除了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.crm.enums.operatelog;
/**
* functionName 常量枚举
* 方便别的模块调用
*
* @author HUIHUI
*/
public interface CrmParseFunctionNameConstants {
String GET_CONTACT_BY_ID = "getContactById"; // 获取联系人信息
String GET_CUSTOMER_BY_ID = "getCustomerById"; // 获取客户信息
String GET_CUSTOMER_INDUSTRY = "getCustomerIndustry"; // 获取客户行业信息
String GET_CUSTOMER_LEVEL = "getCustomerLevel"; // 获取客户级别
String GET_CUSTOMER_SOURCE = "getCustomerSource"; // 获取客户来源
String GET_CONTRACT_BY_ID = "getContractById"; // 获取合同信息
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.framework.common.validation.Telephone;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.*;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
@ -14,10 +13,13 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTACT_BY_ID;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_BY_ID;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.*;
@Schema(description = "管理后台 - CRM 联系人创建/更新 Request VO")
@Data
public class CrmContactSaveReqVO {
public class CrmContactSaveReqVO {
@Schema(description = "主键", example = "3167")
private Long id;
@ -28,11 +30,11 @@ public class CrmContactSaveReqVO {
private String name;
@Schema(description = "客户编号", example = "10795")
@DiffLogField(name = "姓名", function = CrmCustomerParseFunction.NAME)
@DiffLogField(name = "姓名", function = GET_CUSTOMER_BY_ID)
private Long customerId;
@Schema(description = "性别")
@DiffLogField(name = "性别", function = CrmSexParseFunction.NAME)
@DiffLogField(name = "性别", function = GET_SEX)
private Integer sex;
@Schema(description = "职位")
@ -40,11 +42,11 @@ public class CrmContactSaveReqVO {
private String post;
@Schema(description = "是否关键决策人")
@DiffLogField(name = "关键决策人", function = CrmBooleanParseFunction.NAME)
@DiffLogField(name = "关键决策人", function = GET_BOOLEAN)
private Boolean master;
@Schema(description = "直属上级", example = "23457")
@DiffLogField(name = "直属上级", function = CrmContactParseFunction.NAME)
@DiffLogField(name = "直属上级", function = GET_CONTACT_BY_ID)
private Long parentId;
@Schema(description = "手机号", example = "1387171766")
@ -71,7 +73,7 @@ public class CrmContactSaveReqVO {
private String email;
@Schema(description = "地区编号", example = "20158")
@DiffLogField(name = "所在地", function = "getAreaById")
@DiffLogField(name = "所在地", function = GET_AREA)
private Integer areaId;
@Schema(description = "地址")
@ -84,7 +86,7 @@ public class CrmContactSaveReqVO {
@Schema(description = "负责人用户编号", example = "14334")
@NotNull(message = "负责人不能为空")
@DiffLogField(name = "负责人", function = CrmSysUserParseFunction.NAME)
@DiffLogField(name = "负责人", function = GET_ADMIN_USER_BY_ID)
private Long ownerUserId;
@Schema(description = "最后跟进时间")

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.crm.controller.admin.customer;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
@ -32,6 +34,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
@ -112,32 +115,40 @@ public class CrmCustomerController {
}
// 2. 拼接数据
Map<Long, Long> poolDayMap = getPoolDayMap(pageResult); // 距离进入公海的时间
Map<Long, Long> poolDayMap = null;
if (ObjUtil.notEqual(pageVO.getPool(), Boolean.TRUE)) {
poolDayMap = getPoolDayMap(pageResult.getList()); // 距离进入公海的时间
}
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId())));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap));
}
// TODO @puhui999加下注释哈
private Map<Long, Long> getPoolDayMap(PageResult<CrmCustomerDO> pageResult) {
Map<Long, Long> poolDayMap = null;
/**
* 获取距离进入公海的时间
*
* @param customerList 客户列表
* @return Map<key 客户编号, value 距离进入公海的时间>
*/
private Map<Long, Long> getPoolDayMap(List<CrmCustomerDO> customerList) {
CrmCustomerPoolConfigDO customerPoolConfig = customerPoolConfigService.getCustomerPoolConfig();
// TODO @puhui999if return 减少括号
if (customerPoolConfig != null && customerPoolConfig.getEnabled()) { // 有公海配置的情况
// TODO @puhui999item 改成 customer 更好容易理解
poolDayMap = convertMap(pageResult.getList(), CrmCustomerDO::getId, item -> {
long dealExpireDay = 0;
if (!item.getDealStatus()) { // 检查是否成交
dealExpireDay = customerPoolConfig.getDealExpireDays() - LocalDateTimeUtils.between(item.getCreateTime());
}
// TODO @puhui999需要考虑 contactLastTime 为空的情况哈
long contactExpireDay = customerPoolConfig.getContactExpireDays() - LocalDateTimeUtils.between(item.getContactLastTime());
return dealExpireDay == 0 ? contactExpireDay : Math.min(dealExpireDay, contactExpireDay);
});
// TODO @puhui999需要考虑 lock 的情况么
if (customerPoolConfig == null || !customerPoolConfig.getEnabled()) {
return MapUtil.empty();
}
return poolDayMap;
// TODO @puhui999需要考虑 lock 的情况么 回复锁定正常显示距离进入公海的时间有个提示
return convertMap(customerList, CrmCustomerDO::getId, customer -> {
long dealExpireDay = 0;
if (!customer.getDealStatus()) { // 检查是否成交
dealExpireDay = customerPoolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime());
}
LocalDateTime lastTime = customer.getContactLastTime() != null ? customer.getContactLastTime() : customer.getCreateTime();
long contactExpireDay = customerPoolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
if (contactExpireDay < 0) {
contactExpireDay = 0; // 如果为负的话重置为零
}
return Math.min(dealExpireDay, contactExpireDay);
});
}
@GetMapping(value = "/list-all-simple")

View File

@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.crm.controller.admin.customer;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
import io.swagger.v3.oas.annotations.Operation;
@ -30,7 +30,7 @@ public class CrmCustomerPoolConfigController {
@PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')")
public CommonResult<CrmCustomerPoolConfigRespVO> getCustomerPoolConfig() {
CrmCustomerPoolConfigDO customerPoolConfig = customerPoolConfigService.getCustomerPoolConfig();
return success(CrmCustomerConvert.INSTANCE.convert(customerPoolConfig));
return success(BeanUtils.toBean(customerPoolConfig, CrmCustomerPoolConfigRespVO.class));
}
@PutMapping("/save")

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@ -11,7 +12,7 @@ import java.util.List;
public class CrmCustomerDistributeReqVO {
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024]")
@NotNull(message = "客户编号不能为空") // TODO @puhui999list @NotEmpty
@NotEmpty(message = "客户编号不能为空")
private List<Long> ids;
@Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")

View File

@ -5,9 +5,6 @@ import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.framework.common.validation.Telephone;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmIndustryParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmLevelParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmSourceParseFunction;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
@ -20,6 +17,8 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.*;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_AREA;
@Schema(description = "管理后台 - CRM 客户新增/修改 Request VO")
@Data
@ -34,17 +33,17 @@ public class CrmCustomerSaveReqVO {
private String name;
@Schema(description = "所属行业", example = "1")
@DiffLogField(name = "所属行业", function = CrmIndustryParseFunction.NAME)
@DiffLogField(name = "所属行业", function = GET_CUSTOMER_INDUSTRY)
@DictFormat(CRM_CUSTOMER_INDUSTRY)
private Integer industryId;
@Schema(description = "客户等级", example = "2")
@DiffLogField(name = "客户等级", function = CrmLevelParseFunction.NAME)
@DiffLogField(name = "客户等级", function = GET_CUSTOMER_LEVEL)
@InEnum(CrmCustomerLevelEnum.class)
private Integer level;
@Schema(description = "客户来源", example = "3")
@DiffLogField(name = "客户来源", function = CrmSourceParseFunction.NAME)
@DiffLogField(name = "客户来源", function = GET_CUSTOMER_SOURCE)
private Integer source;
@Schema(description = "手机", example = "18000000000")
@ -87,7 +86,7 @@ public class CrmCustomerSaveReqVO {
private String remark;
@Schema(description = "地区编号", example = "20158")
@DiffLogField(name = "地区编号", function = "getAreaById")
@DiffLogField(name = "地区编号", function = GET_AREA)
private Integer areaId;
@Schema(description = "详细地址", example = "北京市海淀区")

View File

@ -7,6 +7,9 @@ import lombok.Data;
import java.util.List;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_DEPT_BY_ID;
@Schema(description = "管理后台 - 客户限制配置创建/更新 Request VO")
@Data
public class CrmCustomerLimitConfigSaveReqVO {
@ -19,13 +22,12 @@ public class CrmCustomerLimitConfigSaveReqVO {
@DiffLogField(name = "规则类型")
private Integer type;
// TODO @puhui999可以把 Function 那的 functionName 搞成 NAME 枚举这里直接引用这样后续改动更方便哈
@Schema(description = "规则适用人群")
@DiffLogField(name = "规则适用人群", function = "getAdminUserById")
@DiffLogField(name = "规则适用人群", function = GET_ADMIN_USER_BY_ID)
private List<Long> userIds;
@Schema(description = "规则适用部门")
@DiffLogField(name = "规则适用部门", function = "getDeptById")
@DiffLogField(name = "规则适用部门", function = GET_DEPT_BY_ID)
private List<Long> deptIds;
@Schema(description = "数量上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384")

View File

@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup;
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.operatelog.core.annotations.OperateLog;
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.followup.CrmFollowUpRecordDO;
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
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.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 跟进记录")
@RestController
@RequestMapping("/crm/follow-up-record")
@Validated
public class CrmFollowUpRecordController {
@Resource
private CrmFollowUpRecordService crmFollowUpRecordService;
@PostMapping("/create")
@Operation(summary = "创建跟进记录")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:create')")
public CommonResult<Long> createFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO createReqVO) {
return success(crmFollowUpRecordService.createFollowUpRecord(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新跟进记录")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:update')")
public CommonResult<Boolean> updateFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO updateReqVO) {
crmFollowUpRecordService.updateFollowUpRecord(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除跟进记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:delete')")
public CommonResult<Boolean> deleteFollowUpRecord(@RequestParam("id") Long id) {
crmFollowUpRecordService.deleteFollowUpRecord(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得跟进记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')")
public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long id) {
CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordService.getFollowUpRecord(id);
return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得跟进记录分页")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')")
public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
PageResult<CrmFollowUpRecordDO> pageResult = crmFollowUpRecordService.getFollowUpRecordPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出跟进记录 Excel")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:export')")
@OperateLog(type = EXPORT)
public void exportFollowUpRecordExcel(@Valid CrmFollowUpRecordPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CrmFollowUpRecordDO> list = crmFollowUpRecordService.getFollowUpRecordPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "跟进记录.xls", "数据", CrmFollowUpRecordRespVO.class,
BeanUtils.toBean(list, CrmFollowUpRecordRespVO.class));
}
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 跟进记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmFollowUpRecordPageReqVO extends PageParam {
@Schema(description = "数据类型", example = "2")
private Integer bizType;
@Schema(description = "数据编号", example = "5564")
private Long bizId;
@Schema(description = "跟进类型", example = "2")
private Integer type;
@Schema(description = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] nextTime;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
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.time.LocalDateTime;
@Schema(description = "管理后台 - 跟进记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmFollowUpRecordRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800")
@ExcelProperty("编号")
private Long id;
@Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("数据类型")
private Integer bizType;
@Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564")
@ExcelProperty("数据编号")
private Long bizId;
@Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty(value = "跟进类型", converter = DictConvert.class)
@DictFormat("crm_follow_up_type") // TODO 代码优化建议设置到对应的 DictTypeConstants 枚举类中
private Integer type;
@Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("跟进内容")
private String content;
@Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("下次联系时间")
private LocalDateTime nextTime;
@Schema(description = "关联的商机编号数组")
@ExcelProperty("关联的商机编号数组")
private String businessIds;
@Schema(description = "关联的联系人编号数组")
@ExcelProperty("关联的联系人编号数组")
private String contactIds;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 跟进记录新增/修改 Request VO")
@Data
public class CrmFollowUpRecordSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800")
private Long id;
@Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "数据类型不能为空")
private Integer bizType;
@Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564")
@NotNull(message = "数据编号不能为空")
private Long bizId;
@Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "跟进类型不能为空")
private Integer type;
@Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "跟进内容不能为空")
private String content;
@Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "下次联系时间不能为空")
private LocalDateTime nextTime;
@Schema(description = "关联的商机编号数组")
private String businessIds;
@Schema(description = "关联的联系人编号数组")
private String contactIds;
}

View File

@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.*;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
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.CrmReceivableUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@ -59,7 +62,7 @@ public class CrmReceivableController {
@Operation(summary = "创建回款")
@PreAuthorize("@ss.hasPermission('crm:receivable:create')")
public CommonResult<Long> createReceivable(@Valid @RequestBody CrmReceivableCreateReqVO createReqVO) {
return success(receivableService.createReceivable(createReqVO));
return success(receivableService.createReceivable(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@ -141,12 +144,4 @@ public class CrmReceivableController {
return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList);
}
@PutMapping("/transfer")
@Operation(summary = "回款转移")
@PreAuthorize("@ss.hasPermission('crm:receivable:update')")
public CommonResult<Boolean> transfer(@Valid @RequestBody CrmReceivableTransferReqVO reqVO) {
receivableService.transferReceivable(reqVO, getLoginUserId());
return success(true);
}
}

View File

@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.*;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
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.CrmReceivablePlanUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@ -148,12 +151,4 @@ public class CrmReceivablePlanController {
return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList);
}
@PutMapping("/transfer")
@Operation(summary = "回款计划转移")
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')")
public CommonResult<Boolean> transfer(@Valid @RequestBody CrmReceivablePlanTransferReqVO reqVO) {
receivablePlanService.transferReceivablePlan(reqVO, getLoginUserId());
return success(true);
}
}

View File

@ -3,13 +3,8 @@ package cn.iocoder.yudao.module.crm.convert.customer;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;
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.dal.dataobject.customer.CrmCustomerPoolConfigDO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@ -31,9 +26,6 @@ public interface CrmCustomerConvert {
CrmCustomerConvert INSTANCE = Mappers.getMapper(CrmCustomerConvert.class);
// TODO @puhui999可以清理掉可以用 BeanUtil 替代的方法哈
CrmCustomerDO convert(CrmCustomerSaveReqVO bean);
CrmCustomerRespVO convert(CrmCustomerDO bean);
/**
@ -74,12 +66,4 @@ public interface CrmCustomerConvert {
return result;
}
CrmCustomerPoolConfigRespVO convert(CrmCustomerPoolConfigDO customerPoolConfig);
CrmCustomerPoolConfigDO convert(CrmCustomerPoolConfigSaveReqVO updateReqVO);
// TODO @min使用 BeanUtils 拷贝哈我们慢慢简单的对象不再直接基于 convert 做啦
@Mapping(ignore = true, target = "id")
CrmCustomerSaveReqVO convert(CrmClueDO bean);
}

View File

@ -3,14 +3,12 @@ package cn.iocoder.yudao.module.crm.convert.customer;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
/**
@ -23,13 +21,8 @@ public interface CrmCustomerLimitConfigConvert {
CrmCustomerLimitConfigConvert INSTANCE = Mappers.getMapper(CrmCustomerLimitConfigConvert.class);
// TODO @puhui999可以把 convert 改成 BeanUtils
CrmCustomerLimitConfigDO convert(CrmCustomerLimitConfigSaveReqVO bean);
CrmCustomerLimitConfigRespVO convert(CrmCustomerLimitConfigDO bean);
List<CrmCustomerLimitConfigRespVO> convertList(List<CrmCustomerLimitConfigDO> list);
PageResult<CrmCustomerLimitConfigRespVO> convertPage(PageResult<CrmCustomerLimitConfigDO> page);
default PageResult<CrmCustomerLimitConfigRespVO> convertPage(PageResult<CrmCustomerLimitConfigDO> pageResult,

View File

@ -105,7 +105,10 @@ public class CrmContactDO extends BaseDO {
* 最后跟进时间
*/
private LocalDateTime contactLastTime;
// TODO @puhui999增加一个字段 contactLastContent最后跟进内容
/**
* 最后跟进内容
*/
private String contactLastContent;
/**
* 下次联系时间
*/

View File

@ -118,7 +118,10 @@ public class CrmCustomerDO extends BaseDO {
* 最后跟进时间
*/
private LocalDateTime contactLastTime;
// TODO @puhui999增加一个字段 contactLastContent最后跟进内容
/**
* 最后跟进内容
*/
private String contactLastContent;
/**
* 下次联系时间
*/

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.followup;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
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.enums.common.CrmBizTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@ -21,7 +23,7 @@ import java.util.List;
*
* @author 芋道源码
*/
@TableName(value = "crm_follow_up_record")
@TableName(value = "crm_follow_up_record", autoResultMap = true)
@KeySequence("crm_follow_up_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ -70,12 +72,14 @@ public class CrmFollowUpRecordDO extends BaseDO {
*
* 关联 {@link CrmBusinessDO#getId()}
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> businessIds;
/**
* 关联的联系人编号数组
*
* 关联 {@link CrmContactDO#getId()}
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> contactIds;
}

View File

@ -28,6 +28,12 @@ public interface CrmContactMapper extends BaseMapperX<CrmContactDO> {
.set(CrmContactDO::getOwnerUserId, ownerUserId));
}
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()) // 指定客户编号

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.dal.mysql.customer;
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.customer.CrmCustomerPoolConfigDO;
import org.apache.ibatis.annotations.Mapper;
@ -11,4 +12,9 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Mapper
public interface CrmCustomerPoolConfigMapper extends BaseMapperX<CrmCustomerPoolConfigDO> {
default CrmCustomerPoolConfigDO selectOne() {
return selectOne(new LambdaQueryWrapperX<CrmCustomerPoolConfigDO>().last("LIMIT 1"));
}
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.crm.dal.mysql.followup;
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.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 跟进记录 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface CrmFollowUpRecordMapper extends BaseMapperX<CrmFollowUpRecordDO> {
default PageResult<CrmFollowUpRecordDO> selectPage(CrmFollowUpRecordPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CrmFollowUpRecordDO>()
.eqIfPresent(CrmFollowUpRecordDO::getBizType, reqVO.getBizType())
.eqIfPresent(CrmFollowUpRecordDO::getBizId, reqVO.getBizId())
.eqIfPresent(CrmFollowUpRecordDO::getType, reqVO.getType())
.betweenIfPresent(CrmFollowUpRecordDO::getNextTime, reqVO.getNextTime())
.betweenIfPresent(CrmFollowUpRecordDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CrmFollowUpRecordDO::getId));
}
}

View File

@ -59,4 +59,11 @@ public interface CrmPermissionMapper extends BaseMapperX<CrmPermissionDO> {
.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));
}
}

View File

@ -8,8 +8,10 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTACT_BY_ID;
/**
* 行业的 {@link IParseFunction} 实现类
* CRM 联系人 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@ -17,8 +19,6 @@ import org.springframework.stereotype.Component;
@Slf4j
public class CrmContactParseFunction implements IParseFunction {
public static final String NAME = "getContactById";
@Resource
private CrmContactService contactService;
@ -29,7 +29,7 @@ public class CrmContactParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_CONTACT_BY_ID;
}
@Override

View File

@ -1,26 +1,26 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import com.mzt.logapi.service.IParseFunction;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTRACT_BY_ID;
/**
* 行业的 {@link IParseFunction} 实现类
* CRM 合同 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Component
@Slf4j
public class CrmSysUserParseFunction implements IParseFunction {
public static final String NAME = "getUserById";
public class CrmContractParseFunction implements IParseFunction {
@Resource
private AdminUserApi adminUserApi;
private CrmContractService contractService;
@Override
public boolean executeBefore() {
@ -29,7 +29,7 @@ public class CrmSysUserParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_CONTRACT_BY_ID;
}
@Override
@ -37,8 +37,8 @@ public class CrmSysUserParseFunction implements IParseFunction {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
AdminUserRespDTO adminUserRespDTO = adminUserApi.getUser(Long.parseLong(value.toString()));
return adminUserRespDTO == null ? "" : adminUserRespDTO.getNickname();
CrmContractDO contract = contractService.getContract(Long.parseLong(value.toString()));
return contract == null ? "" : contract.getName();
}
}

View File

@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_INDUSTRY;
/**
* 行业的 {@link IParseFunction} 实现类
@ -15,9 +16,7 @@ import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_I
*/
@Component
@Slf4j
public class CrmIndustryParseFunction implements IParseFunction {
public static final String NAME = "getIndustryById";
public class CrmCustomerIndustryParseFunction implements IParseFunction {
@Override
public boolean executeBefore() {
@ -26,7 +25,7 @@ public class CrmIndustryParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_CUSTOMER_INDUSTRY;
}
@Override

View File

@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_LEVEL;
/**
* 客户等级的 {@link IParseFunction} 实现类
@ -15,9 +16,7 @@ import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_L
*/
@Component
@Slf4j
public class CrmLevelParseFunction implements IParseFunction {
public static final String NAME = "getLevel";
public class CrmCustomerLevelParseFunction implements IParseFunction {
@Override
public boolean executeBefore() {
@ -26,7 +25,7 @@ public class CrmLevelParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_CUSTOMER_LEVEL;
}
@Override

View File

@ -8,8 +8,10 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_BY_ID;
/**
* 行业的 {@link IParseFunction} 实现类
* CRM 客户 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@ -17,8 +19,6 @@ import org.springframework.stereotype.Component;
@Slf4j
public class CrmCustomerParseFunction implements IParseFunction {
public static final String NAME = "getCustomerById";
@Resource
private CrmCustomerService customerService;
@ -29,7 +29,7 @@ public class CrmCustomerParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_CUSTOMER_BY_ID;
}
@Override

View File

@ -7,17 +7,18 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_SOURCE;
/**
* 客户来源的 {@link IParseFunction} 实现类
* CRM 客户来源的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Component
@Slf4j
public class CrmSourceParseFunction implements IParseFunction {
public class CrmCustomerSourceParseFunction implements IParseFunction {
public static final String NAME = "getSource";
public static final String NAME = "getCustomerSource";
@Override
public boolean executeBefore() {
@ -26,7 +27,7 @@ public class CrmSourceParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_CUSTOMER_SOURCE;
}
@Override

View File

@ -98,4 +98,12 @@ public interface CrmBusinessService {
*/
void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);
/**
* 获取关联客户的商机数量
*
* @param customerId 客户编号
* @return 数量
*/
Long getBusinessCountByCustomerId(Long customerId);
}

View File

@ -3,6 +3,7 @@ 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.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
@ -17,6 +18,9 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
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;
@ -28,6 +32,7 @@ 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.convertSet;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
/**
* 商机 Service 实现类
@ -48,7 +53,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@Override
@Transactional(rollbackFor = Exception.class)
// TODO @商机待定操作日志
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}",
success = CRM_BUSINESS_CREATE_SUCCESS)
public Long createBusiness(CrmBusinessCreateReqVO createReqVO, Long userId) {
// 1. 插入商机
CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO);
@ -60,17 +66,20 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
// 2. 创建数据权限
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
.setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
// 4. 记录操作日志上下文
LogRecordContext.putVariable("business", business);
return business.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id",
level = CrmPermissionLevelEnum.WRITE)
// TODO @商机待定操作日志
@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(CrmBusinessUpdateReqVO updateReqVO) {
// 1. 校验存在
validateBusinessExists(updateReqVO.getId());
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
// 2. 更新商机
CrmBusinessDO updateObj = CrmBusinessConvert.INSTANCE.convert(updateReqVO);
@ -78,20 +87,28 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
// TODO 商机待定插入商机与产品的关联表校验商品存在
// TODO @商机待定如果状态发生变化插入商机状态变更记录表
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessUpdateReqVO.class));
LogRecordContext.putVariable("businessName", oldBusiness.getName());
}
@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) {
// 校验存在
validateBusinessExists(id);
CrmBusinessDO business = validateBusinessExists(id);
// TODO @商机待定需要校验有没关联合同CrmContractDO businessId 字段
// 删除
businessMapper.deleteById(id);
// 删除数据权限
permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);
// 记录操作日志上下文
LogRecordContext.putVariable("businessName", business.getName());
}
private CrmBusinessDO validateBusinessExists(Long id) {
@ -102,6 +119,28 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
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(
CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
// 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) {
@ -142,17 +181,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
@Override
@Transactional(rollbackFor = Exception.class)
// TODO @puhui999操作日志
public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) {
// 1 校验商机是否存在
validateBusinessExists(reqVO.getId());
// 2.1 数据权限转移
permissionService.transferPermission(
CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
// 2.2 设置新的负责人
businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
public Long getBusinessCountByCustomerId(Long customerId) {
return businessMapper.selectCount(CrmBusinessDO::getCustomerId, customerId);
}
}

View File

@ -3,12 +3,13 @@ package cn.iocoder.yudao.module.crm.service.clue;
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.clue.vo.CrmCluePageReqVO;
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.controller.admin.clue.vo.CrmClueTransformReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@ -139,8 +140,9 @@ public class CrmClueServiceImpl implements CrmClueService {
// 遍历线索创建对应的客户
clues.forEach(clue -> {
clue.setId(null);
// 创建客户
customerService.createCustomer(CrmCustomerConvert.INSTANCE.convert(clue), userId);
customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerSaveReqVO.class), userId);
// 更新线索状态
// TODO @min新建一个 CrmClueDO 去更新尽量规避直接用原本的对象去更新因为这样万一并发更新会存在覆盖的问题
// TODO @puhui999如果有跟进记录需要一起转过去

View File

@ -1,7 +1,9 @@
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.*;
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.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import jakarta.validation.Valid;
@ -39,6 +41,22 @@ public interface CrmContactService {
*/
void deleteContact(Long id);
/**
* 联系人转移
*
* @param reqVO 请求
* @param userId 用户编号
*/
void transferContact(CrmContactTransferReqVO reqVO, Long userId);
/**
* 更新客户联系人负责人
*
* @param customerId 客户编号
* @param ownerUserId 用户编号
*/
void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId);
/**
* 获得联系人
*
@ -85,11 +103,11 @@ public interface CrmContactService {
PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO);
/**
* 联系人转移
* 获取关联客户的联系人数量
*
* @param reqVO 请求
* @param userId 用户编号
* @param customerId 客户编号
* @return 数量
*/
void transferContact(CrmContactTransferReqVO reqVO, Long userId);
Long getContactCountByCustomerId(Long customerId);
}

View File

@ -33,7 +33,7 @@ import java.util.List;
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.CRM_CONTACT_TYPE;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
import static java.util.Collections.singletonList;
@ -65,7 +65,8 @@ public class CrmContactServiceImpl implements CrmContactService {
@Override
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_CONTACT_TYPE, subType = "创建联系人", bizNo = "{{#contactId}}", success = "创建了联系人[{{#contactName}}]")
@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) {
// 1. 校验
validateRelationDataExists(createReqVO);
@ -86,18 +87,18 @@ public class CrmContactServiceImpl implements CrmContactService {
}
// 5. 记录操作日志
LogRecordContext.putVariable("contactId", contact.getId());
LogRecordContext.putVariable("contactName", contact.getName());
LogRecordContext.putVariable("contact", contact);
return contact.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_CONTACT_TYPE, subType = "更新联系人", bizNo = "{{#updateReqVO.id}}", success = "更新了联系人{_DIFF{#updateReqVO}}")
@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. 校验存在
CrmContactDO contactDO = validateContactExists(updateReqVO.getId());
CrmContactDO oldContact = validateContactExists(updateReqVO.getId());
validateRelationDataExists(updateReqVO);
// 2. 更新联系人
@ -105,7 +106,8 @@ public class CrmContactServiceImpl implements CrmContactService {
contactMapper.updateById(updateObj);
// 3. 记录操作日志
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contactDO, CrmContactSaveReqVO.class));
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class));
LogRecordContext.putVariable("contactName", oldContact.getName());
}
/**
@ -133,11 +135,13 @@ public class CrmContactServiceImpl implements CrmContactService {
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
@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 校验存在
validateContactExists(id);
CrmContactDO contact = validateContactExists(id);
// 1.2 校验是否关联合同
if (contractService.getContractCountByContactId(id) > 0) {
throw exception(CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS);
@ -151,6 +155,9 @@ public class CrmContactServiceImpl implements CrmContactService {
// 4.2 删除商机关联
contactBusinessService.deleteContactBusinessByContactId(id);
// TODO @puhui999删除跟进记录
// 记录操作日志上下文
LogRecordContext.putVariable("contactName", contact.getName());
}
private CrmContactDO validateContactExists(Long id) {
@ -161,6 +168,32 @@ public class CrmContactServiceImpl implements CrmContactService {
return contactDO;
}
@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(
CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()));
// 2.2 设置新的负责人
contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
// 3. 记录转移日志
LogRecordContext.putVariable("contact", contact);
}
@Override
public void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {
contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId);
}
//======================= 查询相关 =======================
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
public CrmContactDO getContact(Long id) {
@ -192,19 +225,8 @@ public class CrmContactServiceImpl implements CrmContactService {
}
@Override
// TODO @puhui999权限校验
// TODO @puhui999记录操作日志将联系人名字转移给新负责人
public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
// 1 校验联系人是否存在
validateContactExists(reqVO.getId());
// 2.1 数据权限转移
permissionService.transferPermission(
CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()));
// 2.2 设置新的负责人
contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
// 3. TODO 记录转移日志
public Long getContactCountByCustomerId(Long customerId) {
return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId);
}
}

View File

@ -95,4 +95,12 @@ public interface CrmContractService {
*/
Long getContractCountByContactId(Long contactId);
/**
* 获取关联客户的合同数量
*
* @param customerId 客户编号
* @return 数量
*/
Long getContractCountByCustomerId(Long customerId);
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.contract;
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.contract.vo.CrmContractCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
@ -15,6 +16,9 @@ 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.crm.service.permission.bo.CrmPermissionCreateReqBO;
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;
@ -25,6 +29,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
/**
* CRM 合同 Service 实现类
@ -42,7 +47,9 @@ public class CrmContractServiceImpl implements CrmContractService {
private CrmPermissionService crmPermissionService;
@Override
// TODO @puhui999添加操作日志
@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(CrmContractCreateReqVO createReqVO, Long userId) {
// TODO @合同待定插入合同商品需要搞个 BusinessProductDO
// 插入合同
@ -53,38 +60,52 @@ public class CrmContractServiceImpl implements CrmContractService {
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
.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)
// TODO @puhui999添加操作日志
public void updateContract(CrmContractUpdateReqVO updateReqVO) {
// TODO @合同待定只有草稿审批中可以编辑
// 校验存在
validateContractExists(updateReqVO.getId());
CrmContractDO oldContract = validateContractExists(updateReqVO.getId());
// 更新合同
CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO);
contractMapper.updateById(updateObj);
// TODO @合同待定插入合同商品需要搞个 BusinessProductDO
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractUpdateReqVO.class));
LogRecordContext.putVariable("contractName", oldContract.getName());
}
// TODO @合同待定缺一个取消合同的接口只有草稿审批中可以取消CrmAuditStatusEnum
// TODO @合同待定缺一个发起审批的接口只有草稿可以发起审批CrmAuditStatusEnum
@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) {
// TODO @合同待定如果被 CrmReceivableDO 所使用则不允许删除
// 校验存在
validateContractExists(id);
CrmContractDO contract = validateContractExists(id);
// 删除
contractMapper.deleteById(id);
// 删除数据权限
crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id);
// 记录操作日志上下文
LogRecordContext.putVariable("contractName", contract.getName());
}
private CrmContractDO validateContractExists(Long id) {
@ -95,6 +116,27 @@ public class CrmContractServiceImpl implements CrmContractService {
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(
CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
// 2.2 设置负责人
contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
// 3. 记录转移日志
LogRecordContext.putVariable("contract", contract);
}
//======================= 查询相关 =======================
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
public CrmContractDO getContract(Long id) {
@ -120,25 +162,15 @@ public class CrmContractServiceImpl implements CrmContractService {
return contractMapper.selectPageByCustomerId(pageReqVO);
}
@Override
@Transactional(rollbackFor = Exception.class)
// 3. TODO @puhui999记录转移日志
// TODO @puhui999权限校验这里要搞哇
public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
// 1. 校验合同是否存在
validateContractExists(reqVO.getId());
// 2.1 数据权限转移
crmPermissionService.transferPermission(
CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
// 2.2 设置负责人
contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
}
@Override
public Long getContractCountByContactId(Long contactId) {
return contractMapper.selectCountByContactId(contactId);
}
@Override
public Long getContractCountByCustomerId(Long customerId) {
return contractMapper.selectCount(CrmContractDO::getCustomerId, customerId);
}
// TODO @合同待定需要新增一个 ContractConfigDO 合同配置重点是到期提醒
}

View File

@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerLimitConfigConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper;
import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum;
@ -49,7 +48,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
public Long createCustomerLimitConfig(CrmCustomerLimitConfigSaveReqVO createReqVO) {
validateUserAndDept(createReqVO.getUserIds(), createReqVO.getDeptIds());
// 插入
CrmCustomerLimitConfigDO customerLimitConfig = CrmCustomerLimitConfigConvert.INSTANCE.convert(createReqVO);
CrmCustomerLimitConfigDO customerLimitConfig = BeanUtils.toBean(createReqVO, CrmCustomerLimitConfigDO.class);
customerLimitConfigMapper.insert(customerLimitConfig);
// 记录操作日志上下文
@ -66,7 +65,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
CrmCustomerLimitConfigDO oldLimitConfig = validateCustomerLimitConfigExists(updateReqVO.getId());
validateUserAndDept(updateReqVO.getUserIds(), updateReqVO.getDeptIds());
// 更新
CrmCustomerLimitConfigDO updateObj = CrmCustomerLimitConfigConvert.INSTANCE.convert(updateReqVO);
CrmCustomerLimitConfigDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerLimitConfigDO.class);
customerLimitConfigMapper.updateById(updateObj);
// 记录操作日志上下文

View File

@ -1,8 +1,7 @@
package cn.iocoder.yudao.module.crm.service.customer;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerPoolConfigMapper;
import com.mzt.logapi.context.LogRecordContext;
@ -34,8 +33,7 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
*/
@Override
public CrmCustomerPoolConfigDO getCustomerPoolConfig() {
// TODO @puhui999这个要搞到 mapper 里噢
return customerPoolConfigMapper.selectOne(new LambdaQueryWrapperX<CrmCustomerPoolConfigDO>().last("LIMIT 1"));
return customerPoolConfigMapper.selectOne();
}
/**
@ -49,7 +47,7 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) {
// 存在则进行更新
CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig();
CrmCustomerPoolConfigDO poolConfig = CrmCustomerConvert.INSTANCE.convert(saveReqVO);
CrmCustomerPoolConfigDO poolConfig = BeanUtils.toBean(saveReqVO, CrmCustomerPoolConfigDO.class);
if (Objects.nonNull(dbConfig)) {
customerPoolConfigMapper.updateById(poolConfig.setId(dbConfig.getId()));
// 记录操作日志上下文

View File

@ -18,6 +18,9 @@ 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.framework.permission.core.util.CrmPermissionUtils;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@ -26,6 +29,7 @@ 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;
@ -59,6 +63,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
private CrmPermissionService permissionService;
@Resource
private CrmCustomerLimitConfigService customerLimitConfigService;
@Resource
@Lazy
private CrmContactService contactService;
@Resource
@Lazy
private CrmBusinessService businessService;
@Resource
@Lazy
private CrmContractService contractService;
@Resource
private AdminUserApi adminUserApi;
@ -73,7 +86,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
// 2. 插入客户
CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO)
CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class)
.setLockStatus(false).setDealStatus(false)
.setContactLastTime(LocalDateTime.now());
// TODO @puhui999可能要加个 receiveTime 字段记录最后接收时间
@ -100,7 +113,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId());
// 2. 更新客户
CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);
CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);
customerMapper.updateById(updateObj);
// 3. 记录操作日志上下文
@ -116,8 +129,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
public void deleteCustomer(Long id) {
// 校验存在
CrmCustomerDO customer = validateCustomerExists(id);
// TODO @puhui999如果有联系人商机则不允许删除
// 检查引用
checkCustomerReference(id);
// 删除
customerMapper.deleteById(id);
// 删除数据权限
@ -128,6 +141,23 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
LogRecordContext.putVariable("customerName", customer.getName());
}
/**
* 校验客户是否被引用
*
* @param id 客户编号
*/
private void checkCustomerReference(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());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
@ -145,10 +175,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
// 2.2 转移后重新设置负责人
customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
// 3. TODO 记录转移日志
// 记录操作日志上下文
// TODO @puhui999crmCustomer=customer也看看其他有没类似的情况哈
LogRecordContext.putVariable("crmCustomer", customer);
// 3. 记录转移日志
LogRecordContext.putVariable("customer", customer);
}
@Override
@ -172,7 +200,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
// 3. 记录操作日志上下文
// tips: 因为这里使用的是老的状态所以记录时反着记录也就是 lockStatus true 那么就是解锁反之为锁定
LogRecordContext.putVariable("crmCustomer", customer);
LogRecordContext.putVariable("customer", customer);
}
// ==================== 公海相关操作 ====================
@ -202,6 +230,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
CrmPermissionLevelEnum.OWNER.getLevel());
// TODO @puhui999联系人的负责人也要设置为 null这块和领取是对应的因为领取后负责人也要关联过来
// 提问那是不是可以这样理解客户所有联系人的负责人默认为客户的负责人然后添加客户团队成员时才存在同时分配给的操作
contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
// 记录操作日志上下文
LogRecordContext.putVariable("customerName", customer.getName());

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.crm.service.followup;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import jakarta.validation.Valid;
/**
* 跟进记录 Service 接口
*
* @author 芋道源码
*/
public interface CrmFollowUpRecordService {
/**
* 创建跟进记录
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO);
/**
* 更新跟进记录
*
* @param updateReqVO 更新信息
*/
void updateFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO updateReqVO);
/**
* 删除跟进记录
*
* @param id 编号
*/
void deleteFollowUpRecord(Long id);
/**
* 获得跟进记录
*
* @param id 编号
* @return 跟进记录
*/
CrmFollowUpRecordDO getFollowUpRecord(Long id);
/**
* 获得跟进记录分页
*
* @param pageReqVO 分页查询
* @return 跟进记录分页
*/
PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO);
}

View File

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.crm.service.followup;
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.followup.vo.CrmFollowUpRecordPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
/**
* 跟进记录 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
@Resource
private CrmFollowUpRecordMapper crmFollowUpRecordMapper;
@Override
public Long createFollowUpRecord(CrmFollowUpRecordSaveReqVO createReqVO) {
// 插入
CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);
crmFollowUpRecordMapper.insert(followUpRecord);
// 返回
return followUpRecord.getId();
}
@Override
public void updateFollowUpRecord(CrmFollowUpRecordSaveReqVO updateReqVO) {
// 校验存在
validateFollowUpRecordExists(updateReqVO.getId());
// 更新
CrmFollowUpRecordDO updateObj = BeanUtils.toBean(updateReqVO, CrmFollowUpRecordDO.class);
crmFollowUpRecordMapper.updateById(updateObj);
}
@Override
public void deleteFollowUpRecord(Long id) {
// 校验存在
validateFollowUpRecordExists(id);
// 删除
crmFollowUpRecordMapper.deleteById(id);
}
private void validateFollowUpRecordExists(Long id) {
if (crmFollowUpRecordMapper.selectById(id) == null) {
throw exception(FOLLOW_UP_RECORD_NOT_EXISTS);
}
}
@Override
public CrmFollowUpRecordDO getFollowUpRecord(Long id) {
return crmFollowUpRecordMapper.selectById(id);
}
@Override
public PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO) {
return crmFollowUpRecordMapper.selectPage(pageReqVO);
}
}

View File

@ -20,6 +20,7 @@ import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@ -36,7 +37,7 @@ import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnu
public class CrmPermissionServiceImpl implements CrmPermissionService {
@Resource
private CrmPermissionMapper crmPermissionMapper;
private CrmPermissionMapper permissionMapper;
@Resource
private AdminUserApi adminUserApi;
@ -44,50 +45,59 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
@Override
@Transactional(rollbackFor = Exception.class)
public Long createPermission(CrmPermissionCreateReqBO createReqBO) {
// TODO @puhui999排重
validatePermissionNotExists(Collections.singletonList(createReqBO));
// 1. 校验用户是否存在
adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId()));
// 2. 创建
CrmPermissionDO permission = CrmPermissionConvert.INSTANCE.convert(createReqBO);
crmPermissionMapper.insert(permission);
permissionMapper.insert(permission);
return permission.getId();
}
@Override
public void createPermissionBatch(List<CrmPermissionCreateReqBO> createReqBOs) {
// TODO @puhui999排重
validatePermissionNotExists(createReqBOs);
// 1. 校验用户是否存在
adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId));
// 2. 创建
List<CrmPermissionDO> permissions = CrmPermissionConvert.INSTANCE.convertList(createReqBOs);
crmPermissionMapper.insertBatch(permissions);
permissionMapper.insertBatch(permissions);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updatePermission(CrmPermissionUpdateReqVO updateReqVO) {
// TODO @puhui999排重
// 1. 校验存在
validateCrmPermissionExists(updateReqVO.getIds());
validatePermissionExists(updateReqVO.getIds());
// 2. 更新
List<CrmPermissionDO> updateDO = CrmPermissionConvert.INSTANCE.convertList(updateReqVO);
crmPermissionMapper.updateBatch(updateDO);
permissionMapper.updateBatch(updateDO);
}
private void validateCrmPermissionExists(Collection<Long> ids) {
List<CrmPermissionDO> permissionList = crmPermissionMapper.selectBatchIds(ids);
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 = crmPermissionMapper.selectByBizTypeAndBizIdByUserId(
CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId(
transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId());
String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType());
if (oldPermission == null // 不是拥有者并且不是超管
@ -102,25 +112,25 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId()));
// 2. 修改新负责人的权限
List<CrmPermissionDO> permissions = crmPermissionMapper.selectByBizTypeAndBizId(
List<CrmPermissionDO> permissions = permissionMapper.selectByBizTypeAndBizId(
transferReqBO.getBizType(), transferReqBO.getBizId()); // 获得所有数据权限
CrmPermissionDO permission = CollUtil.findOne(permissions,
item -> ObjUtil.equal(item.getUserId(), transferReqBO.getNewOwnerUserId()));
if (permission == null) {
crmPermissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType())
permissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType())
.setBizId(transferReqBO.getBizId()).setUserId(transferReqBO.getNewOwnerUserId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
} else {
crmPermissionMapper.updateById(new CrmPermissionDO().setId(permission.getId())
permissionMapper.updateById(new CrmPermissionDO().setId(permission.getId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
}
// 3. 修改老负责人的权限
if (transferReqBO.getOldOwnerPermissionLevel() != null) {
crmPermissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId())
permissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId())
.setLevel(transferReqBO.getOldOwnerPermissionLevel()));
} else {
crmPermissionMapper.deleteById(oldPermission.getId());
permissionMapper.deleteById(oldPermission.getId());
}
}
@ -128,19 +138,19 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
@Transactional(rollbackFor = Exception.class)
public void deletePermission(Integer bizType, Long bizId, Integer level) {
// 校验存在
List<CrmPermissionDO> permissions = crmPermissionMapper.selectListByBizTypeAndBizIdAndLevel(
List<CrmPermissionDO> permissions = permissionMapper.selectListByBizTypeAndBizIdAndLevel(
bizType, bizId, level);
if (CollUtil.isEmpty(permissions)) {
throw exception(CRM_PERMISSION_NOT_EXISTS);
}
// 删除数据权限
crmPermissionMapper.deleteBatchIds(convertSet(permissions, CrmPermissionDO::getId));
permissionMapper.deleteBatchIds(convertSet(permissions, CrmPermissionDO::getId));
}
@Override
public void deletePermission(Integer bizType, Long bizId) {
int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId);
int deletedCount = permissionMapper.deletePermission(bizType, bizId);
if (deletedCount == 0) {
throw exception(CRM_PERMISSION_NOT_EXISTS);
}
@ -148,7 +158,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
@Override
public void deletePermissionBatch(Collection<Long> ids, Long userId) {
List<CrmPermissionDO> permissions = crmPermissionMapper.selectBatchIds(ids);
List<CrmPermissionDO> permissions = permissionMapper.selectBatchIds(ids);
if (CollUtil.isEmpty(permissions)) {
throw exception(CRM_PERMISSION_NOT_EXISTS);
}
@ -157,19 +167,19 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
throw exception(CRM_PERMISSION_DELETE_FAIL);
}
// 校验操作人是否为负责人
CrmPermissionDO permission = crmPermissionMapper.selectByIdAndUserId(permissions.get(0).getBizId(), userId);
CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(permissions.get(0).getBizId(), userId);
if (!CrmPermissionLevelEnum.isOwner(permission.getLevel())) {
throw exception(CRM_PERMISSION_DELETE_DENIED);
}
// 删除数据权限
crmPermissionMapper.deleteBatchIds(ids);
permissionMapper.deleteBatchIds(ids);
}
@Override
public void deleteSelfPermission(Long id, Long userId) {
// 校验数据存在且是自己
CrmPermissionDO permission = crmPermissionMapper.selectByIdAndUserId(id, userId);
CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(id, userId);
if (permission == null) {
throw exception(CRM_PERMISSION_NOT_EXISTS);
}
@ -179,22 +189,22 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
}
// 删除
crmPermissionMapper.deleteById(id);
permissionMapper.deleteById(id);
}
@Override
public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Long bizId) {
return crmPermissionMapper.selectByBizTypeAndBizId(bizType, bizId);
return permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
}
@Override
public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Collection<Long> bizIds) {
return crmPermissionMapper.selectByBizTypeAndBizIds(bizType, bizIds);
return permissionMapper.selectByBizTypeAndBizIds(bizType, bizIds);
}
@Override
public List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId) {
return crmPermissionMapper.selectListByBizTypeAndUserId(bizType, userId);
return permissionMapper.selectListByBizTypeAndUserId(bizType, userId);
}
}

View File

@ -3,7 +3,6 @@ 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.plan.CrmReceivablePlanCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
@ -78,12 +77,4 @@ public interface CrmReceivablePlanService {
*/
PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO);
/**
* 回款计划转移
*
* @param reqVO 请求
* @param userId 用户编号
*/
void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId);
}

View File

@ -4,9 +4,9 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
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.CrmReceivablePlanCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@ -20,6 +20,9 @@ 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 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.validation.annotation.Validated;
@ -29,6 +32,7 @@ import java.util.List;
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.*;
// TODO @liuhongfeng参考 CrmReceivableServiceImpl 写的 todo
@ -49,10 +53,11 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
@Resource
private CrmCustomerService customerService;
@Resource
private CrmPermissionService crmPermissionService;
private CrmPermissionService permissionService;
@Override
// TODO @puhui999操作日志
@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(CrmReceivablePlanCreateReqVO createReqVO, Long userId) {
// TODO @liuhongfeng第几期的计算基于是 contractId + contractDO 的第几个还款
// TODO @liuhongfeng contractId校验合同是否存在
@ -64,9 +69,12 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
receivablePlanMapper.insert(receivablePlan);
// 创建数据权限
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
.setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
// 4. 记录操作日志上下文
LogRecordContext.putVariable("receivablePlan", receivablePlan);
return receivablePlan.getId();
}
@ -89,31 +97,44 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
}
@Override
@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)
// TODO @puhui999操作日志
public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) {
// TODO @liuhongfeng如果已经有对应的还款则不允许编辑
// 校验存在
validateReceivablePlanExists(updateReqVO.getId());
CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
// 更新
CrmReceivablePlanDO updateObj = CrmReceivablePlanConvert.INSTANCE.convert(updateReqVO);
receivablePlanMapper.updateById(updateObj);
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanUpdateReqVO.class));
LogRecordContext.putVariable("receivablePlan", oldReceivablePlan);
}
@Override
@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) {
// 校验存在
validateReceivablePlanExists(id);
CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id);
// 删除
receivablePlanMapper.deleteById(id);
// 删除数据权限
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
// 记录操作日志上下文
LogRecordContext.putVariable("receivablePlan", receivablePlan);
}
private void validateReceivablePlanExists(Long id) {
if (receivablePlanMapper.selectById(id) == null) {
private CrmReceivablePlanDO validateReceivablePlanExists(Long id) {
CrmReceivablePlanDO receivablePlan = receivablePlanMapper.selectById(id);
if (receivablePlan == null) {
throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
}
return receivablePlan;
}
@Override
@ -141,19 +162,4 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
return receivablePlanMapper.selectPageByCustomerId(pageReqVO);
}
// TODO @puhui999这个没有 transfer 接口可能是的哈
@Override
public void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId) {
// 1 校验回款计划是否存在
validateReceivablePlanExists(reqVO.getId());
// 2.1 数据权限转移
crmPermissionService.transferPermission(
CrmReceivablePlanConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()));
// 2.2 设置新的负责人
receivablePlanMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
// 3. TODO 记录转移日志
}
}

View File

@ -3,7 +3,6 @@ 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.CrmReceivableCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
@ -23,9 +22,10 @@ public interface CrmReceivableService {
* 创建回款
*
* @param createReqVO 创建信息
* @param userId 用户编号
* @return 编号
*/
Long createReceivable(@Valid CrmReceivableCreateReqVO createReqVO);
Long createReceivable(@Valid CrmReceivableCreateReqVO createReqVO, Long userId);
/**
* 更新回款
@ -78,12 +78,4 @@ public interface CrmReceivableService {
*/
PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO);
/**
* 回款转移
*
* @param reqVO 请求
* @param userId 用户编号
*/
void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId);
}

View File

@ -5,9 +5,9 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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.receivable.CrmReceivableCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@ -22,6 +22,10 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
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 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.validation.annotation.Validated;
@ -31,6 +35,7 @@ import java.util.List;
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.*;
/**
* CRM 回款 Service 实现类
@ -51,11 +56,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
@Resource
private CrmReceivablePlanService receivablePlanService;
@Resource
private CrmPermissionService crmPermissionService;
private CrmPermissionService permissionService;
@Override
// TODO @puhui999操作日志
public Long createReceivable(CrmReceivableCreateReqVO createReqVO) {
@LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
success = CRM_RECEIVABLE_CREATE_SUCCESS)
public Long createReceivable(CrmReceivableCreateReqVO createReqVO, Long userId) {
// 插入还款
CrmReceivableDO receivable = CrmReceivableConvert.INSTANCE.convert(createReqVO);
if (ObjectUtil.isNull(receivable.getAuditStatus())) {
@ -67,8 +73,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
checkReceivable(receivable);
receivableMapper.insert(receivable);
// 3. 创建数据权限
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
.setBizId(receivable.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
// TODO @liuhongfeng需要更新关联的 plan
// 记录操作日志上下文
LogRecordContext.putVariable("receivable", receivable);
return receivable.getId();
}
@ -98,11 +108,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
}
@Override
// TODO @puhui999操作日志
// TODO @puhui999权限校验
@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(CrmReceivableUpdateReqVO updateReqVO) {
// 校验存在
validateReceivableExists(updateReqVO.getId());
CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId());
// TODO @liuhongfeng只有在草稿审核中可以提交修改
// 更新还款
@ -110,6 +121,9 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
receivableMapper.updateById(updateObj);
// TODO @liuhongfeng需要更新关联的 plan
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableUpdateReqVO.class));
LogRecordContext.putVariable("receivable", oldReceivable);
}
// TODO @liuhongfeng缺一个取消合同的接口只有草稿审批中可以取消CrmAuditStatusEnum
@ -117,24 +131,33 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
// TODO @liuhongfeng缺一个发起审批的接口只有草稿可以发起审批CrmAuditStatusEnum
@Override
// TODO @puhui999操作日志
// TODO @puhui999权限校验
@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) {
// TODO @liuhongfeng如果被 CrmReceivablePlanDO 所使用则不允许删除
// 校验存在
validateReceivableExists(id);
CrmReceivableDO receivable = validateReceivableExists(id);
// 删除
receivableMapper.deleteById(id);
// 删除数据权限
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
// 记录操作日志上下文
LogRecordContext.putVariable("receivable", receivable);
}
private void validateReceivableExists(Long id) {
if (receivableMapper.selectById(id) == null) {
private CrmReceivableDO validateReceivableExists(Long id) {
CrmReceivableDO receivable = receivableMapper.selectById(id);
if (receivable == null) {
throw exception(RECEIVABLE_NOT_EXISTS);
}
return receivable;
}
// TODO @芋艿数据权限
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.READ)
public CrmReceivableDO getReceivable(Long id) {
return receivableMapper.selectById(id);
}
@ -158,18 +181,4 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
return receivableMapper.selectPageByCustomerId(pageReqVO);
}
@Override
public void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId) {
// 1 校验回款是否存在
validateReceivableExists(reqVO.getId());
// 2.1 数据权限转移
crmPermissionService.transferPermission(
CrmReceivableConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()));
// 2.2 设置新的负责人
receivableMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
// 3. TODO 记录转移日志
}
}

View File

@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
@ -42,7 +43,7 @@ public class CrmCrmReceivableServiceImplTest extends BaseDbUnitTest {
CrmReceivableCreateReqVO reqVO = randomPojo(CrmReceivableCreateReqVO.class);
// 调用
Long receivableId = receivableService.createReceivable(reqVO);
Long receivableId = receivableService.createReceivable(reqVO, getLoginUserId());
// 断言
assertNotNull(receivableId);
// 校验记录的属性是否正确

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.enums.operatelog;
/**
* functionName 常量枚举
* 方便别的模块调用
*
* @author HUIHUI
*/
public interface SysParseFunctionNameConstants {
String GET_ADMIN_USER_BY_ID = "getAdminUserById"; // 获取用户信息
String GET_DEPT_BY_ID = "getDeptById"; // 获取部门信息
String GET_AREA = "getArea"; // 获取区域信息
String GET_SEX = "getSex"; // 获取性别
String GET_BOOLEAN = "getBoolean"; // 获取是否
}

View File

@ -8,6 +8,8 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
/**
* 管理员名字的 {@link IParseFunction} 实现类
*
@ -22,7 +24,7 @@ public class AdminUserParseFunction implements IParseFunction {
@Override
public String functionName() {
return "getAdminUserById";
return GET_ADMIN_USER_BY_ID;
}
@Override

View File

@ -6,6 +6,8 @@ import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_AREA;
/**
* 地名的 {@link IParseFunction} 实现类
*
@ -22,7 +24,7 @@ public class AreaParseFunction implements IParseFunction {
@Override
public String functionName() {
return "getAreaById";
return GET_AREA;
}
@Override

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
package cn.iocoder.yudao.module.system.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
@ -7,16 +7,16 @@ import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_BOOLEAN;
/**
* 行业的 {@link IParseFunction} 实现类
* 是否类型 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Component
@Slf4j
public class CrmBooleanParseFunction implements IParseFunction {
public static final String NAME = "getBooleanById";
public class BooleanParseFunction implements IParseFunction {
@Override
public boolean executeBefore() {
@ -25,7 +25,7 @@ public class CrmBooleanParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_BOOLEAN;
}
@Override

View File

@ -8,6 +8,8 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_DEPT_BY_ID;
/**
* 管理员名字的 {@link IParseFunction} 实现类
*
@ -22,7 +24,7 @@ public class DeptParseFunction implements IParseFunction {
@Override
public String functionName() {
return "getDeptById";
return GET_DEPT_BY_ID;
}
@Override

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
package cn.iocoder.yudao.module.system.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
@ -7,6 +7,8 @@ import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_SEX;
/**
* 行业的 {@link IParseFunction} 实现类
*
@ -14,9 +16,7 @@ import org.springframework.stereotype.Component;
*/
@Component
@Slf4j
public class CrmSexParseFunction implements IParseFunction {
public static final String NAME = "getSexById";
public class SexParseFunction implements IParseFunction {
@Override
public boolean executeBefore() {
@ -25,7 +25,7 @@ public class CrmSexParseFunction implements IParseFunction {
@Override
public String functionName() {
return NAME;
return GET_SEX;
}
@Override