diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index 00ac14356..da0c4e735 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -62,6 +62,7 @@
0.1.55
2.9.1
2.7.0
+ 3.0.6
3.5.0
4.11.0
@@ -99,6 +100,17 @@
yudao-spring-boot-starter-biz-operatelog
${revision}
+
+ io.github.mouzt
+ bizlog-sdk
+ ${bizlog-sdk.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
cn.iocoder.boot
yudao-spring-boot-starter-biz-dict
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
index c7e50a487..53b5574f9 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
@@ -177,4 +177,14 @@ public class DateUtils {
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
}
+ /**
+ * 是否昨天
+ *
+ * @param date 日期
+ * @return 是否
+ */
+ public static boolean isYesterday(LocalDateTime date) {
+ return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
new file mode 100644
index 000000000..92da847fd
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.crm.enums;
+
+/**
+ * CRM 操作日志枚举
+ *
+ * @author HUIHUI
+ */
+public interface LogRecordConstants {
+
+ //======================= 客户模块类型 =======================
+ // TODO puhui999: 确保模块命名方式为 module + 子模块名称的方式。统一定义模块名称是为了方便查询各自记录的操作日志,列如说:查询客户【张三的操作日志】就可以 module + bizId
+ String CRM_LEADS = "CRM-线索";
+ String CRM_CUSTOMER = "CRM-客户";
+ String CRM_CONTACT = "CRM-联系人";
+ String CRM_BUSINESS = "CRM-商机";
+ String CRM_CONTRACT = "CRM-合同";
+ String CRM_PRODUCT = "CRM-产品";
+ String CRM_RECEIVABLE = "CRM-回款";
+ String CRM_RECEIVABLE_PLAN = "CRM-回款计划";
+
+ //======================= 客户转移操作日志 =======================
+
+ String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
+ String TRANSFER_CUSTOMER_LOG_FAIL = "";
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
index 0a441d229..f0784cab2 100644
--- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
@@ -22,7 +22,9 @@ public enum CrmBizTypeEnum implements IntArrayValuable {
CRM_CONTACT(3, "联系人"),
CRM_BUSINESS(4, "商机"),
CRM_CONTRACT(5, "合同"),
- CRM_PRODUCT(6, "产品")
+ CRM_PRODUCT(6, "产品"),
+ CRM_RECEIVABLE(7, "回款"),
+ CRM_RECEIVABLE_PLAN(8, "回款计划")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizTypeEnum::getType).toArray();
diff --git a/yudao-module-crm/yudao-module-crm-biz/pom.xml b/yudao-module-crm/yudao-module-crm-biz/pom.xml
index 15bbc932d..9e1a9e152 100644
--- a/yudao-module-crm/yudao-module-crm-biz/pom.xml
+++ b/yudao-module-crm/yudao-module-crm-biz/pom.xml
@@ -60,6 +60,10 @@
cn.iocoder.boot
yudao-spring-boot-starter-excel
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-dict
+
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
index d23595290..e4e24d29d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
@@ -122,7 +122,7 @@ public class CrmBusinessController {
@Operation(summary = "获得商机分页,基于指定客户")
public CommonResult> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) {
Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
- PageResult pageResult = businessService.getBusinessPageByCustomer(pageReqVO, getLoginUserId());
+ PageResult pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);
// 处理客户名称回显
// TODO @ljlleo:可以使用 CollectionUtils.convertSet 替代常用的 stream 操作,更简洁一点;下面几个也是哈;
Set customerIds = pageResult.getList().stream()
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java
index 579026047..3c9520607 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java
@@ -24,7 +24,4 @@ public class CrmBusinessPageReqVO extends PageParam {
@InEnum(CrmSceneTypeEnum.class)
private Integer sceneType; // 场景类型,为 null 时则表示全部
- @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
- private Boolean pool; // null 则表示为不是公海数据
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java
index c76c4873f..a76c48cae 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java
@@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
import jakarta.validation.constraints.NotNull;
+import lombok.Data;
@Schema(description = "管理后台 - 商机转移 Request VO")
@Data
public class CrmBusinessTransferReqVO {
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
- @NotNull(message = "联系人编号不能为空")
+ @NotNull(message = "商机编号不能为空")
private Long id;
/**
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
index 04f1b4db0..7227b3d12 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
@@ -88,4 +88,12 @@ public class CrmClueController {
ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas);
}
+ @PutMapping("/transfer")
+ @Operation(summary = "线索转移")
+ @PreAuthorize("@ss.hasPermission('crm:clue:update')")
+ public CommonResult transfer(@Valid @RequestBody CrmClueTransferReqVO reqVO) {
+ clueService.transferClue(reqVO, getLoginUserId());
+ return success(true);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java
new file mode 100644
index 000000000..da71a1ec2
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 线索转移 Request VO")
+@Data
+public class CrmClueTransferReqVO {
+
+ @Schema(description = "线索编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "线索编号不能为空")
+ private Long id;
+
+ /**
+ * 新负责人的用户编号
+ */
+ @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "新负责人的用户编号不能为空")
+ private Long newOwnerUserId;
+
+ /**
+ * 老负责人加入团队后的权限级别。如果 null 说明移除
+ *
+ * 关联 {@link CrmPermissionLevelEnum}
+ */
+ @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer oldOwnerPermissionLevel;
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
index dbdc0663d..9bb06a1bc 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
@@ -9,7 +9,7 @@ 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.contact.vo.*;
-import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert;
+import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
@@ -100,7 +100,7 @@ public class CrmContactController {
// 3. 直属上级
List parentContactList = contactService.getContactList(
Collections.singletonList(contact.getParentId()), getLoginUserId());
- return success(ContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList));
+ return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList));
}
@GetMapping("/simple-all-list")
@@ -110,7 +110,7 @@ public class CrmContactController {
CrmContactPageReqVO pageReqVO = new CrmContactPageReqVO();
pageReqVO.setPageSize(PAGE_SIZE_NONE);
List list = contactService.getContactPage(pageReqVO, getLoginUserId()).getList();
- return success(ContactConvert.INSTANCE.convertAllList(list));
+ return success(CrmContactConvert.INSTANCE.convertAllList(list));
}
@GetMapping("/page")
@@ -125,7 +125,7 @@ public class CrmContactController {
@Operation(summary = "获得联系人分页,基于指定客户")
public CommonResult> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) {
Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
- PageResult pageResult = contactService.getContactPageByCustomerId(pageVO, getLoginUserId());
+ PageResult pageResult = contactService.getContactPageByCustomerId(pageVO);
return success(convertDetailContactPage(pageResult));
}
@@ -161,7 +161,15 @@ public class CrmContactController {
// 3. 直属上级
List parentContactList = contactService.getContactList(
convertSet(contactList, CrmContactDO::getParentId), getLoginUserId());
- return ContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList);
+ return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList);
+ }
+
+ @PutMapping("/transfer")
+ @Operation(summary = "联系人转移")
+ @PreAuthorize("@ss.hasPermission('crm:contact:update')")
+ public CommonResult transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
+ contactService.transferContact(reqVO, getLoginUserId());
+ return success(true);
}
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java
index eb6ebd76c..75294a1bd 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java
@@ -39,7 +39,4 @@ public class CrmContactPageReqVO extends PageParam {
@InEnum(CrmSceneTypeEnum.class)
private Integer sceneType; // 场景类型,为 null 时则表示全部
- @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
- private Boolean pool; // null 则表示为不是公海数据
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
index 623a9ab91..f0442c4d3 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -8,7 +8,7 @@ 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.contract.vo.*;
-import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert;
+import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
@@ -80,7 +80,7 @@ public class CrmContractController {
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
public CommonResult getContract(@RequestParam("id") Long id) {
CrmContractDO contract = contractService.getContract(id);
- return success(ContractConvert.INSTANCE.convert(contract));
+ return success(CrmContractConvert.INSTANCE.convert(contract));
}
@GetMapping("/page")
@@ -95,7 +95,7 @@ public class CrmContractController {
@Operation(summary = "获得联系人分页,基于指定客户")
public CommonResult> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
- PageResult pageResult = contractService.getContractPageByCustomer(pageVO, getLoginUserId());
+ PageResult pageResult = contractService.getContractPageByCustomerId(pageVO);
return success(convertDetailContractPage(pageResult));
}
@@ -108,7 +108,7 @@ public class CrmContractController {
PageResult pageResult = contractService.getContractPage(exportReqVO, getLoginUserId());
// 导出 Excel
ExcelUtils.write(response, "合同.xls", "数据", CrmContractExcelVO.class,
- ContractConvert.INSTANCE.convertList02(pageResult.getList()));
+ CrmContractConvert.INSTANCE.convertList02(pageResult.getList()));
}
/**
@@ -128,7 +128,7 @@ public class CrmContractController {
// 2. 获取创建人、负责人列表
Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
- return ContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList);
+ return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList);
}
@PutMapping("/transfer")
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java
index 2b3123bd9..94199ada6 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java
@@ -30,7 +30,4 @@ public class CrmContractPageReqVO extends PageParam {
@InEnum(CrmSceneTypeEnum.class)
private Integer sceneType; // 场景类型,为 null 时则表示全部
- @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
- private Boolean pool; // null 则表示为不是公海数据 TODO @puhui999:合同没有公海。目前只有【客户】【线索】有公海,其它都没
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
index f6ecb473b..6a5c6774c 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
@@ -1,6 +1,16 @@
-### 请求 /update
-GET {{baseUrl}}/crm/customer/page?pageNo=1&pageSize=10&name="张三"
+### 请求 /transfer
+PUT {{baseUrl}}/crm/customer/transfer
+Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
+{
+ "id": 10,
+ "newOwnerUserId": 127
+}
+### 自定义日志记录结果
+### 操作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=客户转移, bizId=10, content=把客户【张三】的负责人从【芋道源码(15612345678)】变更为了【tttt】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/transfer, userIp=127.0.0.1, userAgent=Apache-HttpClient/4.5.14 (Java/17.0.9))
+
+### diff 日志
+### | 操作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=更新客户, bizId=11, content=更新了客户【所属行业】从【H 住宿和餐饮业】修改为【D 电力、热力、燃气及水生产和供应业】;【客户等级】从【C (非优先客户)】修改为【A (重点客户)】;【客户来源】从【线上咨询】修改为【预约上门】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/update, userIp=0:0:0:0:0:0:0:1, userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36)
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
index f06e26e4a..8386e80ed 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -11,20 +11,22 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
+import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
import org.mapstruct.ap.internal.util.Collections;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
-import jakarta.annotation.Resource;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -36,6 +38,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER;
@Tag(name = "管理后台 - CRM 客户")
@RestController
@@ -50,6 +53,8 @@ public class CrmCustomerController {
private DeptApi deptApi;
@Resource
private AdminUserApi adminUserApi;
+ @Resource
+ private OperateLogApi operateLogApi;
@PostMapping("/create")
@Operation(summary = "创建客户")
@@ -59,7 +64,7 @@ public class CrmCustomerController {
}
@PutMapping("/update")
- @Operation(summary = "更新客户")
+ //@Operation(summary = "更新客户")
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
public CommonResult updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
customerService.updateCustomer(updateReqVO);
@@ -123,13 +128,28 @@ public class CrmCustomerController {
}
@PutMapping("/transfer")
- @Operation(summary = "客户转移")
+ //@Operation(summary = "客户转移")
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
public CommonResult transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
customerService.transferCustomer(reqVO, getLoginUserId());
return success(true);
}
+ @GetMapping("/operate-log")
+ @Operation(summary = "获得客户操作日志")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('crm:customer:query')")
+ public CommonResult> getOperateLog(@RequestParam("id") Long id) {
+ // 1. 获取客户
+ CrmCustomerDO customer = customerService.getCustomer(id);
+ if (customer == null) {
+ return success(null);
+ }
+
+ // 2. 获取操作日志
+ return success(operateLogApi.getOperateLogByModuleAndBizId(CRM_CUSTOMER, id));
+ }
+
// TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;
@PutMapping("/lock")
@Operation(summary = "锁定/解锁客户")
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
index 3d03ba807..8049c344b 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
@@ -3,17 +3,20 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
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 com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
+import lombok.Data;
+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;
+import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
/**
* 客户 Base VO,提供给添加、修改、详细的子 VO 使用
@@ -23,57 +26,73 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
public class CrmCustomerBaseVO {
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+ @DiffLogField(name = "客户名称")
@NotEmpty(message = "客户名称不能为空")
private String name;
@Schema(description = "所属行业", example = "1")
+ @DiffLogField(name = "所属行业", function = "getIndustryById")
+ @DictFormat(CRM_CUSTOMER_INDUSTRY)
private Integer industryId;
@Schema(description = "客户等级", example = "2")
+ @DiffLogField(name = "客户等级", function = "getLevel")
@InEnum(CrmCustomerLevelEnum.class)
private Integer level;
@Schema(description = "客户来源", example = "3")
+ @DiffLogField(name = "客户来源", function = "getSource")
private Integer source;
@Schema(description = "手机", example = "18000000000")
+ @DiffLogField(name = "手机")
@Mobile
private String mobile;
@Schema(description = "电话", example = "18000000000")
+ @DiffLogField(name = "电话")
@Telephone
private String telephone;
@Schema(description = "网址", example = "https://www.baidu.com")
+ @DiffLogField(name = "网址")
private String website;
@Schema(description = "QQ", example = "123456789")
+ @DiffLogField(name = "QQ")
@Size(max = 20, message = "QQ长度不能超过 20 个字符")
private String qq;
- @Schema(description = "wechat", example = "123456789")
+ @Schema(description = "微信", example = "123456789")
+ @DiffLogField(name = "微信")
@Size(max = 255, message = "微信长度不能超过 255 个字符")
private String wechat;
- @Schema(description = "email", example = "123456789@qq.com")
+ @Schema(description = "邮箱", example = "123456789@qq.com")
+ @DiffLogField(name = "邮箱")
@Email(message = "邮箱格式不正确")
@Size(max = 255, message = "邮箱长度不能超过 255 个字符")
private String email;
@Schema(description = "客户描述", example = "任意文字")
+ @DiffLogField(name = "客户描述")
@Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
private String description;
@Schema(description = "备注", example = "随便")
+ @DiffLogField(name = "备注")
private String remark;
@Schema(description = "地区编号", example = "20158")
+ @DiffLogField(name = "地区编号", function = "getAreaById")
private Integer areaId;
@Schema(description = "详细地址", example = "北京市海淀区")
+ @DiffLogField(name = "详细地址")
private String detailAddress;
@Schema(description = "下次联系时间")
+ @DiffLogField(name = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java
index c425520a9..9bdc43532 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java
@@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
import jakarta.validation.constraints.NotNull;
+import lombok.Data;
@Schema(description = "管理后台 - CRM 客户转移 Request VO")
@Data
public class CrmCustomerTransferReqVO {
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
- @NotNull(message = "联系人编号不能为空")
+ @NotNull(message = "客户编号不能为空")
private Long id;
/**
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
index 4c4be4a25..a353491e7 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
@@ -7,10 +7,7 @@ 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.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.controller.admin.receivable.vo.receivable.*;
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;
@@ -36,6 +33,7 @@ import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@@ -94,7 +92,7 @@ public class CrmReceivableController {
@Operation(summary = "获得回款分页")
@PreAuthorize("@ss.hasPermission('crm:receivable:query')")
public CommonResult> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) {
- PageResult pageResult = receivableService.getReceivablePage(pageReqVO);
+ PageResult pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId());
return success(convertDetailReceivablePage(pageResult));
}
@@ -102,7 +100,7 @@ public class CrmReceivableController {
@Operation(summary = "获得回款分页,基于指定客户")
public CommonResult> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) {
Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
- PageResult pageResult = receivableService.getReceivablePageByCustomer(pageReqVO);
+ PageResult pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO);
return success(convertDetailReceivablePage(pageResult));
}
@@ -113,7 +111,8 @@ public class CrmReceivableController {
@OperateLog(type = EXPORT)
public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO,
HttpServletResponse response) throws IOException {
- PageResult pageResult = receivableService.getReceivablePage(exportReqVO);
+ exportReqVO.setPageSize(PAGE_SIZE_NONE);
+ PageResult pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId());
// 导出 Excel
ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class,
convertDetailReceivablePage(pageResult).getList());
@@ -142,4 +141,12 @@ public class CrmReceivableController {
return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList);
}
+ @PutMapping("/transfer")
+ @Operation(summary = "回款转移")
+ @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
+ public CommonResult transfer(@Valid @RequestBody CrmReceivableTransferReqVO reqVO) {
+ receivableService.transferReceivable(reqVO, getLoginUserId());
+ return success(true);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
index f8e2e6ecd..9f3c23895 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
@@ -7,10 +7,7 @@ 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.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.controller.admin.receivable.vo.plan.*;
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;
@@ -38,6 +35,7 @@ import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@@ -65,7 +63,7 @@ public class CrmReceivablePlanController {
@Operation(summary = "创建回款计划")
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')")
public CommonResult createReceivablePlan(@Valid @RequestBody CrmReceivablePlanCreateReqVO createReqVO) {
- return success(receivablePlanService.createReceivablePlan(createReqVO));
+ return success(receivablePlanService.createReceivablePlan(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@@ -98,7 +96,7 @@ public class CrmReceivablePlanController {
@Operation(summary = "获得回款计划分页")
@PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
public CommonResult> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
- PageResult pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO);
+ PageResult pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId());
return success(convertDetailReceivablePlanPage(pageResult));
}
@@ -106,7 +104,7 @@ public class CrmReceivablePlanController {
@Operation(summary = "获得回款计划分页,基于指定客户")
public CommonResult> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
- PageResult pageResult = receivablePlanService.getReceivablePlanPageByCustomer(pageReqVO);
+ PageResult pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
return success(convertDetailReceivablePlanPage(pageResult));
}
@@ -117,7 +115,8 @@ public class CrmReceivablePlanController {
@OperateLog(type = EXPORT)
public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO,
HttpServletResponse response) throws IOException {
- PageResult pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO);
+ exportReqVO.setPageSize(PAGE_SIZE_NONE);
+ PageResult pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId());
// 导出 Excel
ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class,
convertDetailReceivablePlanPage(pageResult).getList());
@@ -149,4 +148,12 @@ 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 transfer(@Valid @RequestBody CrmReceivablePlanTransferReqVO reqVO) {
+ receivablePlanService.transferReceivablePlan(reqVO, getLoginUserId());
+ return success(true);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java
index 16681ba3c..f86aa346d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java
@@ -25,7 +25,4 @@ public class CrmReceivablePlanPageReqVO extends PageParam {
@InEnum(CrmSceneTypeEnum.class)
private Integer sceneType; // 场景类型,为 null 时则表示全部
- @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
- private Boolean pool; // null 则表示为不是公海数据
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanTransferReqVO.java
new file mode 100644
index 000000000..09f85e419
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanTransferReqVO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
+
+import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - CRM 回款计划转移 Request VO")
+@Data
+public class CrmReceivablePlanTransferReqVO {
+
+ @Schema(description = "回款计划编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "回款计划编号不能为空")
+ private Long id;
+
+ /**
+ * 新负责人的用户编号
+ */
+ @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "新负责人的用户编号不能为空")
+ private Long newOwnerUserId;
+
+ /**
+ * 老负责人加入团队后的权限级别。如果 null 说明移除
+ *
+ * 关联 {@link CrmPermissionLevelEnum}
+ */
+ @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer oldOwnerPermissionLevel;
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java
index 3eef5d013..1bca32fa3 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java
@@ -27,7 +27,4 @@ public class CrmReceivablePageReqVO extends PageParam {
@InEnum(CrmSceneTypeEnum.class)
private Integer sceneType; // 场景类型,为 null 时则表示全部
- @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
- private Boolean pool; // null 则表示为不是公海数据
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableTransferReqVO.java
new file mode 100644
index 000000000..f94ce4c1b
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableTransferReqVO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
+
+import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - CRM 回款转移 Request VO")
+@Data
+public class CrmReceivableTransferReqVO {
+
+ @Schema(description = "回款编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "回款编号不能为空")
+ private Long id;
+
+ /**
+ * 新负责人的用户编号
+ */
+ @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "新负责人的用户编号不能为空")
+ private Long newOwnerUserId;
+
+ /**
+ * 老负责人加入团队后的权限级别。如果 null 说明移除
+ *
+ * 关联 {@link CrmPermissionLevelEnum}
+ */
+ @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer oldOwnerPermissionLevel;
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java
index 76ea428c7..2649065a1 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java
@@ -1,13 +1,14 @@
package cn.iocoder.yudao.module.crm.convert.clue;
-import java.util.*;
-
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
+import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
/**
* 线索 Convert
@@ -29,4 +30,7 @@ public interface CrmClueConvert {
List convertList02(List list);
+ @Mapping(target = "bizId", source = "reqVO.id")
+ CrmPermissionTransferReqBO convert(CrmClueTransferReqVO reqVO, Long userId);
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java
similarity index 94%
rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java
rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java
index 636835be7..421e3cd98 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java
@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferRe
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
-import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@@ -26,9 +25,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAnd
* @author 芋道源码
*/
@Mapper
-public interface ContactConvert {
+public interface CrmContactConvert {
- ContactConvert INSTANCE = Mappers.getMapper(ContactConvert.class);
+ CrmContactConvert INSTANCE = Mappers.getMapper(CrmContactConvert.class);
CrmContactDO convert(CrmContactCreateReqVO bean);
@@ -48,10 +47,7 @@ public interface ContactConvert {
List convertAllList(List list);
- @Mappings({
- @Mapping(target = "bizId", source = "reqVO.id"),
- @Mapping(target = "newOwnerUserId", source = "reqVO.id")
- })
+ @Mapping(target = "bizId", source = "reqVO.id")
CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId);
/**
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
similarity index 89%
rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java
rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
index 2041ba5de..74b7a4663 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
@@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferRe
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
-import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@@ -23,9 +22,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAnd
* @author dhb52
*/
@Mapper
-public interface ContractConvert {
+public interface CrmContractConvert {
- ContractConvert INSTANCE = Mappers.getMapper(ContractConvert.class);
+ CrmContractConvert INSTANCE = Mappers.getMapper(CrmContractConvert.class);
CrmContractDO convert(CrmContractCreateReqVO bean);
@@ -39,10 +38,7 @@ public interface ContractConvert {
List convertList02(List list);
- @Mappings({
- @Mapping(target = "bizId", source = "reqVO.id"),
- @Mapping(target = "newOwnerUserId", source = "reqVO.id")
- })
+ @Mapping(target = "bizId", source = "reqVO.id")
CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId);
default PageResult convertPage(PageResult pageResult, Map userMap,
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java
index 27502103d..e7340fc86 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java
@@ -3,12 +3,15 @@ package cn.iocoder.yudao.module.crm.convert.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.CrmReceivableRespVO;
+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.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
+import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@@ -58,4 +61,7 @@ public interface CrmReceivableConvert {
findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname()));
}
+ @Mapping(target = "bizId", source = "reqVO.id")
+ CrmPermissionTransferReqBO convert(CrmReceivableTransferReqVO reqVO, Long userId);
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java
index a89140d07..70e930880 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java
@@ -3,13 +3,16 @@ package cn.iocoder.yudao.module.crm.convert.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.CrmReceivablePlanRespVO;
+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.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
+import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@@ -63,4 +66,7 @@ public interface CrmReceivablePlanConvert {
findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname()));
}
+ @Mapping(target = "bizId", source = "reqVO.id")
+ CrmPermissionTransferReqBO convert(CrmReceivablePlanTransferReqVO reqVO, Long userId);
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
index 7c06e57a0..6dfdf8fad 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
@@ -27,14 +28,23 @@ public interface CrmBusinessMapper extends BaseMapperX {
.set(CrmBusinessDO::getOwnerUserId, ownerUserId));
}
+ default PageResult selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
+ return selectPage(pageReqVO, new LambdaQueryWrapperX()
+ .eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号
+ .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
+ .orderByDesc(CrmBusinessDO::getId));
+ }
+
default PageResult selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), CrmBusinessDO::getId,
- userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
+ CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
+ if (!condition) {
+ return PageResult.empty();
+ }
// 拼接自身的查询条件
query.selectAll(CrmBusinessDO.class)
- .eqIfPresent(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号
.likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
.orderByDesc(CrmBusinessDO::getId);
return selectJoinPage(pageReqVO, CrmBusinessDO.class, query);
@@ -43,7 +53,7 @@ public interface CrmBusinessMapper extends BaseMapperX {
default List selectBatchIds(Collection ids, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId);
+ CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId);
return selectJoinList(CrmBusinessDO.class, query);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
index 479a12f1a..cd558dce4 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
@@ -30,8 +30,11 @@ public interface CrmClueMapper extends BaseMapperX {
default PageResult selectPage(CrmCluePageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_LEADS.getType(), CrmClueDO::getId,
- userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(),
+ CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ if (!condition) {
+ return PageResult.empty();
+ }
// 拼接自身的查询条件
query.selectAll(CrmClueDO.class)
.likeIfPresent(CrmClueDO::getName, pageReqVO.getName())
@@ -44,7 +47,7 @@ public interface CrmClueMapper extends BaseMapperX {
default List selectBatchIds(Collection ids, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId);
+ CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId);
return selectJoinList(CrmClueDO.class, query);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
index 189f36f08..adc4ced87 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.contact;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
@@ -27,14 +28,28 @@ public interface CrmContactMapper extends BaseMapperX {
.set(CrmContactDO::getOwnerUserId, ownerUserId));
}
+ default PageResult selectPageByCustomerId(CrmContactPageReqVO pageVO) {
+ return selectPage(pageVO, new LambdaQueryWrapperX()
+ .eq(CrmContactDO::getCustomerId, pageVO.getCustomerId()) // 指定客户编号
+ .likeIfPresent(CrmContactDO::getName, pageVO.getName())
+ .eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile())
+ .eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone())
+ .eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail())
+ .eqIfPresent(CrmContactDO::getQq, pageVO.getQq())
+ .eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat())
+ .orderByDesc(CrmContactDO::getId));
+ }
+
default PageResult selectPage(CrmContactPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_CONTACT.getType(), CrmContactDO::getId,
- userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),
+ CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
+ if (!condition) {
+ return PageResult.empty();
+ }
// 拼接自身的查询条件
query.selectAll(CrmContactDO.class)
- .eqIfPresent(CrmContactDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号
.likeIfPresent(CrmContactDO::getName, pageReqVO.getName())
.eqIfPresent(CrmContactDO::getMobile, pageReqVO.getMobile())
.eqIfPresent(CrmContactDO::getTelephone, pageReqVO.getTelephone())
@@ -48,7 +63,7 @@ public interface CrmContactMapper extends BaseMapperX {
default List selectBatchIds(Collection ids, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
+ CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
return selectJoinList(CrmContactDO.class, query);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
index 37452feb7..60a5ab785 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -27,13 +28,26 @@ public interface CrmContractMapper extends BaseMapperX {
.set(CrmContractDO::getOwnerUserId, ownerUserId));
}
+ default PageResult selectPageByCustomerId(CrmContractPageReqVO pageReqVO) {
+ return selectPage(pageReqVO, new LambdaQueryWrapperX()
+ .eq(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
+ .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
+ .likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
+ .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
+ .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
+ .orderByDesc(CrmContractDO::getId));
+ }
+
default PageResult selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
- // 构建数据权限连表条件
- CrmQueryWrapperUtils.builderPageQuery(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), CrmContractDO::getId,
- userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ // 拼接数据权限的查询条件
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(),
+ CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
+ if (!condition) {
+ return PageResult.empty();
+ }
+ // 拼接自身的查询条件
mpjLambdaWrapperX.selectAll(CrmContractDO.class)
- .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
.likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
.likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
.eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
@@ -45,7 +59,7 @@ public interface CrmContractMapper extends BaseMapperX {
default List selectBatchIds(Collection ids, Long userId) {
MPJLambdaWrapperX mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
// 构建数据权限连表条件
- CrmQueryWrapperUtils.builderListQueryBatch(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
+ CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
return selectJoinList(CrmContractDO.class, mpjLambdaWrapperX);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
index 86c8617b7..b399ca7df 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -30,8 +30,11 @@ public interface CrmCustomerMapper extends BaseMapperX {
default PageResult selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), CrmCustomerDO::getId,
- userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
+ CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
+ if (!condition) {
+ return PageResult.empty();
+ }
// 拼接自身的查询条件
query.selectAll(CrmCustomerDO.class)
.likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
@@ -45,7 +48,7 @@ public interface CrmCustomerMapper extends BaseMapperX {
default List selectBatchIds(Collection ids, Long userId) {
MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
- CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId);
+ CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId);
return selectJoinList(CrmCustomerDO.class, query);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java
index 349318502..e7de279d8 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java
@@ -46,4 +46,10 @@ public interface CrmPermissionMapper extends BaseMapperX {
.eq(CrmPermissionDO::getId, id).eq(CrmPermissionDO::getUserId, userId));
}
+ default int deletePermission(Integer bizType, Long bizId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(CrmPermissionDO::getBizType, bizType)
+ .eq(CrmPermissionDO::getBizId, bizId));
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
index 9da130022..fca4e30e2 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
@@ -3,10 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
+import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
+import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
+import java.util.Collection;
+import java.util.List;
+
/**
* 回款 Mapper
*
@@ -15,15 +22,13 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CrmReceivableMapper extends BaseMapperX {
- default PageResult selectPage(CrmReceivablePageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX()
- .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo())
- .eqIfPresent(CrmReceivableDO::getPlanId, reqVO.getPlanId())
- .eqIfPresent(CrmReceivableDO::getCustomerId, reqVO.getCustomerId())
- .orderByDesc(CrmReceivableDO::getId));
+ default int updateOwnerUserIdById(Long id, Long ownerUserId) {
+ return update(new LambdaUpdateWrapper()
+ .eq(CrmReceivableDO::getId, id)
+ .set(CrmReceivableDO::getOwnerUserId, ownerUserId));
}
- default PageResult selectPageByCustomer(CrmReceivablePageReqVO reqVO) {
+ default PageResult selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX()
.eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递
.eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo())
@@ -31,4 +36,27 @@ public interface CrmReceivableMapper extends BaseMapperX {
.orderByDesc(CrmReceivableDO::getId));
}
+ default PageResult selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) {
+ MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
+ // 拼接数据权限的查询条件
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
+ CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
+ if (!condition) {
+ return PageResult.empty();
+ }
+ // 拼接自身的查询条件
+ query.selectAll(CrmReceivableDO.class)
+ .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo())
+ .eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId())
+ .orderByDesc(CrmReceivableDO::getId);
+ return selectJoinPage(pageReqVO, CrmReceivableDO.class, query);
+ }
+
+ default List selectBatchIds(Collection ids, Long userId) {
+ MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
+ // 拼接数据权限的查询条件
+ CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), ids, userId);
+ return selectJoinList(CrmReceivableDO.class, query);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
index 52d16f0e1..e22c920b9 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
@@ -3,10 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
+import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
+import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
+import java.util.Collection;
+import java.util.List;
+
/**
* 回款计划 Mapper
*
@@ -15,18 +22,40 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CrmReceivablePlanMapper extends BaseMapperX {
- default PageResult selectPage(CrmReceivablePlanPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX()
- .eqIfPresent(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId())
- .eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId())
- .orderByDesc(CrmReceivablePlanDO::getId));
+ default int updateOwnerUserIdById(Long id, Long ownerUserId) {
+ return update(new LambdaUpdateWrapper()
+ .eq(CrmReceivablePlanDO::getId, id)
+ .set(CrmReceivablePlanDO::getOwnerUserId, ownerUserId));
}
- default PageResult selectPageByCustomer(CrmReceivablePlanPageReqVO reqVO) {
+ default PageResult selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX()
.eq(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId()) // 必须传递
.eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId())
.orderByDesc(CrmReceivablePlanDO::getId));
}
+ default PageResult selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
+ MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
+ // 拼接数据权限的查询条件
+ boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
+ CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
+ if (!condition) {
+ return PageResult.empty();
+ }
+ // 拼接自身的查询条件
+ query.selectAll(CrmReceivablePlanDO.class)
+ .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId())
+ .eqIfPresent(CrmReceivablePlanDO::getContractId, pageReqVO.getContractId())
+ .orderByDesc(CrmReceivablePlanDO::getId);
+ return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query);
+ }
+
+ default List selectBatchIds(Collection ids, Long userId) {
+ MPJLambdaWrapperX query = new MPJLambdaWrapperX<>();
+ // 拼接数据权限的查询条件
+ CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), ids, userId);
+ return selectJoinList(CrmReceivablePlanDO.class, query);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmIndustryParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmIndustryParseFunction.java
new file mode 100644
index 000000000..0a468dfa8
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmIndustryParseFunction.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.crm.framework.bizlog.function;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
+import com.mzt.logapi.service.IParseFunction;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
+
+/**
+ * 自定义函数-通过行业编号获取行业信息
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Component
+public class CrmIndustryParseFunction implements IParseFunction {
+
+ @Override
+ public boolean executeBefore() {
+ return true; // 先转换值后对比
+ }
+
+ @Override
+ public String functionName() {
+ return "getIndustryById";
+ }
+
+ @Override
+ public String apply(Object value) {
+ if (value == null) {
+ return "";
+ }
+ if (StrUtil.isEmpty(value.toString())) {
+ return "";
+ }
+
+ // 获取行业信息
+ try {
+ return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());
+ } catch (Exception ignored) {
+ }
+ return "";
+ }
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmLevelParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmLevelParseFunction.java
new file mode 100644
index 000000000..15af42d5e
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmLevelParseFunction.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.crm.framework.bizlog.function;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
+import com.mzt.logapi.service.IParseFunction;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;
+
+/**
+ * 自定义函数-通过客户等级编号获取客户等级信息
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Component
+public class CrmLevelParseFunction implements IParseFunction {
+
+ @Override
+ public boolean executeBefore() {
+ return true; // 先转换值后对比
+ }
+
+ @Override
+ public String functionName() {
+ return "getLevel";
+ }
+
+ @Override
+ public String apply(Object value) {
+ if (value == null) {
+ return "";
+ }
+ if (StrUtil.isEmpty(value.toString())) {
+ return "";
+ }
+
+ // 获取客户等级信息
+ try {
+ return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString());
+ } catch (Exception ignored) {
+ }
+ return "";
+ }
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmSourceParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmSourceParseFunction.java
new file mode 100644
index 000000000..0a630dfe6
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmSourceParseFunction.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.crm.framework.bizlog.function;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
+import com.mzt.logapi.service.IParseFunction;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE;
+
+/**
+ * 自定义函数-通过客户来源编号获取客户来源信息
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Component
+public class CrmSourceParseFunction implements IParseFunction {
+
+ @Override
+ public boolean executeBefore() {
+ return true; // 先转换值后对比
+ }
+
+ @Override
+ public String functionName() {
+ return "getSource";
+ }
+
+ @Override
+ public String apply(Object value) {
+ if (value == null) {
+ return "";
+ }
+ if (StrUtil.isEmpty(value.toString())) {
+ return "";
+ }
+
+ // 获取客户来源信息
+ try {
+ return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString());
+ } catch (Exception ignored) {
+ }
+ return "";
+ }
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/package-info.java
new file mode 100644
index 000000000..b756f540d
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.module.crm.framework.bizlog;
\ No newline at end of file
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
index c46f5f0bf..d81a75d9a 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
@@ -75,10 +75,9 @@ public interface CrmBusinessService {
* 数据权限:基于 {@link CrmCustomerDO} 读取
*
* @param pageReqVO 分页查询
- * @param userId 用户编号
* @return 联系人分页
*/
- PageResult getBusinessPageByCustomer(CrmBusinessPageReqVO pageReqVO, Long userId);
+ PageResult getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO);
/**
* 商机转移
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
index 98630ffdf..f2db01b83 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -111,11 +111,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
@Override
- public PageResult getBusinessPageByCustomer(CrmBusinessPageReqVO pageReqVO, Long userId) {
- // 校验客户存在 TODO @puhui999:这里不校验
- customerService.validateCustomer(pageReqVO.getCustomerId());
- // TODO @puhui999:感觉这里貌似不太复用用 selectPage,因为他可能没商机权限,只是因为能看 customer,所以可以看到列表
- return businessMapper.selectPage(pageReqVO, userId);
+ @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
+ public PageResult getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
+ return businessMapper.selectPageByCustomerId(pageReqVO);
}
@Override
@@ -127,7 +125,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
// 2.1 数据权限转移
crmPermissionService.transferPermission(
CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
-
// 2.2 设置新的负责人
businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
index 52290e106..f5dd48bfa 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.clue;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
import jakarta.validation.Valid;
@@ -64,4 +65,12 @@ public interface CrmClueService {
*/
PageResult getCluePage(CrmCluePageReqVO pageReqVO, Long userId);
+ /**
+ * 线索转移
+ *
+ * @param reqVO 请求
+ * @param userId 用户编号
+ */
+ void transferClue(CrmClueTransferReqVO reqVO, Long userId);
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
index 63e2fcbd2..414a52d17 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
@@ -101,4 +102,18 @@ public class CrmClueServiceImpl implements CrmClueService {
return clueMapper.selectPage(pageReqVO, userId);
}
+ @Override
+ public void transferClue(CrmClueTransferReqVO reqVO, Long userId) {
+ // 1 校验线索是否存在
+ validateClueExists(reqVO.getId());
+
+ // 2.1 数据权限转移
+ crmPermissionService.transferPermission(
+ CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType()));
+ // 2.2 设置新的负责人
+ clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
+
+ // 3. TODO 记录转移日志
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
index 4306d8aea..7d0c1dc0c 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
@@ -3,8 +3,10 @@ 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.CrmContactCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import jakarta.validation.Valid;
import java.util.Collection;
@@ -71,12 +73,19 @@ public interface CrmContactService {
/**
* 获得联系人分页
*
- * 数据权限:基于 {@link CrmContactDO}
+ * 数据权限:基于 {@link CrmCustomerDO}
*
* @param pageVO 分页查询
- * @param userId 用户编号
* @return 联系人分页
*/
- PageResult getContactPageByCustomerId(CrmContactPageReqVO pageVO, Long userId);
+ PageResult getContactPageByCustomerId(CrmContactPageReqVO pageVO);
+
+ /**
+ * 联系人转移
+ *
+ * @param reqVO 请求
+ * @param userId 用户编号
+ */
+ void transferContact(CrmContactTransferReqVO reqVO, Long userId);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
index 39aac0e11..50b1bd4be 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
@@ -3,11 +3,8 @@ package cn.iocoder.yudao.module.crm.service.contact;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBaseVO;
-import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactCreateReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactUpdateReqVO;
-import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert;
+import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
+import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -56,7 +53,7 @@ public class CrmContactServiceImpl implements CrmContactService {
// 1.1 校验
validateRelationDataExists(createReqVO);
// 1.2 插入
- CrmContactDO contact = ContactConvert.INSTANCE.convert(createReqVO);
+ CrmContactDO contact = CrmContactConvert.INSTANCE.convert(createReqVO);
contactMapper.insert(contact);
// 2. 创建数据权限
@@ -73,7 +70,7 @@ public class CrmContactServiceImpl implements CrmContactService {
validateContactExists(updateReqVO.getId());
validateRelationDataExists(updateReqVO);
// 2. 更新
- CrmContactDO updateObj = ContactConvert.INSTANCE.convert(updateReqVO);
+ CrmContactDO updateObj = CrmContactConvert.INSTANCE.convert(updateReqVO);
contactMapper.updateById(updateObj);
}
@@ -134,11 +131,23 @@ public class CrmContactServiceImpl implements CrmContactService {
}
@Override
- public PageResult getContactPageByCustomerId(CrmContactPageReqVO pageVO, Long userId) {
- // 校验用户存在
- customerService.validateCustomer(pageVO.getCustomerId());
- // TODO @puhui999:getBusinessPageByCustomer 同理
- return contactMapper.selectPage(pageVO, userId);
+ @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageVO.customerId", level = CrmPermissionLevelEnum.READ)
+ public PageResult getContactPageByCustomerId(CrmContactPageReqVO pageVO) {
+ return contactMapper.selectPageByCustomerId(pageVO);
+ }
+
+ @Override
+ public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
+ // 1 校验联系人是否存在
+ validateContactExists(reqVO.getId());
+
+ // 2.1 数据权限转移
+ crmPermissionService.transferPermission(
+ CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()));
+ // 2.2 设置新的负责人
+ contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
+
+ // 3. TODO 记录转移日志
}
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
index 0531dbe38..7d3f83335 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
@@ -75,12 +75,9 @@ public interface CrmContractService {
* 数据权限:基于 {@link CrmCustomerDO} 读取
*
* @param pageReqVO 分页查询
- * @param userId 用户编号
* @return 联系人分页
*/
- default PageResult getContractPageByCustomer(CrmContractPageReqVO pageReqVO, Long userId) {
- return getContractPage(pageReqVO, userId);
- }
+ PageResult getContractPageByCustomerId(CrmContractPageReqVO pageReqVO);
/**
* 合同转移
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
index 23795f94e..6f2fc16ab 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractCreat
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractUpdateReqVO;
-import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert;
+import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -44,7 +44,7 @@ public class CrmContractServiceImpl implements CrmContractService {
@Override
public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) {
// 插入
- CrmContractDO contract = ContractConvert.INSTANCE.convert(createReqVO);
+ CrmContractDO contract = CrmContractConvert.INSTANCE.convert(createReqVO);
contractMapper.insert(contract);
// 创建数据权限
@@ -61,7 +61,7 @@ public class CrmContractServiceImpl implements CrmContractService {
// 校验存在
validateContractExists(updateReqVO.getId());
// 更新
- CrmContractDO updateObj = ContractConvert.INSTANCE.convert(updateReqVO);
+ CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO);
contractMapper.updateById(updateObj);
}
@@ -104,6 +104,12 @@ public class CrmContractServiceImpl implements CrmContractService {
return contractMapper.selectPage(pageReqVO, userId);
}
+ @Override
+ @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
+ public PageResult getContractPageByCustomerId(CrmContractPageReqVO pageReqVO) {
+ return contractMapper.selectPageByCustomerId(pageReqVO);
+ }
+
@Override
@Transactional(rollbackFor = Exception.class)
public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
@@ -112,7 +118,7 @@ public class CrmContractServiceImpl implements CrmContractService {
// 2.1 数据权限转移
crmPermissionService.transferPermission(
- ContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
+ CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
// 2.2 设置负责人
contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
index 93589a5b0..87a01653b 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.customer;
import cn.hutool.core.collection.CollUtil;
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.CrmCustomerCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
@@ -15,6 +16,9 @@ import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
+import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -24,6 +28,8 @@ import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER;
+import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS;
import static java.util.Collections.singletonList;
/**
@@ -59,11 +65,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
@Override
@Transactional(rollbackFor = Exception.class)
+ @LogRecord(success = "更新了客户{_DIFF{#updateReqVO}}", type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}")
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) {
// 校验存在
- validateCustomerExists(updateReqVO.getId());
+ CrmCustomerDO oldCustomerDO = validateCustomerExists(updateReqVO.getId());
+ // __DIFF 函数传递了一个参数,传递的参数是修改之后的对象,这种方式需要在方法内部向 LogRecordContext 中 put 一个变量,代表是之前的对象,这个对象可以是null
+ LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomerDO, CrmCustomerUpdateReqVO.class));
// 更新
CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);
customerMapper.updateById(updateObj);
@@ -82,10 +91,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
}
- private void validateCustomerExists(Long id) {
- if (customerMapper.selectById(id) == null) {
+ private CrmCustomerDO validateCustomerExists(Long id) {
+ CrmCustomerDO customerDO = customerMapper.selectById(id);
+ if (customerDO == null) {
throw exception(CUSTOMER_NOT_EXISTS);
}
+ return customerDO;
}
@Override
@@ -114,7 +125,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
*/
@Override
public void validateCustomer(Long customerId) {
- // TODO puhui999: 不返回客户不走校验应该可行
// 校验客户是否存在
if (customerId == null) {
throw exception(CUSTOMER_NOT_EXISTS);
@@ -127,11 +137,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
@Override
@Transactional(rollbackFor = Exception.class)
+ @LogRecord(success = TRANSFER_CUSTOMER_LOG_SUCCESS, type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}")
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
// 1. 校验客户是否存在
validateCustomer(reqVO.getId());
-
+ // 添加 crmCustomer 到日志上下文 TODO 日志记录放在 service 里是因为已经过了权限校验查询时不用走两次校验
+ LogRecordContext.putVariable("crmCustomer", customerMapper.selectById(reqVO.getId()));
// 2.1 数据权限转移
crmPermissionService.transferPermission(
CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()));
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
index d7ebb572b..76f37f909 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
@@ -138,15 +138,13 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void deletePermission(Integer bizType, Long bizId) {
- // TODO @puhui999:这种直接写条件删除;不需要先查询,再删除
- List permissionList = crmPermissionMapper.selectByBizTypeAndBizId(bizType, bizId);
- if (CollUtil.isEmpty(permissionList)) {
- return;
- }
-
// 删除数据权限
- crmPermissionMapper.deleteBatchIds(convertSet(permissionList, CrmPermissionDO::getId));
+ int deletedCol = crmPermissionMapper.deletePermission(bizType, bizId);
+ if (deletedCol == 0) {
+ throw exception(CRM_PERMISSION_NOT_EXISTS);
+ }
}
@Override
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
index ba8a62d04..93d05e651 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
@@ -3,11 +3,12 @@ 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;
-
import jakarta.validation.Valid;
+
import java.util.Collection;
import java.util.List;
@@ -24,7 +25,7 @@ public interface CrmReceivablePlanService {
* @param createReqVO 创建信息
* @return 编号
*/
- Long createReceivablePlan(@Valid CrmReceivablePlanCreateReqVO createReqVO);
+ Long createReceivablePlan(@Valid CrmReceivablePlanCreateReqVO createReqVO, Long userId);
/**
* 更新回款计划
@@ -62,9 +63,10 @@ public interface CrmReceivablePlanService {
* 数据权限:基于 {@link CrmReceivablePlanDO} 读取
*
* @param pageReqVO 分页查询
+ * @param userId 用户编号
* @return 回款计划分页
*/
- PageResult getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO);
+ PageResult getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId);
/**
* 获得回款计划分页,基于指定客户
@@ -74,6 +76,14 @@ public interface CrmReceivablePlanService {
* @param pageReqVO 分页查询
* @return 回款计划分页
*/
- PageResult getReceivablePlanPageByCustomer(CrmReceivablePlanPageReqVO pageReqVO);
+ PageResult getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO);
+
+ /**
+ * 回款计划转移
+ *
+ * @param reqVO 请求
+ * @param userId 用户编号
+ */
+ void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
index 7fe055690..39154dd5d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
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.convert.receivable.CrmReceivablePlanConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -17,10 +18,12 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
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 jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
@@ -28,7 +31,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
// TODO @liuhongfeng:参考 CrmReceivableServiceImpl 写的 todo 哈;
-// TODO @puhui999:数据权限
+
/**
* 回款计划 Service 实现类
*
@@ -45,9 +48,11 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
private CrmContractService contractService;
@Resource
private CrmCustomerService customerService;
+ @Resource
+ private CrmPermissionService crmPermissionService;
@Override
- public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO) {
+ public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) {
// 插入
CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO);
receivablePlan.setFinishStatus(false);
@@ -55,29 +60,33 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
checkReceivablePlan(receivablePlan);
receivablePlanMapper.insert(receivablePlan);
+ // 创建数据权限
+ crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType())
+ .setBizId(receivablePlan.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
// 返回
return receivablePlan.getId();
}
private void checkReceivablePlan(CrmReceivablePlanDO receivablePlan) {
- if(ObjectUtil.isNull(receivablePlan.getContractId())){
+ if (ObjectUtil.isNull(receivablePlan.getContractId())) {
throw exception(CONTRACT_NOT_EXISTS);
}
CrmContractDO contract = contractService.getContract(receivablePlan.getContractId());
- if(ObjectUtil.isNull(contract)){
+ if (ObjectUtil.isNull(contract)) {
throw exception(CONTRACT_NOT_EXISTS);
}
CrmCustomerDO customer = customerService.getCustomer(receivablePlan.getCustomerId());
- if(ObjectUtil.isNull(customer)){
+ if (ObjectUtil.isNull(customer)) {
throw exception(CUSTOMER_NOT_EXISTS);
}
}
@Override
+ @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) {
// 校验存在
validateReceivablePlanExists(updateReqVO.getId());
@@ -88,6 +97,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
}
@Override
+ @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
public void deleteReceivablePlan(Long id) {
// 校验存在
validateReceivablePlanExists(id);
@@ -102,6 +112,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
}
@Override
+ @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.READ)
public CrmReceivablePlanDO getReceivablePlan(Long id) {
return receivablePlanMapper.selectById(id);
}
@@ -115,14 +126,28 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
}
@Override
- public PageResult getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO) {
- return receivablePlanMapper.selectPage(pageReqVO);
+ public PageResult getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
+ return receivablePlanMapper.selectPage(pageReqVO, userId);
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
- public PageResult getReceivablePlanPageByCustomer(CrmReceivablePlanPageReqVO pageReqVO) {
- return receivablePlanMapper.selectPageByCustomer(pageReqVO);
+ public PageResult getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO) {
+ return receivablePlanMapper.selectPageByCustomerId(pageReqVO);
+ }
+
+ @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 记录转移日志
}
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
index bf9e5571b..79be4b338 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
@@ -3,11 +3,12 @@ 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;
-
import jakarta.validation.Valid;
+
import java.util.Collection;
import java.util.List;
@@ -62,9 +63,10 @@ public interface CrmReceivableService {
* 数据权限:基于 {@link CrmReceivableDO} 读取
*
* @param pageReqVO 分页查询
+ * @param userId 用户编号
* @return 回款分页
*/
- PageResult getReceivablePage(CrmReceivablePageReqVO pageReqVO);
+ PageResult getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId);
/**
* 获得回款分页,基于指定客户
@@ -74,6 +76,14 @@ public interface CrmReceivableService {
* @param pageReqVO 分页查询
* @return 回款分页
*/
- PageResult getReceivablePageByCustomer(CrmReceivablePageReqVO pageReqVO);
+ PageResult getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO);
+
+ /**
+ * 回款转移
+ *
+ * @param reqVO 请求
+ * @param userId 用户编号
+ */
+ void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId);
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
index ac3b71e08..38bf5266e 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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.convert.receivable.CrmReceivableConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -20,10 +21,11 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
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 jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
@@ -48,6 +50,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
private CrmCustomerService customerService;
@Resource
private CrmReceivablePlanService receivablePlanService;
+ @Resource
+ private CrmPermissionService crmPermissionService;
// TODO @liuhongfeng:创建还款后,是不是什么时候,要更新 plan?
@Override
@@ -70,22 +74,22 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
// TODO @liuhongfeng:这里的括号要注意排版;
private void checkReceivable(CrmReceivableDO receivable) {
// TODO @liuhongfeng:这个放在参数校验合适
- if(ObjectUtil.isNull(receivable.getContractId())){
+ if (ObjectUtil.isNull(receivable.getContractId())) {
throw exception(CONTRACT_NOT_EXISTS);
}
CrmContractDO contract = contractService.getContract(receivable.getContractId());
- if(ObjectUtil.isNull(contract)){
+ if (ObjectUtil.isNull(contract)) {
throw exception(CONTRACT_NOT_EXISTS);
}
CrmCustomerDO customer = customerService.getCustomer(receivable.getCustomerId());
- if(ObjectUtil.isNull(customer)){
+ if (ObjectUtil.isNull(customer)) {
throw exception(CUSTOMER_NOT_EXISTS);
}
CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(receivable.getPlanId());
- if(ObjectUtil.isNull(receivablePlan)){
+ if (ObjectUtil.isNull(receivablePlan)) {
throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
}
@@ -129,16 +133,29 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
return receivableMapper.selectBatchIds(ids);
}
- // TODO @芋艿:数据权限
@Override
- public PageResult getReceivablePage(CrmReceivablePageReqVO pageReqVO) {
- return receivableMapper.selectPage(pageReqVO);
+ public PageResult getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId) {
+ return receivableMapper.selectPage(pageReqVO, userId);
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
- public PageResult getReceivablePageByCustomer(CrmReceivablePageReqVO pageReqVO) {
- return receivableMapper.selectPageByCustomer(pageReqVO);
+ public PageResult getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) {
+ 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 记录转移日志
}
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java
index 5eb80b292..dfdb3ecb9 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java
@@ -6,6 +6,7 @@ import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
+import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
@@ -17,7 +18,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
- * CRM 分页查询工具类
+ * CRM 查询工具类
*
* @author HUIHUI
*/
@@ -26,17 +27,17 @@ public class CrmQueryWrapperUtils {
/**
* 构造 CRM 数据类型数据分页查询条件
*
- * @param query 连表查询对象
- * @param bizType 数据类型 {@link CrmBizTypeEnum}
- * @param bizId 数据编号
- * @param userId 用户编号
- * @param sceneType 场景类型
- * @param pool 公海
+ * @param query 连表查询对象
+ * @param bizType 数据类型 {@link CrmBizTypeEnum}
+ * @param bizId 数据编号
+ * @param userId 用户编号
+ * @param sceneType 场景类型
+ * @param pool 公海
+ * @return 是否 (是:需要执行查询,否:不需要查询调用方法直接返回空)
*/
- // TODO @puhui999:bizId 直接传递会不会简单点
- // TODO @puhui999:builderPageQuery 应该不仅仅适合于分页查询,应该适用于所有的查询;可以改成 appendPermissionCondition
- public static , S> void builderPageQuery(T query, Integer bizType, SFunction bizId,
- Long userId, Integer sceneType, Boolean pool) {
+ // TODO @puhui999:bizId 直接传递会不会简单点 回复:还是需要 SFunction 因为分页连表时不知道 bizId 是多少
+ public static , S> boolean appendPermissionCondition(T query, Integer bizType, SFunction bizId,
+ Long userId, Integer sceneType, Boolean pool) {
// 1. 构建数据权限连表条件
if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限
query.innerJoin(CrmPermissionDO.class, on ->
@@ -48,52 +49,55 @@ public class CrmQueryWrapperUtils {
query.eq("owner_user_id", userId);
}
// 2.2 场景二:我参与的数据
- // TODO @puhui999:参与,指的是有读写权限噢;可以把 1. 的合并到 2.2 里;因为 2.1 不需要;
if (CrmSceneTypeEnum.isInvolved(sceneType)) {
- query.ne("owner_user_id", userId);
+ query
+ .ne("owner_user_id", userId)
+ .and(q -> q.eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel())
+ .or()
+ .eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.WRITE.getLevel()));
+
}
// 2.3 场景三:下属负责的数据
if (CrmSceneTypeEnum.isSubordinate(sceneType)) {
List subordinateUsers = getAdminUserApi().getUserListBySubordinate(userId);
- // TODO @puhui999:如果为空,不拼接,就是查询了所有数据呀?
- if (CollUtil.isNotEmpty(subordinateUsers)) {
- query.in("owner_user_id", convertSet(subordinateUsers, AdminUserRespDTO::getId));
+ if (CollUtil.isEmpty(subordinateUsers)) {
+ return false;
}
+ query.in("owner_user_id", convertSet(subordinateUsers, AdminUserRespDTO::getId));
}
- // 2. 拼接公海的查询条件
+ // 3. 拼接公海的查询条件
if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海
query.isNull("owner_user_id");
} else { // 情况二:不是公海
query.isNotNull("owner_user_id");
}
+
+ return true;
}
/**
* 构造 CRM 数据类型批量数据查询条件
*
- * @param query 连表查询对象
- * @param bizType 数据类型 {@link CrmBizTypeEnum}
- * @param bizIds 数据编号
- * @param userId 用户编号
+ * @param query 连表查询对象
+ * @param bizType 数据类型 {@link CrmBizTypeEnum}
+ * @param bizIds 数据编号
+ * @param userId 用户编号
*/
- // TODO @puhui999:可以改成 appendPermissionCondition
- // TODO @puhui999:S 是不是可以删除
- public static , S> void builderListQueryBatch(T query, Integer bizType, Collection bizIds, Long userId) {
- // TODO @puhui999:这里先 if return 简单点
- if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限
- query.innerJoin(CrmPermissionDO.class, on ->
- on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds)
- .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId));
+ public static > void appendPermissionCondition(T query, Integer bizType, Collection bizIds, Long userId) {
+ if (ObjUtil.equal(validateAdminUser(userId), Boolean.TRUE)) {// 管理员不需要数据权限
+ return;
}
+
+ query.innerJoin(CrmPermissionDO.class, on ->
+ on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds)
+ .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId));
}
- // TODO @puhui999:需要加个变量,不用每次都拿哈;
private static AdminUserApi getAdminUserApi() {
- return SpringUtil.getBean(AdminUserApi.class);
+ return AdminUserApiHolder.ADMIN_USER_API;
}
- // TODO @puhui999:需要实现;
/**
* 校验用户是否是管理员
*
@@ -101,7 +105,27 @@ public class CrmQueryWrapperUtils {
* @return 是/否
*/
private static boolean validateAdminUser(Long userId) {
+ // TODO 查询权限配置表用户的角色信息
+ //CrmPermissionConfig permissionConfig = crmPermissionConfigService.getPermissionConfigByUserId(userId);
+ //if (permissionConfig == null) {
+ // return false;
+ //}
+ //// 校验是否为管理员
+ //if (permissionConfig.getIsAdmin()){
+ // return true;
+ //}
return false;
}
+ /**
+ * 静态内部类实现 AdminUserApi 单例获取
+ *
+ * @author HUIHUI
+ */
+ private static class AdminUserApiHolder {
+
+ private static final AdminUserApi ADMIN_USER_API = SpringUtil.getBean(AdminUserApi.class);
+
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java
index 7fa8986b0..4f17abcba 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java
@@ -7,12 +7,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceiv
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;
+import jakarta.annotation.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
-import jakarta.annotation.Resource;
-
+import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@@ -22,6 +22,7 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PL
import static org.junit.jupiter.api.Assertions.*;
// TODO 芋艿:后续,需要补充测试用例
+
/**
* {@link CrmReceivablePlanServiceImpl} 的单元测试类
*
@@ -43,7 +44,7 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest {
CrmReceivablePlanCreateReqVO reqVO = randomPojo(CrmReceivablePlanCreateReqVO.class);
// 调用
- Long receivablePlanId = receivablePlanService.createReceivablePlan(reqVO);
+ Long receivablePlanId = receivablePlanService.createReceivablePlan(reqVO, 1L);
// 断言
assertNotNull(receivablePlanId);
// 校验记录的属性是否正确
@@ -87,8 +88,8 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest {
// 调用
receivablePlanService.deleteReceivablePlan(id);
- // 校验数据不存在了
- assertNull(crmReceivablePlanMapper.selectById(id));
+ // 校验数据不存在了
+ assertNull(crmReceivablePlanMapper.selectById(id));
}
@Test
@@ -103,34 +104,34 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest {
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetReceivablePlanPage() {
- // mock 数据
- CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class, o -> { // 等会查询到
- o.setPeriod(null);
- o.setReturnTime(null);
- o.setRemindDays(null);
- o.setRemindTime(null);
- o.setCustomerId(null);
- o.setContractId(null);
- o.setOwnerUserId(null);
- o.setRemark(null);
- o.setCreateTime(null);
- });
- crmReceivablePlanMapper.insert(dbReceivablePlan);
- // 测试 customerId 不匹配
- crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setCustomerId(null)));
- // 测试 contractId 不匹配
- crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setContractId(null)));
- // 准备参数
- CrmReceivablePlanPageReqVO reqVO = new CrmReceivablePlanPageReqVO();
- reqVO.setCustomerId(null);
- reqVO.setContractId(null);
-
- // 调用
- PageResult pageResult = receivablePlanService.getReceivablePlanPage(reqVO);
- // 断言
- assertEquals(1, pageResult.getTotal());
- assertEquals(1, pageResult.getList().size());
- assertPojoEquals(dbReceivablePlan, pageResult.getList().get(0));
+ // mock 数据
+ CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class, o -> { // 等会查询到
+ o.setPeriod(null);
+ o.setReturnTime(null);
+ o.setRemindDays(null);
+ o.setRemindTime(null);
+ o.setCustomerId(null);
+ o.setContractId(null);
+ o.setOwnerUserId(null);
+ o.setRemark(null);
+ o.setCreateTime(null);
+ });
+ crmReceivablePlanMapper.insert(dbReceivablePlan);
+ // 测试 customerId 不匹配
+ crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setCustomerId(null)));
+ // 测试 contractId 不匹配
+ crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setContractId(null)));
+ // 准备参数
+ CrmReceivablePlanPageReqVO reqVO = new CrmReceivablePlanPageReqVO();
+ reqVO.setCustomerId(null);
+ reqVO.setContractId(null);
+ reqVO.setPageSize(PAGE_SIZE_NONE);
+ // 调用
+ PageResult pageResult = receivablePlanService.getReceivablePlanPage(reqVO, 1L);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbReceivablePlan, pageResult.getList().get(0));
}
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java
index 93ac0c99b..1f3d821af 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java
@@ -7,12 +7,11 @@ import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.Crm
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
+import jakarta.annotation.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
-import jakarta.annotation.Resource;
-
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@@ -133,7 +132,7 @@ public class CrmCrmReceivableServiceImplTest extends BaseDbUnitTest {
reqVO.setCustomerId(null);
// 调用
- PageResult pageResult = receivableService.getReceivablePage(reqVO);
+ PageResult pageResult = receivableService.getReceivablePage(reqVO, 1L);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm
index fcc2e9197..835c0192e 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm
@@ -63,7 +63,7 @@ export function export${simpleClassName}Excel(params) {
responseType: 'blob'
})
}
-## 特殊:主子表专属逻辑 TODO @puhui999:下面方法的【空格】不太对
+## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
@@ -76,7 +76,7 @@ export function export${simpleClassName}Excel(params) {
// ==================== 子表($subTable.classComment) ====================
## 情况一:MASTER_ERP 时,需要分查询页子表
- #if ( $table.templateType == 11 )
+ #if ($table.templateType == 11)
// 获得${subTable.classComment}分页
export function get${subSimpleClassName}Page(params) {
return request({
@@ -87,7 +87,7 @@ export function export${simpleClassName}Excel(params) {
}
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
- #if ( $subTable.subJoinMany )
+ #if ($subTable.subJoinMany)
// 获得${subTable.classComment}列表
export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) {
return request({
@@ -106,7 +106,7 @@ export function export${simpleClassName}Excel(params) {
#end
#end
## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
- #if ( $table.templateType == 11 )
+ #if ($table.templateType == 11)
// 新增${subTable.classComment}
export function create${subSimpleClassName}(data) {
return request({
@@ -115,7 +115,6 @@ export function export${simpleClassName}Excel(params) {
data
})
}
-
// 修改${subTable.classComment}
export function update${subSimpleClassName}(data) {
return request({
@@ -124,7 +123,6 @@ export function export${simpleClassName}Excel(params) {
data
})
}
-
// 删除${subTable.classComment}
export function delete${subSimpleClassName}(id) {
return request({
@@ -132,7 +130,6 @@ export function export${simpleClassName}Excel(params) {
method: 'delete'
})
}
-
// 获得${subTable.classComment}
export function get${subSimpleClassName}(id) {
return request({
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
index a9db09ec8..354f5b359 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
@@ -7,11 +7,10 @@ import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivity
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
+import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
-
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS;
@@ -25,21 +24,21 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINA
public class CombinationRecordApiImpl implements CombinationRecordApi {
@Resource
- private CombinationRecordService recordService;
+ private CombinationRecordService combinationRecordService;
@Override
public void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
- recordService.validateCombinationRecord(userId, activityId, headId, skuId, count);
+ combinationRecordService.validateCombinationRecord(userId, activityId, headId, skuId, count);
}
@Override
public CombinationRecordCreateRespDTO createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
- return CombinationActivityConvert.INSTANCE.convert4(recordService.createCombinationRecord(reqDTO));
+ return CombinationActivityConvert.INSTANCE.convert4(combinationRecordService.createCombinationRecord(reqDTO));
}
@Override
public boolean isCombinationRecordSuccess(Long userId, Long orderId) {
- CombinationRecordDO record = recordService.getCombinationRecord(userId, orderId);
+ CombinationRecordDO record = combinationRecordService.getCombinationRecord(userId, orderId);
if (record == null) {
throw exception(COMBINATION_RECORD_NOT_EXISTS);
}
@@ -48,7 +47,7 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
@Override
public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
- return recordService.validateJoinCombination(userId, activityId, headId, skuId, count);
+ return combinationRecordService.validateJoinCombination(userId, activityId, headId, skuId, count);
}
}
diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java
index 63193b029..9da5927f1 100644
--- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java
+++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO;
import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO;
@@ -47,10 +48,9 @@ public interface MemberSignInRecordConvert {
// 1. 计算是第几天签到
configs.sort(Comparator.comparing(MemberSignInConfigDO::getDay));
MemberSignInConfigDO lastConfig = CollUtil.getLast(configs); // 最大签到天数配置
- // 1.2. 计算今天是第几天签到
+ // 1.2. 计算今天是第几天签到 (只有连续签到才加否则重置为 1)
int day = 1;
- // TODO @puhui999:要判断是不是昨天签到的;是否是昨天的判断,可以抽个方法到 util 里
- if (lastRecord != null) {
+ if (lastRecord != null && DateUtils.isYesterday(lastRecord.getCreateTime())) {
day = lastRecord.getDay() + 1;
}
// 1.3 判断是否超出了最大签到配置
diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java
index 79f7c8729..cdf32105a 100644
--- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java
+++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java
@@ -19,14 +19,12 @@ import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
+import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
-import java.time.LocalDate;
-import java.util.Comparator;
import java.util.List;
import java.util.Set;
@@ -77,50 +75,15 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
}
summary.setTodaySignIn(DateUtils.isToday(lastRecord.getCreateTime()));
- // 4. 校验今天是否签到,没有签到则直接返回
+ // 4.1 校验今天是否签到,没有签到则直接返回
if (!summary.getTodaySignIn()) {
return summary;
}
- // 4.1. 判断连续签到天数
- // TODO @puhui999:连续签到,可以基于 lastRecord 的 day 和当前时间判断呀?按 day 统计连续签到天数可能不准确
- // 1. day 只是记录第几天签到的有可能不连续,比如第一次签到是周一,第二次签到是周三这样 lastRecord 的 day 为 2 但是并不是连续的两天
- // 2. day 超出签到规则的最大天数会重置到从第一天开始签到(我理解为开始下一轮,类似一周签到七天七天结束下周又从周一开始签到)
- // 1. 回复:周三签到,day 要归 1 呀。连续签到哈;
- List signInRecords = signInRecordMapper.selectListByUserId(userId);
- signInRecords.sort(Comparator.comparing(MemberSignInRecordDO::getCreateTime).reversed()); // 根据签到时间倒序
- summary.setContinuousDay(calculateConsecutiveDays(signInRecords));
+ // 4.2 连续签到天数
+ summary.setContinuousDay(lastRecord.getDay());
return summary;
}
- /**
- * 计算连续签到天数
- *
- * @param signInRecords 签到记录列表
- * @return int 连续签到天数
- */
- public int calculateConsecutiveDays(List signInRecords) {
- int consecutiveDays = 1; // 初始连续天数为1
- LocalDate previousDate = null;
-
- for (MemberSignInRecordDO record : signInRecords) {
- LocalDate currentDate = record.getCreateTime().toLocalDate();
-
- if (previousDate != null) {
- // 检查相邻两个日期是否连续
- if (currentDate.minusDays(1).isEqual(previousDate)) {
- consecutiveDays++;
- } else {
- // 如果日期不连续,停止遍历
- break;
- }
- }
-
- previousDate = currentDate;
- }
-
- return consecutiveDays;
- }
-
@Override
public PageResult getSignInRecordPage(MemberSignInRecordPageReqVO pageReqVO) {
// 根据用户昵称查询出用户ids
diff --git a/yudao-module-system/yudao-module-system-api/pom.xml b/yudao-module-system/yudao-module-system-api/pom.xml
index 655db05a7..3c91bd974 100644
--- a/yudao-module-system/yudao-module-system-api/pom.xml
+++ b/yudao-module-system/yudao-module-system-api/pom.xml
@@ -22,6 +22,23 @@
yudao-common
+
+
+
+ io.github.mouzt
+ bizlog-sdk
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
org.springframework.boot
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java
index 6e28c7d78..ad42c87f7 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java
@@ -1,9 +1,11 @@
package cn.iocoder.yudao.module.system.api.logger;
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
-
+import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
import jakarta.validation.Valid;
+import java.util.List;
+
/**
* 操作日志 API 接口
*
@@ -18,4 +20,13 @@ public interface OperateLogApi {
*/
void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO);
+ /**
+ * 获取指定模块的指定数据的操作日志
+ *
+ * @param module 操作模块
+ * @param bizId 操作模块编号
+ * @return 操作日志
+ */
+ List getOperateLogByModuleAndBizId(String module, Long bizId);
+
}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java
new file mode 100644
index 000000000..9a918abe3
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java
@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.system.api.logger.dto;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+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.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
+
+/**
+ * 系统操作日志 Resp DTO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class OperateLogV2RespDTO {
+
+ /**
+ * 链路追踪编号
+ *
+ * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
+ */
+ private String traceId;
+ /**
+ * 用户编号
+ *
+ * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ *
+ * 关联 {@link UserTypeEnum}
+ */
+ private Integer userType;
+ /**
+ * 操作模块
+ */
+ private String module;
+ /**
+ * 操作名
+ */
+ private String name;
+ /**
+ * 操作模块业务编号
+ */
+ private Long bizId;
+ /**
+ * 操作内容,记录整个操作的明细
+ * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
+ */
+ private String content;
+ /**
+ * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
+ * 例如说,记录订单编号,{ orderId: "1"}
+ */
+ private String extra;
+
+ /**
+ * 请求方法名
+ */
+ private String requestMethod;
+ /**
+ * 请求地址
+ */
+ private String requestUrl;
+ /**
+ * 用户 IP
+ */
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ private String userAgent;
+
+ /**
+ * 创建时间
+ */
+ // TODO puhui999: 木得效果怎么肥事
+ @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
+ private LocalDateTime createTime;
+ /**
+ * 创建者,关联 AdminUserDO#getId
+ */
+ private String creator;
+ /**
+ * 创建者名称
+ */
+ private String creatorName;
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java
index 20aa2635d..d748bcc23 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java
@@ -1,11 +1,23 @@
package cn.iocoder.yudao.module.system.api.logger;
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
+import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
+import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
/**
* 操作日志 API 实现类
@@ -18,10 +30,31 @@ public class OperateLogApiImpl implements OperateLogApi {
@Resource
private OperateLogService operateLogService;
+ @Resource
+ private AdminUserService adminUserService;
@Override
public void createOperateLog(OperateLogCreateReqDTO createReqDTO) {
operateLogService.createOperateLog(createReqDTO);
}
+ @Override
+ public List getOperateLogByModuleAndBizId(String module, Long bizId) {
+ List logList = operateLogService.getOperateLogByModuleAndBizId(module, bizId);
+ if (CollUtil.isEmpty(logList)) {
+ return Collections.emptyList();
+ }
+
+ // 获取用户
+ List userList = adminUserService.getUserList(convertSet(logList, item -> Long.parseLong(item.getCreator())));
+ Map userMap = convertMap(userList, AdminUserDO::getId);
+ return convertList(logList, item -> {
+ OperateLogV2RespDTO bean = BeanUtils.toBean(item, OperateLogV2RespDTO.class);
+ findAndThen(userMap, Long.parseLong(item.getCreator()), user -> {
+ bean.setCreatorName(user.getNickname());
+ });
+ return bean;
+ });
+ }
+
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java
new file mode 100644
index 000000000..1b7b1eaab
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java
@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.logger;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 操作日志表 V2
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "system_operate_log_v2", autoResultMap = true)
+@KeySequence("system_operate_log_seq_v2") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OperateLogV2DO extends BaseDO {
+
+ /**
+ * 日志主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 链路追踪编号
+ *
+ * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
+ */
+ private String traceId;
+ /**
+ * 用户编号
+ *
+ * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ *
+ * 关联 {@link UserTypeEnum}
+ */
+ private Integer userType;
+ /**
+ * 操作模块
+ */
+ private String module;
+ /**
+ * 操作名
+ */
+ private String name;
+ /**
+ * 操作模块业务编号
+ */
+ private Long bizId;
+ /**
+ * 操作内容,记录整个操作的明细
+ * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
+ */
+ private String content;
+ /**
+ * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
+ * 例如说,记录订单编号,{ orderId: "1"}
+ */
+ private String extra;
+ /**
+ * 请求方法名
+ */
+ private String requestMethod;
+ /**
+ * 请求地址
+ */
+ private String requestUrl;
+ /**
+ * 用户 IP
+ */
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ private String userAgent;
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java
new file mode 100644
index 000000000..362f0d2c4
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.system.dal.mysql.logger;
+
+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.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface OperateLogV2Mapper extends BaseMapperX {
+
+ default PageResult selectPage(OperateLogPageReqVO reqVO, Collection userIds) {
+ LambdaQueryWrapperX query = new LambdaQueryWrapperX()
+ .likeIfPresent(OperateLogV2DO::getModule, reqVO.getModule())
+ .inIfPresent(OperateLogV2DO::getUserId, userIds);
+ query.orderByDesc(OperateLogV2DO::getId); // 降序
+ return selectPage(reqVO, query);
+ }
+
+ default List selectListByModuleAndBizId(String module, Long bizId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(OperateLogV2DO::getModule, module)
+ .eq(OperateLogV2DO::getBizId, bizId)
+ .orderByDesc(OperateLogV2DO::getCreateTime));
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java
new file mode 100644
index 000000000..9e6a9dd85
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.system.framework.bizlog.config;
+
+import com.mzt.logapi.starter.annotation.EnableLogRecord;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * mzt-biz-log 配置类
+ *
+ * @author HUIHUI
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦
+public class YudaoOperateLogV2Configuration {
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java
new file mode 100644
index 000000000..60fee85be
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java
@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.system.framework.bizlog.function;
+
+import cn.hutool.core.util.ObjUtil;
+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 com.mzt.logapi.service.IParseFunction;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自定义函数-通过用户编号获取用户信息
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Component
+public class AdminUserParseFunction implements IParseFunction {
+
+ @Resource
+ private AdminUserApi adminUserApi;
+
+ @Override
+ public String functionName() {
+ return "getAdminUserById";
+ }
+
+ @Override
+ public String apply(Object value) {
+ if (value == null) {
+ //log.warn("(getAdminUserById) 解析异常参数为 null");
+ return "";
+ }
+ if (StrUtil.isEmpty(value.toString())) {
+ //log.warn("(getAdminUserById) 解析异常参数为空");
+ return "";
+ }
+
+ // 获取用户信息
+ AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString()));
+ if (user == null) {
+ log.warn("(getAdminUserById) 获取用户信息失败,参数为:{}", value);
+ return "";
+ }
+ // 返回格式 芋道源码(13888888888)
+ String nickname = user.getNickname();
+ if (ObjUtil.isNotEmpty(user.getMobile())) {
+ return nickname.concat("(").concat(user.getMobile()).concat(")");
+ }
+ return nickname;
+ }
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AreaParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AreaParseFunction.java
new file mode 100644
index 000000000..f486a49fa
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AreaParseFunction.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.system.framework.bizlog.function;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
+import com.mzt.logapi.service.IParseFunction;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自定义函数-通过区域编号获取区域信息
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Component
+public class AreaParseFunction implements IParseFunction {
+
+ @Override
+ public boolean executeBefore() {
+ return true; // 先转换值后对比
+ }
+
+ @Override
+ public String functionName() {
+ return "getAreaById";
+ }
+
+ @Override
+ public String apply(Object value) {
+ if (value == null) {
+ return "";
+ }
+ if (StrUtil.isEmpty(value.toString())) {
+ return "";
+ }
+
+ return AreaUtils.format(Integer.parseInt(value.toString()));
+ }
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/package-info.java
new file mode 100644
index 000000000..c96f3a0c1
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.module.system.framework.bizlog;
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java
new file mode 100644
index 000000000..a81280f99
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java
@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.system.framework.bizlog.service;
+
+import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
+import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
+import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
+import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;
+import com.mzt.logapi.beans.LogRecord;
+import com.mzt.logapi.service.ILogRecordService;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 操作日志 ILogRecordService 实现类
+ *
+ * 基于 {@link OperateLogService} 实现,记录操作日志
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Service
+public class ILogRecordServiceImpl implements ILogRecordService {
+
+ @Resource
+ private OperateLogService operateLogService;
+
+ @Override
+ public void record(LogRecord logRecord) {
+ OperateLogV2CreateReqBO reqBO = new OperateLogV2CreateReqBO();
+ // 补全通用字段
+ reqBO.setTraceId(TracerUtils.getTraceId());
+ // 补充用户信息
+ fillUserFields(reqBO);
+ // 补全模块信息
+ fillModuleFields(reqBO, logRecord);
+ // 补全请求信息
+ fillRequestFields(reqBO);
+ // 异步记录日志
+ operateLogService.createOperateLogV2(reqBO);
+ log.info("操作日志 ===> {}", reqBO);
+ }
+
+ private static void fillUserFields(OperateLogV2CreateReqBO reqBO) {
+ reqBO.setUserId(WebFrameworkUtils.getLoginUserId());
+ reqBO.setUserType(WebFrameworkUtils.getLoginUserType());
+ }
+
+ public static void fillModuleFields(OperateLogV2CreateReqBO reqBO, LogRecord logRecord) {
+ reqBO.setModule(logRecord.getType()); // 大模块类型如 crm-客户
+ reqBO.setName(logRecord.getSubType());// 操作名称如 转移客户
+ reqBO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号
+ reqBO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
+ reqBO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}
+ }
+
+ private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) {
+ // 获得 Request 对象
+ HttpServletRequest request = ServletUtils.getRequest();
+ if (request == null) {
+ return;
+ }
+ // 补全请求信息
+ reqBO.setRequestMethod(request.getMethod());
+ reqBO.setRequestUrl(request.getRequestURI());
+ reqBO.setUserIp(ServletUtils.getClientIP(request));
+ reqBO.setUserAgent(ServletUtils.getUserAgent(request));
+ }
+
+ @Override
+ public List queryLog(String bizNo, String type) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List queryLogByBizNo(String bizNo, String type, String subType) {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java
index 89e562c65..30a1a1a7c 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java
@@ -4,6 +4,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
+import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;
+
+import java.util.List;
/**
* 操作日志 Service 接口
@@ -27,4 +31,22 @@ public interface OperateLogService {
*/
PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO);
+ //======================= LOG V2 =======================
+
+ /**
+ * 记录操作日志 V2
+ *
+ * @param createReqBO 创建请求
+ */
+ void createOperateLogV2(OperateLogV2CreateReqBO createReqBO);
+
+ /**
+ * 获取指定模块的指定数据的操作日志
+ *
+ * @param module 操作模块
+ * @param bizId 操作模块编号
+ * @return 操作日志
+ */
+ List getOperateLogByModuleAndBizId(String module, Long bizId);
+
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
index 0181e3081..894cc48c8 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
@@ -8,15 +8,19 @@ import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogV2Mapper;
+import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import jakarta.annotation.Resource;
import java.util.Collection;
+import java.util.List;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH;
@@ -34,6 +38,8 @@ public class OperateLogServiceImpl implements OperateLogService {
@Resource
private OperateLogMapper operateLogMapper;
+ @Resource
+ private OperateLogV2Mapper operateLogV2Mapper;
@Resource
private AdminUserService userService;
@@ -60,4 +66,17 @@ public class OperateLogServiceImpl implements OperateLogService {
return operateLogMapper.selectPage(pageReqVO, userIds);
}
+ //======================= LOG V2 =======================
+
+ @Override
+ public void createOperateLogV2(OperateLogV2CreateReqBO createReqBO) {
+ OperateLogV2DO log = BeanUtils.toBean(createReqBO, OperateLogV2DO.class);
+ operateLogV2Mapper.insert(log);
+ }
+
+ @Override
+ public List getOperateLogByModuleAndBizId(String module, Long bizId) {
+ return operateLogV2Mapper.selectListByModuleAndBizId(module, bizId);
+ }
+
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java
new file mode 100644
index 000000000..d6c44604c
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java
@@ -0,0 +1,83 @@
+package cn.iocoder.yudao.module.system.service.logger.bo;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * 系统操作日志 Create Req BO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class OperateLogV2CreateReqBO {
+
+ /**
+ * 链路追踪编号
+ *
+ * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
+ */
+ private String traceId;
+ /**
+ * 用户编号
+ *
+ * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
+ */
+ @NotEmpty(message = "用户编号不能为空")
+ private Long userId;
+ /**
+ * 用户类型
+ *
+ * 关联 {@link UserTypeEnum}
+ */
+ @NotEmpty(message = "用户类型不能为空")
+ private Integer userType;
+ /**
+ * 操作模块
+ */
+ @NotEmpty(message = "操作模块不能为空")
+ private String module;
+ /**
+ * 操作名
+ */
+ @NotEmpty(message = "操作名不能为空")
+ private String name;
+ /**
+ * 操作模块业务编号
+ */
+ @NotEmpty(message = "操作模块业务编号不能为空")
+ private Long bizId;
+ /**
+ * 操作内容,记录整个操作的明细
+ * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
+ */
+ @NotEmpty(message = "操作内容不能为空")
+ private String content;
+ /**
+ * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
+ * 例如说,记录订单编号,{ orderId: "1"}
+ */
+ private String extra;
+
+ /**
+ * 请求方法名
+ */
+ @NotEmpty(message = "请求方法名不能为空")
+ private String requestMethod;
+ /**
+ * 请求地址
+ */
+ @NotEmpty(message = "请求地址不能为空")
+ private String requestUrl;
+ /**
+ * 用户 IP
+ */
+ @NotEmpty(message = "用户 IP 不能为空")
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ @NotEmpty(message = "浏览器 UA 不能为空")
+ private String userAgent;
+
+}