Merge remote-tracking branch 'yudao/develop' into develop

This commit is contained in:
puhui999 2024-02-25 00:39:47 +08:00
commit b9ffb7833c
48 changed files with 650 additions and 333 deletions

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.excel.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
// TODO @puhui999列表有办法通过 field name 主要考虑一个点可能导入模版的顺序可能会变
/**
* Excel 列名枚举
* 默认枚举 26 列列名如果有需求更多的列名请自行补充
@ -12,6 +13,7 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ExcelColumn {
A(0), B(1), C(2), D(3), E(4), F(5), G(6), H(7), I(8),
J(9), K(10), L(11), M(12), N(13), O(14), P(15), Q(16),
R(17), S(18), T(19), U(20), V(21), W(22), X(23), Y(24),

View File

@ -23,9 +23,20 @@ import java.util.stream.Collectors;
*/
public class SelectSheetWriteHandler implements SheetWriteHandler {
/**
* 数据起始行从 0 开始
*
* 约定本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改
*/
public static final int FIRST_ROW = 1;
/**
* 下拉列需要创建下拉框的行数默认两千行如需更多请自行调整
*/
public static final int LAST_ROW = 2000;
private static final String DICT_SHEET_NAME = "字典sheet";
public static final int FIRST_ROW = 1; // 数据起始行从 0 开始本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改
public static final int LAST_ROW = 2000; // 下拉列需要创建下拉框的行数默认两千行如需更多请自行调整
// TODO @puhui999Map<ExcelColumn, List<String>> 可以么之前用 keyvalue 的原因返回给前端无法用 linkedhashmap默认 key 会乱序
private final List<KeyValue<ExcelColumn, List<String>>> selectMap;
public SelectSheetWriteHandler(List<KeyValue<ExcelColumn, List<String>>> selectMap) {
@ -48,32 +59,32 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
return;
}
// 1.1 获取相应操作对象
// 1. 获取相应操作对象
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手
Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿
// 1.2 创建数据字典的 sheet
// 2. 创建数据字典的 sheet
Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
for (KeyValue<ExcelColumn, List<String>> keyValue : selectMap) {
int rowLen = keyValue.getValue().size();
// 设置字典 sheet 页的值 每一列一个字典项
for (int i = 0; i < rowLen; i++) {
int rowLength = keyValue.getValue().size();
// 2.1 设置字典 sheet 页的值 每一列一个字典项
for (int i = 0; i < rowLength; i++) {
Row row = dictSheet.getRow(i);
if (row == null) {
row = dictSheet.createRow(i);
}
row.createCell(keyValue.getKey().getColNum()).setCellValue(keyValue.getValue().get(i));
}
// 1.3 设置单元格下拉选择
setColSelect(writeSheetHolder, workbook, helper, keyValue);
// 2.2 设置单元格下拉选择
setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
}
}
/**
* 设置单元格下拉选择
*/
private static void setColSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
KeyValue<ExcelColumn, List<String>> keyValue) {
private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
KeyValue<ExcelColumn, List<String>> keyValue) {
// 1.1 创建可被其他单元格引用的名称
Name name = workbook.createName();
String excelColumn = keyValue.getKey().name();
@ -81,6 +92,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size();
name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
name.setRefersToFormula(refers); // 设置公式
// 2.1 设置约束
DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
// 设置下拉单元格的首行末行首列末列

View File

@ -96,6 +96,12 @@ public interface LogRecordConstants {
String CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE = "更新商机状态";
String CRM_BUSINESS_UPDATE_STATUS_SUCCESS = "更新了商机【{{#businessName}}】的状态从【{{#oldStatusName}}】变更为了【{{#newStatusName}}】";
// ======================= CRM_CONTRACT_CONFIG 合同配置 =======================
String CRM_CONTRACT_CONFIG_TYPE = "CRM 合同配置";
String CRM_CONTRACT_CONFIG_SUB_TYPE = "{{#isPoolConfigUpdate ? '更新合同配置' : '创建合同配置'}}";
String CRM_CONTRACT_CONFIG_SUCCESS = "{{#isPoolConfigUpdate ? '更新了合同配置' : '创建了合同配置'}}";
// ======================= CRM_CONTRACT 合同 =======================
String CRM_CONTRACT_TYPE = "CRM 合同";

View File

@ -1,9 +0,0 @@
### 合同金额排行榜
GET {{baseUrl}}/crm/bi-rank/get-contract-price-rank?deptId=100&times[0]=2022-12-12 00:00:00&times[1]=2024-12-12 23:59:59
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
### 回款金额排行榜
GET {{baseUrl}}/crm/bi-rank/get-receivable-price-rank?deptId=100&times[0]=2022-12-12 00:00:00&times[1]=2024-12-12 23:59:59
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}

View File

@ -126,15 +126,6 @@ public class CrmBusinessController {
return businessVO;
}
// TODO 芋艿处理下
@GetMapping("/list-by-ids")
@Operation(summary = "获得商机列表")
@Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
@PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<List<CrmBusinessRespVO>> getContactListByIds(@RequestParam("ids") List<Long> ids) {
return success(BeanUtils.toBean(businessService.getBusinessList(ids, getLoginUserId()), CrmBusinessRespVO.class));
}
@GetMapping("/simple-all-list")
@Operation(summary = "获得联系人的精简列表")
@PreAuthorize("@ss.hasPermission('crm:contact:query')")

View File

@ -105,15 +105,6 @@ public class CrmContactController {
return buildContactDetailList(singletonList(contact)).get(0);
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得联系人列表")
@Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
public CommonResult<List<CrmContactRespVO>> getContactListByIds(@RequestParam("ids") List<Long> ids) {
List<CrmContactDO> list = contactService.getContactListByIds(ids, getLoginUserId());
return success(BeanUtils.toBean(list, CrmContactRespVO.class));
}
@GetMapping("/simple-all-list")
@Operation(summary = "获得联系人的精简列表")
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
@ -208,6 +199,15 @@ public class CrmContactController {
return success(true);
}
@PostMapping("/create-business-list2")
@Operation(summary = "创建联系人与商机的关联")
@PreAuthorize("@ss.hasPermission('crm:contact:create-business')")
public CommonResult<Boolean> createContactBusinessList2(@Valid @RequestBody CrmContactBusiness2ReqVO createReqVO) {
contactBusinessLinkService.createContactBusinessList2(createReqVO);
return success(true);
}
@DeleteMapping("/delete-business-list")
@Operation(summary = "删除联系人与联系人的关联")
@PreAuthorize("@ss.hasPermission('crm:contact:delete-business')")
@ -216,4 +216,12 @@ public class CrmContactController {
return success(true);
}
@DeleteMapping("/delete-business-list2")
@Operation(summary = "删除联系人与联系人的关联")
@PreAuthorize("@ss.hasPermission('crm:contact:delete-business')")
public CommonResult<Boolean> deleteContactBusinessList(@Valid @RequestBody CrmContactBusiness2ReqVO deleteReqVO) {
contactBusinessLinkService.deleteContactBusinessList2(deleteReqVO);
return success(true);
}
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - CRM 联系人商机 Request VO") // 商机关联联系人用于关联取消关联的操作
@Data
public class CrmContactBusiness2ReqVO {
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7638")
@NotNull(message="商机不能为空")
private Long businessId;
@Schema(description = "联系人编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "20878")
@NotEmpty(message="联系人数组不能为空")
private List<Long> contactIds;
}

View File

@ -7,7 +7,7 @@ import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - CRM 联系人商机 Request VO") // 用于关联取消关联的操作
@Schema(description = "管理后台 - CRM 联系人商机 Request VO") // 联系人关联商机用于关联取消关联的操作
@Data
public class CrmContactBusinessReqVO {

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - CRM 合同配置")
@RestController
@RequestMapping("/crm/contract-config")
@Validated
public class CrmContractConfigController {
@Resource
private CrmContractConfigService contractConfigService;
@GetMapping("/get")
@Operation(summary = "获取合同配置")
@PreAuthorize("@ss.hasPermission('crm:contract-config:query')")
public CommonResult<CrmContractConfigRespVO> getCustomerPoolConfig() {
CrmContractConfigDO config = contractConfigService.getContractConfig();
return success(BeanUtils.toBean(config, CrmContractConfigRespVO.class));
}
@PutMapping("/save")
@Operation(summary = "更新合同配置")
@PreAuthorize("@ss.hasPermission('crm:contract-config:update')")
public CommonResult<Boolean> saveCustomerPoolConfig(@Valid @RequestBody CrmContractConfigSaveReqVO updateReqVO) {
contractConfigService.saveContractConfig(updateReqVO);
return success(true);
}
}

View File

@ -9,10 +9,10 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@ -138,6 +138,14 @@ public class CrmContractController {
return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));
}
@GetMapping("/page-by-business")
@Operation(summary = "获得合同分页,基于指定商机")
public CommonResult<PageResult<CrmContractRespVO>> getContractPageByBusiness(@Valid CrmContractPageReqVO pageVO) {
Assert.notNull(pageVO.getBusinessId(), "商机编号不能为空");
PageResult<CrmContractDO> pageResult = contractService.getContractPageByBusinessId(pageVO);
return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList())));
}
@GetMapping("/export-excel")
@Operation(summary = "导出合同 Excel")
@PreAuthorize("@ss.hasPermission('crm:contract:export')")
@ -187,8 +195,8 @@ public class CrmContractController {
Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList(convertSet(contractList,
CrmContractDO::getSignContactId)), CrmContactDO::getId);
// 1.4 获取商机
Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(convertSet(contractList,
CrmContractDO::getBusinessId)), CrmBusinessDO::getId);
Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(
convertSet(contractList, CrmContractDO::getBusinessId));
// 2. 拼接数据
return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> {
// 2.1 设置客户信息
@ -207,18 +215,18 @@ public class CrmContractController {
});
}
@GetMapping("/check-contract-count")
@GetMapping("/audit-count")
@Operation(summary = "获得待审核合同数量")
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
public CommonResult<Long> getCheckContractCount() {
return success(contractService.getCheckContractCount(getLoginUserId()));
public CommonResult<Long> getAuditContractCount() {
return success(contractService.getAuditContractCount(getLoginUserId()));
}
@GetMapping("/end-contract-count")
@Operation(summary = "获得即将到期的合同数量")
@GetMapping("/remind-count")
@Operation(summary = "获得即将到期(提醒)的合同数量")
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
public CommonResult<Long> getEndContractCount() {
return success(contractService.getEndContractCount(getLoginUserId()));
public CommonResult<Long> getRemindContractCount() {
return success(contractService.getRemindContractCount(getLoginUserId()));
}
@GetMapping("/list-all-simple-by-customer")

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - CRM 合同配置 Response VO")
@Data
public class CrmContractConfigRespVO {
@Schema(description = "是否开启提前提醒", example = "true")
private Boolean notifyEnabled;
@Schema(description = "提前提醒天数", example = "2")
private Integer notifyDays;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config;
import cn.hutool.core.util.BooleanUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import lombok.Data;
import java.util.Objects;
@Schema(description = "管理后台 - CRM 合同配置 Request VO")
@Data
public class CrmContractConfigSaveReqVO {
@Schema(description = "是否开启提前提醒", example = "true")
@DiffLogField(name = "是否开启提前提醒")
private Boolean notifyEnabled;
@Schema(description = "提前提醒天数", example = "2")
@DiffLogField(name = "提前提醒天数")
private Integer notifyDays;
@AssertTrue(message = "提前提醒天数不能为空")
@JsonIgnore
public boolean isNotifyDaysValid() {
if (!BooleanUtil.isTrue(getNotifyEnabled())) {
return true;
}
return Objects.nonNull(getNotifyDays());
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmBusinessParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmContactParseFunction;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
@ -13,6 +14,8 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -26,7 +29,7 @@ import java.util.ArrayList;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@ -44,6 +47,9 @@ public class CrmFollowUpRecordController {
@Resource
private CrmBusinessService businessService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/create")
@Operation(summary = "创建跟进记录")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:create')")
@ -74,17 +80,24 @@ public class CrmFollowUpRecordController {
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')")
public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
PageResult<CrmFollowUpRecordDO> pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO);
/// 拼接数据
Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList(
convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId);
Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(
convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream())), CrmBusinessDO::getId);
// 1.1 查询联系人和商机
Map<Long, CrmContactDO> contactMap = contactService.getContactMap(
convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream()));
Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(
convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream()));
// 1.2 查询用户
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(pageResult.getList(), item -> Long.valueOf(item.getCreator())));
// 2. 拼接数据
PageResult<CrmFollowUpRecordRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class, record -> {
record.setContactNames(new ArrayList<>()).setBusinessNames(new ArrayList<>());
record.getContactIds().forEach(id -> MapUtils.findAndThen(contactMap, id,
contact -> record.getContactNames().add(contact.getName())));
record.getContactIds().forEach(id -> MapUtils.findAndThen(businessMap, id,
business -> record.getBusinessNames().add(business.getName())));
// 2.1 设置联系人和商机信息
record.setBusinesses(new ArrayList<>()).setContacts(new ArrayList<>());
record.getContactIds().forEach(id -> MapUtils.findAndThen(contactMap, id, contact ->
record.getContacts().add(new CrmBusinessRespVO().setId(contact.getId()).setName(contact.getName()))));
record.getContactIds().forEach(id -> MapUtils.findAndThen(businessMap, id, business ->
record.getBusinesses().add(new CrmBusinessRespVO().setId(business.getId()).setName(business.getName()))));
// 2.2 设置用户信息
MapUtils.findAndThen(userMap, Long.valueOf(record.getCreator()), user -> record.setCreatorName(user.getNickname()));
});
return success(voPageResult);
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -36,19 +38,26 @@ public class CrmFollowUpRecordRespVO {
@Schema(description = "关联的商机编号数组")
private List<Long> businessIds;
@Schema(description = "关联的商机名称数组")
private List<String> businessNames;
@Schema(description = "关联的商机数组")
private List<CrmBusinessRespVO> businesses;
@Schema(description = "关联的联系人编号数组")
private List<Long> contactIds;
@Schema(description = "关联的联系人名称数组")
private List<String> contactNames;
private List<CrmBusinessRespVO> contacts;
@Schema(description = "图片")
private List<String> picUrls;
@Schema(description = "附件")
private List<String> fileUrls;
@Schema(description = "创建人", example = "1024")
@ExcelProperty("创建人")
private String creator;
@Schema(description = "创建人名字", example = "芋道源码")
@ExcelProperty("创建人名字")
private String creatorName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;

View File

@ -3,11 +3,11 @@ package cn.iocoder.yudao.module.crm.controller.admin.permission;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.collect.Multimaps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
@ -29,11 +30,16 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - CRM 数据权限")
@ -98,18 +104,32 @@ public class CrmPermissionController {
@PreAuthorize("@ss.hasPermission('crm:permission:query')")
public CommonResult<List<CrmPermissionRespVO>> getPermissionList(@RequestParam("bizType") Integer bizType,
@RequestParam("bizId") Long bizId) {
List<CrmPermissionDO> permission = permissionService.getPermissionListByBiz(bizType, bizId);
if (CollUtil.isEmpty(permission)) {
List<CrmPermissionDO> permissions = permissionService.getPermissionListByBiz(bizType, bizId);
if (CollUtil.isEmpty(permissions)) {
return success(Collections.emptyList());
}
// 查询相关数据
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(permissions, CrmPermissionDO::getUserId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
Map<Long, PostRespDTO> postMap = postApi.getPostMap(
convertSetByFlatMap(userMap.values(), AdminUserRespDTO::getPostIds,
item -> item != null ? item.stream() : Stream.empty()));
// 拼接数据
List<AdminUserRespDTO> userList = adminUserApi.getUserList(convertSet(permission, CrmPermissionDO::getUserId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId));
Set<Long> postIds = CollectionUtils.convertSetByFlatMap(userList, AdminUserRespDTO::getPostIds,
item -> item != null ? item.stream() : Stream.empty());
Map<Long, PostRespDTO> postMap = postApi.getPostMap(postIds);
return success(CrmPermissionConvert.INSTANCE.convert(permission, userList, deptMap, postMap));
return success(CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> {
findAndThen(userMap, item.getUserId(), user -> {
item.setNickname(user.getNickname());
findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName()));
if (CollUtil.isEmpty(user.getPostIds())) {
item.setPostNames(Collections.emptySet());
return;
}
List<PostRespDTO> postList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds());
item.setPostNames(CollectionUtils.convertSet(postList, PostRespDTO::getName));
});
return item;
}));
}
}

View File

@ -0,0 +1,9 @@
### 合同金额排行榜
GET {{baseUrl}}/crm/statistics-rank/get-contract-price-rank?deptId=100&times[0]=2022-12-12 00:00:00&times[1]=2024-12-12 23:59:59
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
### 回款金额排行榜
GET {{baseUrl}}/crm/statistics-rank/get-receivable-price-rank?deptId=100&times[0]=2022-12-12 00:00:00&times[1]=2024-12-12 23:59:59
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}

View File

@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.crm.controller.admin.bi;
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO;
import cn.iocoder.yudao.module.crm.service.bi.CrmBiRankingService;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@ -19,68 +19,68 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - CRM BI 排行榜")
@Tag(name = "管理后台 - CRM 排行榜统计")
@RestController
@RequestMapping("/crm/bi-rank")
@RequestMapping("/crm/statistics-rank")
@Validated
public class CrmBiRankController {
public class CrmStatisticsRankController {
@Resource
private CrmBiRankingService rankingService;
private CrmStatisticsRankingService rankingService;
@GetMapping("/get-contract-price-rank")
@Operation(summary = "获得合同金额排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getContractPriceRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getContractPriceRank(rankingReqVO));
}
@GetMapping("/get-receivable-price-rank")
@Operation(summary = "获得回款金额排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getReceivablePriceRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getReceivablePriceRank(rankingReqVO));
}
@GetMapping("/get-contract-count-rank")
@Operation(summary = "获得签约合同数量排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getContractCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getContractCountRank(rankingReqVO));
}
@GetMapping("/get-product-sales-rank")
@Operation(summary = "获得产品销量排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getProductSalesRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getProductSalesRank(rankingReqVO));
}
@GetMapping("/get-customer-count-rank")
@Operation(summary = "获得新增客户数排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getCustomerCountRank(rankingReqVO));
}
@GetMapping("/get-contacts-count-rank")
@Operation(summary = "获得新增联系人数排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getContactsCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getContactsCountRank(rankingReqVO));
}
@GetMapping("/get-follow-count-rank")
@Operation(summary = "获得跟进次数排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getFollowCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getFollowCountRank(rankingReqVO));
}
@GetMapping("/get-follow-customer-count-rank")
@Operation(summary = "获得跟进客户数排行榜")
@PreAuthorize("@ss.hasPermission('crm:bi-rank:query')")
public CommonResult<List<CrmBiRanKRespVO>> getFollowCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) {
@PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')")
public CommonResult<List<CrmStatisticsRanKRespVO>> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) {
return success(rankingService.getFollowCustomerCountRank(rankingReqVO));
}

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.crm.controller.admin.bi.vo;
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - CRM BI 排行榜 Response VO")
@Schema(description = "管理后台 - CRM BI 排行榜统计 Response VO")
@Data
public class CrmBiRanKRespVO {
public class CrmStatisticsRanKRespVO {
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long ownerUserId;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.controller.admin.bi.vo;
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
@ -11,9 +11,9 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - CRM BI 排行榜 Request VO")
@Schema(description = "管理后台 - CRM 排行榜统计 Request VO")
@Data
public class CrmBiRankReqVO {
public class CrmStatisticsRankReqVO {
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "部门 id 不能为空")

View File

@ -1,56 +0,0 @@
package cn.iocoder.yudao.module.crm.convert.permission;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.collect.Multimaps;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
/**
* Crm 数据权限 Convert
*
* @author HUIHUI
*/
@Mapper
public interface CrmPermissionConvert {
CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class);
default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permissions, List<AdminUserRespDTO> userList,
Map<Long, DeptRespDTO> deptMap, Map<Long, PostRespDTO> postMap) {
Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(userList, AdminUserRespDTO::getId);
return CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> {
findAndThen(userMap, item.getUserId(), user -> {
item.setNickname(user.getNickname());
findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName()));
if (CollUtil.isEmpty(user.getPostIds())) {
item.setPostNames(Collections.emptySet());
return;
}
List<PostRespDTO> postList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds());
item.setPostNames(CollectionUtils.convertSet(postList, PostRespDTO::getName));
});
return item;
});
}
default List<CrmPermissionDO> convertList(CrmPermissionUpdateReqVO updateReqVO) {
return CollectionUtils.convertList(updateReqVO.getIds(),
id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel()));
}
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.contract;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.*;
import lombok.*;
@TableName("crm_contract_config")
@KeySequence("crm_contract_config_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmContractConfigDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 是否开启提前提醒
*/
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private Boolean notifyEnabled;
/**
* 提前提醒天数
*/
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private Integer notifyDays;
}

View File

@ -12,7 +12,6 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
* 商机 Mapper
@ -54,15 +53,6 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
return selectJoinPage(pageReqVO, CrmBusinessDO.class, query);
}
default List<CrmBusinessDO> selectBatchIds(Collection<Long> ids, Long userId) {
MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId);
// 拼接自身的查询条件
query.selectAll(CrmBusinessDO.class).in(CrmBusinessDO::getId, ids).orderByDesc(CrmBusinessDO::getId);
return selectJoinList(CrmBusinessDO.class, query);
}
default Long selectCountByStatusTypeId(Long statusTypeId) {
return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId);
}

View File

@ -27,6 +27,12 @@ public interface CrmContactBusinessMapper extends BaseMapperX<CrmContactBusiness
.in(CrmContactBusinessDO::getBusinessId, businessIds));
}
default void deleteByBusinessIdAndContactId(Long businessId, List<Long> contactIds) {
delete(new LambdaQueryWrapper<CrmContactBusinessDO>()
.eq(CrmContactBusinessDO::getBusinessId, businessId)
.in(CrmContactBusinessDO::getContactId, contactIds));
}
default List<CrmContactBusinessDO> selectListByContactId(Long contactId) {
return selectList(CrmContactBusinessDO::getContactId, contactId);
}

View File

@ -69,14 +69,6 @@ public interface CrmContactMapper extends BaseMapperX<CrmContactDO> {
return selectJoinPage(pageReqVO, CrmContactDO.class, query);
}
default List<CrmContactDO> selectBatchIds(Collection<Long> ids, Long ownerUserId) {
MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, ownerUserId);
query.selectAll(CrmContactDO.class).in(CrmContactDO::getId, ids).orderByDesc(CrmContactDO::getId);
return selectJoinList(CrmContactDO.class, query);
}
default List<CrmContactDO> selectListByCustomerId(Long customerId) {
return selectList(CrmContactDO::getCustomerId, customerId);
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.crm.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 合同配置 Mapper
*
* @author Wanwan
*/
@Mapper
public interface CrmContractConfigMapper extends BaseMapperX<CrmContractConfigDO> {
default CrmContractConfigDO selectOne() {
return selectOne(new QueryWrapperX<CrmContractConfigDO>().limitN(1));
}
}

View File

@ -5,7 +5,8 @@ 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.controller.admin.contract.vo.contract.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@ -39,7 +40,17 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
.orderByDesc(CrmContractDO::getId));
}
default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
default PageResult<CrmContractDO> selectPageByBusinessId(CrmContractPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmContractDO>()
.eq(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
.likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
.likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
.eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
.eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
.orderByDesc(CrmContractDO::getId));
}
default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId, CrmContractConfigDO config) {
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
@ -57,10 +68,8 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) { // 即将到期
// TODO: @芋艿 需要配置 提前提醒天数
int REMIND_DAYS = 20;
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())
.between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
.between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(config.getNotifyDays()));
} else if (CrmContractPageReqVO.EXPIRY_TYPE_EXPIRED.equals(pageReqVO.getExpiryType())) { // 已到期
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())
.lt(CrmContractDO::getEndTime, endOfToday);
@ -85,17 +94,17 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
return selectCount(CrmContractDO::getBusinessId, businessId);
}
default Long selectCheckContractCount(Long userId) {
default Long selectCountByAudit(Long userId) {
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
// 我负责的 + 非公海
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
CrmContractDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
// 提交 or 审核不通过
query.in(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.REJECT.getStatus());
// 审核
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());
return selectCount(query);
}
default Long selectEndContractCount(Long userId) {
default Long selectCountByRemind(Long userId, CrmContractConfigDO config) {
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
// 我负责的 + 非公海
CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
@ -103,10 +112,8 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
// 即将到期
LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
// TODO: @dhb52 需要配置 提前提醒天数
int REMIND_DAYS = 20;
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus())
.between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) // 必须审批通过
.between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(config.getNotifyDays()));
return selectCount(query);
}

View File

@ -1,18 +1,18 @@
package cn.iocoder.yudao.module.crm.dal.mysql.bi;
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* CRM BI 排行榜 Mapper
* CRM 排行榜统计 Mapper
*
* @author anhaohao
*/
@Mapper
public interface CrmBiRankingMapper {
public interface CrmStatisticsRankingMapper {
/**
* 查询合同金额排行榜
@ -20,7 +20,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 合同金额排行榜
*/
List<CrmBiRanKRespVO> selectContractPriceRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询回款金额排行榜
@ -28,7 +28,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 回款金额排行榜
*/
List<CrmBiRanKRespVO> selectReceivablePriceRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询签约合同数量排行榜
@ -36,7 +36,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 签约合同数量排行榜
*/
List<CrmBiRanKRespVO> selectContractCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询产品销量排行榜
@ -44,7 +44,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 产品销量排行榜
*/
List<CrmBiRanKRespVO> selectProductSalesRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询新增客户数排行榜
@ -52,7 +52,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 新增客户数排行榜
*/
List<CrmBiRanKRespVO> selectCustomerCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询联系人数量排行榜
@ -60,7 +60,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 联系人数量排行榜
*/
List<CrmBiRanKRespVO> selectContactsCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询跟进次数排行榜
@ -68,7 +68,7 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 跟进次数排行榜
*/
List<CrmBiRanKRespVO> selectFollowCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 查询跟进客户数排行榜
@ -76,6 +76,6 @@ public interface CrmBiRankingMapper {
* @param rankReqVO 参数
* @return 跟进客户数排行榜
*/
List<CrmBiRanKRespVO> selectFollowCustomerCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
}

View File

@ -16,6 +16,9 @@ import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* 商机 Service 接口
@ -101,15 +104,17 @@ public interface CrmBusinessService {
* @param ids 编号
* @return 商机列表
*/
List<CrmBusinessDO> getBusinessList(Collection<Long> ids, Long userId);
List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
/**
* 获得商机列表
* 获得商机 Map
*
* @param ids 编号
* @return 商机列表
* @return 商机 Map
*/
List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
default Map<Long, CrmBusinessDO> getBusinessMap(Collection<Long> ids) {
return convertMap(getBusinessList(ids), CrmBusinessDO::getId);
}
/**
* 获得指定商机编号的产品列表

View File

@ -322,14 +322,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
return validateBusinessExists(id);
}
@Override
public List<CrmBusinessDO> getBusinessList(Collection<Long> ids, Long userId) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return businessMapper.selectBatchIds(ids, userId);
}
@Override
public List<CrmBusinessDO> getBusinessList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.service.contact;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusiness2ReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
import jakarta.validation.Valid;
@ -14,19 +15,33 @@ import java.util.List;
public interface CrmContactBusinessService {
/**
* 创建联系人与商机的关联
* 创建联系人与商机的关联通过联系人关联商机
*
* @param createReqVO 创建信息
*/
void createContactBusinessList(@Valid CrmContactBusinessReqVO createReqVO);
/**
* 删除联系人与商机的关联
* 创建联系人与商机的关联通过商机关联联系人
*
* @param createReqVO 创建信息
*/
void createContactBusinessList2(@Valid CrmContactBusiness2ReqVO createReqVO);
/**
* 删除联系人与商机的关联通过联系人取关商机
*
* @param deleteReqVO 删除信息
*/
void deleteContactBusinessList(@Valid CrmContactBusinessReqVO deleteReqVO);
/**
* 删除联系人与商机的关联通过商机取关联系人
*
* @param deleteReqVO 删除信息
*/
void deleteContactBusinessList2(@Valid CrmContactBusiness2ReqVO deleteReqVO);
/**
* 删除联系人与商机的关联基于联系人编号
*

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.service.contact;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusiness2ReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
@ -67,6 +68,32 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
}
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#createReqVO.businessId", level = CrmPermissionLevelEnum.WRITE)
public void createContactBusinessList2(CrmContactBusiness2ReqVO createReqVO) {
CrmBusinessDO business = businessService.getBusiness(createReqVO.getBusinessId());
if (business == null) {
throw exception(BUSINESS_NOT_EXISTS);
}
// 遍历处理考虑到一般数量不会太多代码处理简单
List<CrmContactBusinessDO> saveDOList = new ArrayList<>();
createReqVO.getContactIds().forEach(contactId -> {
CrmContactDO contact = contactService.getContact(contactId);
if (contact == null) {
throw exception(CONTACT_NOT_EXISTS);
}
// 关联判重
if (contactBusinessMapper.selectByContactIdAndBusinessId(contactId, createReqVO.getBusinessId()) != null) {
return;
}
saveDOList.add(new CrmContactBusinessDO(null, contactId, createReqVO.getBusinessId()));
});
// 批量插入
if (CollUtil.isNotEmpty(saveDOList)) {
contactBusinessMapper.insertBatch(saveDOList);
}
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#deleteReqVO.contactId", level = CrmPermissionLevelEnum.WRITE)
public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) {
@ -79,6 +106,18 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
deleteReqVO.getContactId(), deleteReqVO.getBusinessIds());
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#deleteReqVO.businessId", level = CrmPermissionLevelEnum.WRITE)
public void deleteContactBusinessList2(CrmContactBusiness2ReqVO deleteReqVO) {
CrmBusinessDO business = businessService.getBusiness(deleteReqVO.getBusinessId());
if (business == null) {
throw exception(BUSINESS_NOT_EXISTS);
}
// 直接删除
contactBusinessMapper.deleteByBusinessIdAndContactId(
deleteReqVO.getBusinessId(), deleteReqVO.getContactIds());
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.WRITE)
public void deleteContactBusinessByContactId(Long contactId) {

View File

@ -95,15 +95,6 @@ public interface CrmContactService {
*/
void validateContact(Long id);
/**
* 获得联系人列表
*
* @param ids 编号
* @param userId 用户编号
* @return 联系人列表
*/
List<CrmContactDO> getContactListByIds(Collection<Long> ids, Long userId);
/**
* 获得联系人列表
*

View File

@ -257,14 +257,6 @@ public class CrmContactServiceImpl implements CrmContactService {
validateContactExists(id);
}
@Override
public List<CrmContactDO> getContactListByIds(Collection<Long> ids, Long userId) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return contactMapper.selectBatchIds(ids, userId);
}
@Override
public List<CrmContactDO> getContactList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.crm.service.contract;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
import jakarta.validation.Valid;
/**
* 合同配置 Service 接口
*
* @author 芋道源码
*/
public interface CrmContractConfigService {
/**
* 获得合同配置
*
* @return 合同配置
*/
CrmContractConfigDO getContractConfig();
/**
* 保存合同配置
*
* @param saveReqVO 更新信息
*/
void saveContractConfig(@Valid CrmContractConfigSaveReqVO saveReqVO);
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.crm.service.contract;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.config.CrmContractConfigSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractConfigMapper;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Objects;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
/**
* 合同配置 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class CrmContractConfigServiceImpl implements CrmContractConfigService {
@Resource
private CrmContractConfigMapper contractConfigMapper;
@Override
public CrmContractConfigDO getContractConfig() {
return contractConfigMapper.selectOne();
}
@Override
@LogRecord(type = CRM_CONTRACT_CONFIG_TYPE, subType = CRM_CONTRACT_CONFIG_SUB_TYPE, bizNo = "{{#configId}}",
success = CRM_CONTRACT_CONFIG_SUCCESS)
public void saveContractConfig(CrmContractConfigSaveReqVO saveReqVO) {
// 1. 存在则进行更新
CrmContractConfigDO dbConfig = getContractConfig();
CrmContractConfigDO config = BeanUtils.toBean(saveReqVO, CrmContractConfigDO.class);
if (Objects.nonNull(dbConfig)) {
contractConfigMapper.updateById(config.setId(dbConfig.getId()));
// 记录操作日志上下文
LogRecordContext.putVariable("isConfigUpdate", Boolean.TRUE);
LogRecordContext.putVariable("configId", config.getId());
return;
}
// 2. 不存在则进行插入
contractConfigMapper.insert(config);
// 记录操作日志上下文
LogRecordContext.putVariable("isConfigUpdate", Boolean.FALSE);
LogRecordContext.putVariable("configId", config.getId());
}
}

View File

@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.crm.service.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@ -109,10 +110,20 @@ public interface CrmContractService {
* 数据权限基于 {@link CrmCustomerDO} 读取
*
* @param pageReqVO 分页查询
* @return 联系人分页
* @return 合同分页
*/
PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO);
/**
* 获得合同分页基于指定商机
*
* 数据权限基于 {@link CrmBusinessDO} 读取
*
* @param pageReqVO 分页查询
* @return 合同分页
*/
PageResult<CrmContractDO> getContractPageByBusinessId(CrmContractPageReqVO pageReqVO);
/**
* 查询属于某个联系人的合同数量
*
@ -151,14 +162,14 @@ public interface CrmContractService {
* @param userId 用户编号
* @return 提醒数量
*/
Long getCheckContractCount(Long userId);
Long getAuditContractCount(Long userId);
/**
* 获得即将到期的合同数量
* 获得即将到期提醒的合同数量
*
* @param userId 用户编号
* @return 提醒数量
*/
Long getEndContractCount(Long userId);
Long getRemindContractCount(Long userId);
}

View File

@ -10,9 +10,10 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractConfigDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
@ -83,6 +84,8 @@ public class CrmContractServiceImpl implements CrmContractService {
private CrmBusinessService businessService;
@Resource
private CrmContactService contactService;
@Resource
private CrmContractConfigService contractConfigService;
@Resource
private AdminUserApi adminUserApi;
@ -172,8 +175,6 @@ public class CrmContractServiceImpl implements CrmContractService {
}
}
// TODO @合同待定缺一个取消合同的接口只有草稿审批中可以取消CrmAuditStatusEnum
/**
* 校验关联数据是否存在
*
@ -314,7 +315,7 @@ public class CrmContractServiceImpl implements CrmContractService {
contractMapper.updateById(new CrmContractDO().setId(id).setAuditStatus(auditStatus));
}
//======================= 查询相关 =======================
// ======================= 查询相关 =======================
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
@ -332,7 +333,16 @@ public class CrmContractServiceImpl implements CrmContractService {
@Override
public PageResult<CrmContractDO> getContractPage(CrmContractPageReqVO pageReqVO, Long userId) {
return contractMapper.selectPage(pageReqVO, userId);
// 1. 即将到期需要查询合同配置
CrmContractConfigDO config = null;
if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) {
config = contractConfigService.getContractConfig();
if (config != null && Boolean.FALSE.equals(config.getNotifyEnabled())) {
config = null;
}
}
// 2. 查询分页
return contractMapper.selectPage(pageReqVO, userId, config);
}
@Override
@ -341,6 +351,12 @@ public class CrmContractServiceImpl implements CrmContractService {
return contractMapper.selectPageByCustomerId(pageReqVO);
}
@Override
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#pageReqVO.businessId", level = CrmPermissionLevelEnum.READ)
public PageResult<CrmContractDO> getContractPageByBusinessId(CrmContractPageReqVO pageReqVO) {
return contractMapper.selectPageByBusinessId(pageReqVO);
}
@Override
public Long getContractCountByContactId(Long contactId) {
return contractMapper.selectCountByContactId(contactId);
@ -361,16 +377,18 @@ public class CrmContractServiceImpl implements CrmContractService {
return contractProductMapper.selectListByContractId(contactId);
}
// TODO @合同待定需要新增一个 ContractConfigDO 合同配置重点是到期提醒
@Override
public Long getCheckContractCount(Long userId) {
return contractMapper.selectCheckContractCount(userId);
public Long getAuditContractCount(Long userId) {
return contractMapper.selectCountByAudit(userId);
}
@Override
public Long getEndContractCount(Long userId) {
return contractMapper.selectEndContractCount(userId);
public Long getRemindContractCount(Long userId) {
CrmContractConfigDO config = contractConfigService.getContractConfig();
if (config == null || Boolean.FALSE.equals(config.getNotifyEnabled())) {
return 0L;
}
return contractMapper.selectCountByRemind(userId, config);
}
}

View File

@ -26,26 +26,16 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
@Resource
private CrmCustomerPoolConfigMapper customerPoolConfigMapper;
/**
* 获得客户公海配置
*
* @return 客户公海配置
*/
@Override
public CrmCustomerPoolConfigDO getCustomerPoolConfig() {
return customerPoolConfigMapper.selectOne();
}
/**
* 保存客户公海配置
*
* @param saveReqVO 更新信息
*/
@Override
@LogRecord(type = CRM_CUSTOMER_POOL_CONFIG_TYPE, subType = CRM_CUSTOMER_POOL_CONFIG_SUB_TYPE, bizNo = "{{#poolConfigId}}",
success = CRM_CUSTOMER_POOL_CONFIG_SUCCESS)
public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) {
// 存在则进行更新
// 1. 存在则进行更新
CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig();
CrmCustomerPoolConfigDO poolConfig = BeanUtils.toBean(saveReqVO, CrmCustomerPoolConfigDO.class);
if (Objects.nonNull(dbConfig)) {
@ -55,7 +45,8 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
LogRecordContext.putVariable("poolConfigId", poolConfig.getId());
return;
}
// 不存在则进行插入
// 2. 不存在则进行插入
customerPoolConfigMapper.insert(poolConfig);
// 记录操作日志上下文
LogRecordContext.putVariable("isPoolConfigUpdate", Boolean.FALSE);

View File

@ -258,11 +258,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
@Override
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
CrmCustomerImportReqVO importReqVO) {
// 校验非空
importCustomers = filterList(importCustomers, item -> Objects.nonNull(item.getName()));
if (CollUtil.isEmpty(importCustomers)) {
throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
}
// 因为有下拉所以需要过滤掉空行
importCustomers = filterList(importCustomers, item -> Objects.nonNull(item.getName()));
// 逐条处理
CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>())
.updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build();
importCustomers.forEach(importCustomer -> {

View File

@ -69,6 +69,9 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
crmFollowUpRecordMapper.insert(record);
// 2. 更新 bizId 对应的记录
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), record.getBizType())) { // 更新客户跟进信息
customerService.updateCustomerFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
}
if (ObjUtil.equal(CrmBizTypeEnum.CRM_BUSINESS.getType(), record.getBizType())) { // 更新商机跟进信息
businessService.updateBusinessFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
}
@ -81,9 +84,6 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTRACT.getType(), record.getBizType())) { // 更新合同跟进信息
contractService.updateContractFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
}
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), record.getBizType())) { // 更新客户跟进信息
customerService.updateCustomerFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
}
// 3.1 更新 contactIds 对应的记录只更新 nextTime
if (CollUtil.isNotEmpty(createReqVO.getContactIds())) {

View File

@ -2,16 +2,16 @@ package cn.iocoder.yudao.module.crm.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@ -74,8 +74,9 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
// 1. 校验存在
validatePermissionExists(updateReqVO.getIds());
// 2. 更新
List<CrmPermissionDO> updateDO = CrmPermissionConvert.INSTANCE.convertList(updateReqVO);
permissionMapper.updateBatch(updateDO);
List<CrmPermissionDO> updateList = CollectionUtils.convertList(updateReqVO.getIds(),
id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel()));
permissionMapper.updateBatch(updateList);
}
private void validatePermissionExists(Collection<Long> ids) {

View File

@ -1,17 +1,17 @@
package cn.iocoder.yudao.module.crm.service.bi;
package cn.iocoder.yudao.module.crm.service.statistics;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
import java.util.List;
/**
* CRM BI 排行榜 Service 接口
* CRM 排行榜统计 Service 接口
*
* @author anhaohao
*/
public interface CrmBiRankingService {
public interface CrmStatisticsRankingService {
/**
* 获得合同金额排行榜
@ -19,7 +19,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 合同金额排行榜
*/
List<CrmBiRanKRespVO> getContractPriceRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得回款金额排行榜
@ -27,7 +27,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 回款金额排行榜
*/
List<CrmBiRanKRespVO> getReceivablePriceRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得签约合同数量排行榜
@ -35,7 +35,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 签约合同数量排行榜
*/
List<CrmBiRanKRespVO> getContractCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得产品销量排行榜
@ -43,7 +43,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 产品销量排行榜
*/
List<CrmBiRanKRespVO> getProductSalesRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得新增客户数排行榜
@ -51,7 +51,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 新增客户数排行榜
*/
List<CrmBiRanKRespVO> getCustomerCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得联系人数量排行榜
@ -59,7 +59,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 联系人数量排行榜
*/
List<CrmBiRanKRespVO> getContactsCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得跟进次数排行榜
@ -67,7 +67,7 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 跟进次数排行榜
*/
List<CrmBiRanKRespVO> getFollowCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO);
/**
* 获得跟进客户数排行榜
@ -75,6 +75,6 @@ public interface CrmBiRankingService {
* @param rankReqVO 排行参数
* @return 跟进客户数排行榜
*/
List<CrmBiRanKRespVO> getFollowCustomerCountRank(CrmBiRankReqVO rankReqVO);
List<CrmStatisticsRanKRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO);
}

View File

@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.crm.service.bi;
package cn.iocoder.yudao.module.crm.service.statistics;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO;
import cn.iocoder.yudao.module.crm.dal.mysql.bi.CrmBiRankingMapper;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO;
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@ -23,16 +23,16 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* CRM BI 排行榜 Service 实现类
* CRM 排行榜统计 Service 实现类
*
* @author anhaohao
*/
@Service
@Validated
public class CrmBiRankingServiceImpl implements CrmBiRankingService {
public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingService {
@Resource
private CrmBiRankingMapper biRankingMapper;
private CrmStatisticsRankingMapper rankMapper;
@Resource
private AdminUserApi adminUserApi;
@ -40,43 +40,43 @@ public class CrmBiRankingServiceImpl implements CrmBiRankingService {
private DeptApi deptApi;
@Override
public List<CrmBiRanKRespVO> getContractPriceRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectContractPriceRank);
public List<CrmStatisticsRanKRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectContractPriceRank);
}
@Override
public List<CrmBiRanKRespVO> getReceivablePriceRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectReceivablePriceRank);
public List<CrmStatisticsRanKRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectReceivablePriceRank);
}
@Override
public List<CrmBiRanKRespVO> getContractCountRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectContractCountRank);
public List<CrmStatisticsRanKRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectContractCountRank);
}
@Override
public List<CrmBiRanKRespVO> getProductSalesRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectProductSalesRank);
public List<CrmStatisticsRanKRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectProductSalesRank);
}
@Override
public List<CrmBiRanKRespVO> getCustomerCountRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectCustomerCountRank);
public List<CrmStatisticsRanKRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectCustomerCountRank);
}
@Override
public List<CrmBiRanKRespVO> getContactsCountRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectContactsCountRank);
public List<CrmStatisticsRanKRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectContactsCountRank);
}
@Override
public List<CrmBiRanKRespVO> getFollowCountRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectFollowCountRank);
public List<CrmStatisticsRanKRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectFollowCountRank);
}
@Override
public List<CrmBiRanKRespVO> getFollowCustomerCountRank(CrmBiRankReqVO rankReqVO) {
return getRank(rankReqVO, biRankingMapper::selectFollowCustomerCountRank);
public List<CrmStatisticsRanKRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) {
return getRank(rankReqVO, rankMapper::selectFollowCustomerCountRank);
}
/**
@ -86,18 +86,18 @@ public class CrmBiRankingServiceImpl implements CrmBiRankingService {
* @param rankFunction 排行榜方法
* @return 排行版数据
*/
private List<CrmBiRanKRespVO> getRank(CrmBiRankReqVO rankReqVO, Function<CrmBiRankReqVO, List<CrmBiRanKRespVO>> rankFunction) {
private List<CrmStatisticsRanKRespVO> getRank(CrmStatisticsRankReqVO rankReqVO, Function<CrmStatisticsRankReqVO, List<CrmStatisticsRanKRespVO>> rankFunction) {
// 1. 获得用户编号数组
rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId()));
if (CollUtil.isEmpty(rankReqVO.getUserIds())) {
return Collections.emptyList();
}
// 2. 获得排行数据
List<CrmBiRanKRespVO> ranks = rankFunction.apply(rankReqVO);
List<CrmStatisticsRanKRespVO> ranks = rankFunction.apply(rankReqVO);
if (CollUtil.isEmpty(ranks)) {
return Collections.emptyList();
}
ranks.sort(Comparator.comparing(CrmBiRanKRespVO::getCount).reversed());
ranks.sort(Comparator.comparing(CrmStatisticsRanKRespVO::getCount).reversed());
// 3. 拼接用户信息
appendUserInfo(ranks);
return ranks;
@ -108,8 +108,8 @@ public class CrmBiRankingServiceImpl implements CrmBiRankingService {
*
* @param ranks 排行榜数据
*/
private void appendUserInfo(List<CrmBiRanKRespVO> ranks) {
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmBiRanKRespVO::getOwnerUserId));
private void appendUserInfo(List<CrmStatisticsRanKRespVO> ranks) {
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRanKRespVO::getOwnerUserId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> {
rank.setNickname(user.getNickname());

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.bi.CrmBiRankingMapper">
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper">
<select id="selectContractPriceRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
SELECT IFNULL(SUM(price), 0) AS count, owner_user_id
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT IFNULL(SUM(total_price), 0) AS count, owner_user_id
FROM crm_contract
WHERE deleted = 0
AND audit_status = 20
@ -18,7 +18,7 @@
</select>
<select id="selectReceivablePriceRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT IFNULL(SUM(price), 0) AS count, owner_user_id
FROM crm_receivable
WHERE deleted = 0
@ -33,7 +33,7 @@
</select>
<select id="selectContractCountRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT COUNT(1) AS count, owner_user_id
FROM crm_contract
WHERE deleted = 0
@ -49,7 +49,7 @@
<!-- TODO 待定 这里是否需要关联 crm_contract_product 表,计算销售额 -->
<select id="selectProductSalesRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT COUNT(1) AS count, owner_user_id
FROM crm_contract
WHERE deleted = 0
@ -64,7 +64,7 @@
</select>
<select id="selectCustomerCountRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT COUNT(1) AS count, owner_user_id
FROM crm_customer
WHERE deleted = 0
@ -78,7 +78,7 @@
</select>
<select id="selectContactsCountRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT COUNT(1) AS count, owner_user_id
FROM crm_contact
WHERE deleted = 0
@ -92,7 +92,7 @@
</select>
<select id="selectFollowCountRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT COUNT(1) AS count, cc.owner_user_id
FROM crm_follow_up_record AS cfur
LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids)
@ -108,7 +108,7 @@
</select>
<select id="selectFollowCustomerCountRank"
resultType="cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO">
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO">
SELECT COUNT(DISTINCT cc.id) AS count, cc.owner_user_id
FROM crm_follow_up_record AS cfur
LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids)