商机模块合并代码

This commit is contained in:
lzxhqs 2024-01-15 17:50:46 +08:00
commit 510b4fd3de
77 changed files with 998 additions and 608 deletions

View File

@ -28,13 +28,6 @@
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<!-- TODO @puhui999api 之间不直接引入哈然后logrecord function 在微服务下,这么跑会高不起来,所以每个服务自己写 function -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-api</artifactId>
<version>${revision}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -13,5 +13,6 @@ public interface DictTypeConstants {
String CRM_AUDIT_STATUS = "crm_audit_status"; // CRM 审批状态
String CRM_PRODUCT_UNIT = "crm_product_unit"; // CRM 产品单位
String CRM_PRODUCT_STATUS = "crm_product_status"; // CRM 产品状态
String CRM_FOLLOW_UP_TYPE = "crm_follow_up_type"; // 跟进方式
}

View File

@ -14,6 +14,8 @@ public interface ErrorCodeConstants {
// ========== 线索管理 1-020-001-000 ==========
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
ErrorCode CLUE_ANY_CLUE_NOT_EXISTS = new ErrorCode(1_020_001_001, "线索【{}】不存在");
ErrorCode CLUE_ANY_CLUE_ALREADY_TRANSLATED = new ErrorCode(1_020_001_002, "线索【{}】已经转化过了,请勿重复转化");
// ========== 商机管理 1-020-002-000 ==========
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");

View File

@ -1,8 +1,5 @@
package cn.iocoder.yudao.module.crm.enums;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTRACT_BY_ID;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
/**
* CRM 操作日志枚举
* 目的统一管理也减少 Service 里各种复杂字符串
@ -25,7 +22,7 @@ public interface LogRecordConstants {
String CRM_CUSTOMER_DELETE_SUB_TYPE = "删除客户";
String CRM_CUSTOMER_DELETE_SUCCESS = "删除了客户【{{#customerName}}】";
String CRM_CUSTOMER_TRANSFER_SUB_TYPE = "转移客户";
String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#customer.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#customer.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#customer.name}}】的负责人从【{getAdminUserById{#customer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#customer.lockStatus ? '解锁客户' : '锁定客户'}}";
String CRM_CUSTOMER_LOCK_SUCCESS = "{{#customer.lockStatus ? '将客户【' + #customer.name + '】解锁' : '将客户【' + #customer.name + '】锁定'}}";
String CRM_CUSTOMER_POOL_SUB_TYPE = "客户放入公海";
@ -59,7 +56,7 @@ public interface LogRecordConstants {
String CRM_CONTACT_DELETE_SUB_TYPE = "删除联系人";
String CRM_CONTACT_DELETE_SUCCESS = "删除了联系人【{{#contactName}}】";
String CRM_CONTACT_TRANSFER_SUB_TYPE = "转移联系人";
String CRM_CONTACT_TRANSFER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#contact.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
String CRM_CONTACT_TRANSFER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
// ======================= CRM_BUSINESS 商机 =======================
@ -71,7 +68,7 @@ public interface LogRecordConstants {
String CRM_BUSINESS_DELETE_SUB_TYPE = "删除商机";
String CRM_BUSINESS_DELETE_SUCCESS = "删除了商机【{{#businessName}}】";
String CRM_BUSINESS_TRANSFER_SUB_TYPE = "转移商机";
String CRM_BUSINESS_TRANSFER_SUCCESS = "将商机【{{#business.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#business.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
String CRM_BUSINESS_TRANSFER_SUCCESS = "将商机【{{#business.name}}】的负责人从【{getAdminUserById{#business.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
// ======================= CRM_CONTRACT 合同 =======================
@ -83,7 +80,7 @@ public interface LogRecordConstants {
String CRM_CONTRACT_DELETE_SUB_TYPE = "删除合同";
String CRM_CONTRACT_DELETE_SUCCESS = "删除了合同【{{#contractName}}】";
String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移合同";
String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#contract.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{getAdminUserById{#contract.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
// ======================= CRM_PRODUCT 产品 =======================
@ -109,20 +106,20 @@ public interface LogRecordConstants {
String CRM_RECEIVABLE_TYPE = "CRM 回款";
String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款";
String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款";
String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
// ======================= CRM_RECEIVABLE_PLAN 回款计划 =======================
String CRM_RECEIVABLE_PLAN_TYPE = "CRM 回款计划";
String CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE = "创建回款计划";
String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = "创建了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = "创建了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
String CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE = "更新回款计划";
String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = "更新了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}";
String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}";
String CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE = "删除回款计划";
String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = "删除了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = "删除了合同【{getContractById{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.crm.enums.message;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
/**
* CRM 联系状态
*
* @author dhb52
*/
@RequiredArgsConstructor
@Getter
public enum CrmContactStatusEnum implements IntArrayValuable {
NEEDED_TODAY(1, "今日需联系"),
EXPIRED(2, "已逾期"),
ALREADY_CONTACT(3, "已联系"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmContactStatusEnum::getType).toArray();
/**
* 状态
*/
private final Integer type;
/**
* 状态名
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.crm.enums.operatelog;
/**
* functionName 常量枚举
* 方便别的模块调用
*
* @author HUIHUI
*/
// TODO @puhui999这个枚举还是放在对应的 Function 里好主要考虑 Function 实现可以更近一点哈
public interface CrmParseFunctionNameConstants {
String GET_CONTACT_BY_ID = "getContactById"; // 获取联系人信息
String GET_CUSTOMER_BY_ID = "getCustomerById"; // 获取客户信息
String GET_CUSTOMER_INDUSTRY = "getCustomerIndustry"; // 获取客户行业信息
String GET_CUSTOMER_LEVEL = "getCustomerLevel"; // 获取客户级别
String GET_CUSTOMER_SOURCE = "getCustomerSource"; // 获取客户来源
String GET_CONTRACT_BY_ID = "getContractById"; // 获取合同信息
}

View File

@ -3,9 +3,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.business;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@ -31,6 +35,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@ -51,7 +56,6 @@ public class CrmBusinessController {
@Resource
private CrmBusinessStatusService businessStatusService;
// TODO @商机待定CrmBusinessCreateReqVOCrmBusinessUpdateReqVOCrmBusinessRespVO 按照新的 VO 规范
@PostMapping("/create")
@Operation(summary = "创建商机")
@PreAuthorize("@ss.hasPermission('crm:business:create')")
@ -82,7 +86,26 @@ public class CrmBusinessController {
@PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
CrmBusinessDO business = businessService.getBusiness(id);
return success(CrmBusinessConvert.INSTANCE.convert(business));
return success(BeanUtils.toBean(business, CrmBusinessRespVO.class));
}
@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')")
public CommonResult<List<CrmBusinessRespVO>> getSimpleContactList() {
CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO();
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(reqVO, getLoginUserId());
return success(convertList(pageResult.getList(), business -> // 只返回 idname 字段
new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())));
}
@GetMapping("/page")

View File

@ -1,57 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 商机 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class CrmBusinessBaseVO {
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotNull(message = "商机名称不能为空")
private String name;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
@NotNull(message = "商机状态类型不能为空")
private Long statusTypeId;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "商机状态不能为空")
private Long statusId;
@Schema(description = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
@NotNull(message = "客户不能为空")
private Long customerId;
@Schema(description = "预计成交日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime dealTime;
@Schema(description = "商机金额", example = "12371")
private Integer price;
// TODO @ljileo折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
@Schema(description = "整单折扣")
private Integer discountPercent;
@Schema(description = "产品总金额", example = "12025")
private BigDecimal productPrice;
@Schema(description = "备注", example = "随便")
private String remark;
}

View File

@ -1,16 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 商机创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmBusinessCreateReqVO extends CrmBusinessBaseVO {
// TODO @ljileo新建的时候应该可以传递添加的产品
}

View File

@ -1,18 +1,59 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 商机 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmBusinessRespVO extends CrmBusinessBaseVO {
public class CrmBusinessRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
private Long id;
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotNull(message = "商机名称不能为空")
private String name;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
@NotNull(message = "商机状态类型不能为空")
private Long statusTypeId;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "商机状态不能为空")
private Long statusId;
@Schema(description = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
@NotNull(message = "客户不能为空")
private Long customerId;
@Schema(description = "预计成交日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime dealTime;
@Schema(description = "商机金额", example = "12371")
private Integer price;
// TODO @ljileo折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
@Schema(description = "整单折扣")
private Integer discountPercent;
@Schema(description = "产品总金额", example = "12025")
private BigDecimal productPrice;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO;
import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@ -15,9 +17,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* @author lzxhqs
*/
// TODO @ljileoDiffLogField function 完善一下
@Schema(description = "管理后台 - CRM 商机创建/更新 Request VO")
@Data
public class CrmBusinessSaveReqVO {
@ -26,41 +26,52 @@ public class CrmBusinessSaveReqVO {
private Long id;
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@DiffLogField(name = "商机名称")
@NotNull(message = "商机名称不能为空")
private String name;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
@DiffLogField(name = "商机状态")
@NotNull(message = "商机状态类型不能为空")
private Long statusTypeId;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@DiffLogField(name = "商机状态")
@NotNull(message = "商机状态不能为空")
private Long statusId;
@Schema(description = "下次联系时间")
@DiffLogField(name = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
@DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
@NotNull(message = "客户不能为空")
private Long customerId;
@Schema(description = "预计成交日期")
@DiffLogField(name = "预计成交日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime dealTime;
@Schema(description = "商机金额", example = "12371")
@DiffLogField(name = "商机金额")
private Integer price;
// TODO @ljileo折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
@Schema(description = "整单折扣")
@DiffLogField(name = "整单折扣")
private Integer discountPercent;
@Schema(description = "产品总金额", example = "12025")
@DiffLogField(name = "产品总金额")
private BigDecimal productPrice;
@Schema(description = "备注", example = "随便")
@DiffLogField(name = "备注")
private String remark;
// TODO @ljileo修改的时候应该可以传递添加的产品
@Schema(description = "联系人编号", example = "110")
@NotNull(message = "联系人编号不能为空")
private Long contactId;
@ -69,7 +80,8 @@ public class CrmBusinessSaveReqVO {
@InEnum(CrmBizEndStatus.class)
private Integer endStatus;
@Schema(description = "产品列表", example = "")
private List<CrmProductSaveReqVO> product = new ArrayList<>();
@Schema(description = "商机产品列表", example = "")
private List<CrmBusinessProductSaveReqVO> products = new ArrayList<>();
}

View File

@ -1,23 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessBaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 商机更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmBusinessUpdateReqVO extends CrmBusinessBaseVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
@NotNull(message = "主键不能为空")
private Long id;
// TODO @ljileo修改的时候应该可以传递添加的产品
}

View File

@ -84,8 +84,8 @@ public class CrmClueController {
pageReqVO.setPageSize(PAGE_SIZE_NONE);
List<CrmClueDO> list = clueService.getCluePage(pageReqVO, getLoginUserId()).getList();
// 导出 Excel
List<CrmClueExcelVO> datas = BeanUtils.toBean(list, CrmClueExcelVO.class);
ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas);
List<CrmClueRespVO> datas = BeanUtils.toBean(list, CrmClueRespVO.class);
ExcelUtils.write(response, "线索.xls", "数据", CrmClueRespVO.class, datas);
}
@PutMapping("/transfer")

View File

@ -1,53 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.framework.common.validation.Telephone;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
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;
/**
* 线索 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class CrmClueBaseVO {
@Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
@NotEmpty(message = "线索名称不能为空")
private String name;
@Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
private Long customerId;
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
@Schema(description = "电话", example = "18000000000")
@Telephone
private String telephone;
@Schema(description = "手机号", example = "18000000000")
@Mobile
private String mobile;
@Schema(description = "地址", example = "北京市海淀区")
private String address;
@Schema(description = "最后跟进时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactLastTime;
@Schema(description = "负责人编号")
private Long ownerUserId;
@Schema(description = "备注", example = "随便")
private String remark;
}

View File

@ -1,66 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
/**
* 线索 Excel VO
*
* @author Wanwan
*/
@Data
public class CrmClueExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty(value = "转化状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean transformStatus;
@ExcelProperty(value = "跟进状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean followUpStatus;
@ExcelProperty("线索名称")
private String name;
// TODO 这里需要导出成客户名称
@ExcelProperty("客户id")
private Long customerId;
@ExcelProperty("下次联系时间")
private LocalDateTime contactNextTime;
@ExcelProperty("电话")
private String telephone;
@ExcelProperty("手机号")
private String mobile;
@ExcelProperty("地址")
private String address;
@ExcelProperty("负责人的用户编号")
private Long ownerUserId;
@ExcelProperty("最后跟进时间")
private LocalDateTime contactLastTime;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,27 +1,80 @@
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 线索 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmClueRespVO extends CrmClueBaseVO {
@ExcelIgnoreUnannotated
public class CrmClueRespVO {
@Schema(description = "编号,主键自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969")
@ExcelProperty("编号")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "转化状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean transformStatus;
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "跟进状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean followUpStatus;
@Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
@ExcelProperty("线索名称")
private String name;
@Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
// TODO 这里需要导出成客户名称
@ExcelProperty("客户id")
private Long customerId;
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ExcelProperty("下次联系时间")
private LocalDateTime contactNextTime;
@Schema(description = "电话", example = "18000000000")
@ExcelProperty("电话")
private String telephone;
@Schema(description = "手机号", example = "18000000000")
@ExcelProperty("手机号")
private String mobile;
@Schema(description = "地址", example = "北京市海淀区")
@ExcelProperty("地址")
private String address;
@Schema(description = "负责人编号")
@ExcelProperty("负责人的用户编号")
private Long ownerUserId;
@Schema(description = "最后跟进时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ExcelProperty("最后跟进时间")
private LocalDateTime contactLastTime;
@Schema(description = "备注", example = "随便")
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -113,12 +113,23 @@ public class CrmContactController {
return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList));
}
@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) {
return success(BeanUtils.toBean(contactService.getContactList(ids, getLoginUserId()), CrmContactRespVO.class));
}
@GetMapping("/simple-all-list")
@Operation(summary = "获得联系人的精简列表")
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
public CommonResult<List<CrmContactRespVO>> getSimpleContactList() {
List<CrmContactDO> list = contactService.getContactList();
return success(convertList(list, contact -> // 只返回 idname 字段
// TODO @puhui999这种还是搞个 getContactList 方法好点哈
CrmContactPageReqVO reqVO = new CrmContactPageReqVO();
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
PageResult<CrmContactDO> pageResult = contactService.getContactPage(reqVO, getLoginUserId());
return success(convertList(pageResult.getList(), contact -> // 只返回 idname 字段
new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())));
}
@ -153,7 +164,7 @@ public class CrmContactController {
@GetMapping("/operate-log-page")
@Operation(summary = "获得客户操作日志")
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@RequestParam("bizId")Long bizId) {
public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@RequestParam("bizId") Long bizId) {
OperateLogV2PageReqDTO reqVO = new OperateLogV2PageReqDTO();
reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
reqVO.setBizType(CRM_CONTACT_TYPE);

View File

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

View File

@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
@ -53,14 +54,14 @@ public class CrmContractController {
@PostMapping("/create")
@Operation(summary = "创建合同")
@PreAuthorize("@ss.hasPermission('crm:contract:create')")
public CommonResult<Long> createContract(@Valid @RequestBody CrmContractCreateReqVO createReqVO) {
public CommonResult<Long> createContract(@Valid @RequestBody CrmContractSaveReqVO createReqVO) {
return success(contractService.createContract(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@Operation(summary = "更新合同")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
public CommonResult<Boolean> updateContract(@Valid @RequestBody CrmContractUpdateReqVO updateReqVO) {
public CommonResult<Boolean> updateContract(@Valid @RequestBody CrmContractSaveReqVO updateReqVO) {
contractService.updateContract(updateReqVO);
return success(true);
}
@ -78,22 +79,22 @@ public class CrmContractController {
@Operation(summary = "获得合同")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
public CommonResult<ContractRespVO> getContract(@RequestParam("id") Long id) {
public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) {
CrmContractDO contract = contractService.getContract(id);
return success(CrmContractConvert.INSTANCE.convert(contract));
return success(BeanUtils.toBean(contract, CrmContractRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得合同分页")
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {
public CommonResult<PageResult<CrmContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {
PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId());
return success(buildContractDetailPage(pageResult));
}
@GetMapping("/page-by-customer")
@Operation(summary = "获得联系人分页,基于指定客户")
public CommonResult<PageResult<ContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
public CommonResult<PageResult<CrmContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);
return success(buildContractDetailPage(pageResult));
@ -108,7 +109,7 @@ public class CrmContractController {
PageResult<CrmContractDO> pageResult = contractService.getContractPage(exportReqVO, getLoginUserId());
// 导出 Excel
ExcelUtils.write(response, "合同.xls", "数据", CrmContractExcelVO.class,
CrmContractConvert.INSTANCE.convertList02(pageResult.getList()));
BeanUtils.toBean(pageResult.getList(), CrmContractExcelVO.class));
}
/**
@ -117,7 +118,7 @@ public class CrmContractController {
* @param pageResult 简单的合同分页结果
* @return 详细的合同分页结果
*/
private PageResult<ContractRespVO> buildContractDetailPage(PageResult<CrmContractDO> pageResult) {
private PageResult<CrmContractRespVO> buildContractDetailPage(PageResult<CrmContractDO> pageResult) {
List<CrmContractDO> contactList = pageResult.getList();
if (CollUtil.isEmpty(contactList)) {
return PageResult.empty(pageResult.getTotal());

View File

@ -1,37 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - CRM 合同 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ContractRespVO extends CrmContractBaseVO {
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "创建人", example = "25682")
private String creator;
@Schema(description = "创建人名字", example = "test")
private String creatorName;
@Schema(description = "客户名字", example = "test")
private String customerName;
@Schema(description = "负责人", example = "test")
private String ownerUserName;
@Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer auditStatus;
}

View File

@ -1,14 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - CRM 合同创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmContractCreateReqVO extends CrmContractBaseVO {
}

View File

@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @puhui999导出注解哈
@Schema(description = "管理后台 - CRM 合同 Response VO")
@Data
public class CrmContractRespVO {
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
private Long id;
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
private String name;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336")
private Long customerId;
@Schema(description = "商机编号", example = "10864")
private Long businessId;
@Schema(description = "工作流编号", example = "1043")
private Long processInstanceId;
@Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime orderDate;
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17144")
private Long ownerUserId;
// TODO @芋艿未来应该支持自动生成
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101")
private String no;
@Schema(description = "开始时间")
private LocalDateTime startTime;
@Schema(description = "结束时间")
private LocalDateTime endTime;
@Schema(description = "合同金额", example = "5617")
private Integer price;
@Schema(description = "整单折扣")
private Integer discountPercent;
@Schema(description = "产品总金额", example = "19510")
private Integer productPrice;
@Schema(description = "联系人编号", example = "18546")
private Long contactId;
@Schema(description = "公司签约人", example = "14036")
private Long signUserId;
@Schema(description = "最后跟进时间")
private LocalDateTime contactLastTime;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "创建人", example = "25682")
private String creator;
@Schema(description = "创建人名字", example = "test")
private String creatorName;
@Schema(description = "客户名字", example = "test")
private String customerName;
@Schema(description = "负责人", example = "test")
private String ownerUserName;
@Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer auditStatus;
}

View File

@ -1,81 +1,101 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmBusinessParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmContactParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 合同 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Schema(description = "管理后台 - CRM 合同创建/更新 Request VO")
@Data
public class CrmContractBaseVO {
public class CrmContractSaveReqVO {
// TODO @dhb52类似 no 字段的 example 要写xia
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
private Long id;
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@DiffLogField(name = "合同名称")
@NotNull(message = "合同名称不能为空")
private String name;
// TODO @dhb52这个必须传递
@Schema(description = "客户编号", example = "18336")
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336")
@DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
@NotNull(message = "客户编号不能为空")
private Long customerId;
@Schema(description = "商机编号", example = "10864")
@DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME)
private Long businessId;
@Schema(description = "工作流编号", example = "1043")
@DiffLogField(name = "工作流编号")
private Long processInstanceId;
// TODO @dhb52这个必须传递
@Schema(description = "下单日期")
@Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
@DiffLogField(name = "下单日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@NotNull(message = "下单日期不能为空")
private LocalDateTime orderDate;
// TODO @dhb52这个必须传递
@Schema(description = "负责人的用户编号", example = "17144")
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17144")
@DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME)
@NotNull(message = "负责人不能为空")
private Long ownerUserId;
// TODO @芋艿未来应该支持自动生成
// TODO @dhb52这个必须传递
@Schema(description = "合同编号")
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101")
@DiffLogField(name = "合同编号")
@NotNull(message = "合同编号不能为空")
private String no;
@Schema(description = "开始时间")
@DiffLogField(name = "开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "结束时间")
@DiffLogField(name = "结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "合同金额", example = "5617")
@DiffLogField(name = "合同金额")
private Integer price;
@Schema(description = "整单折扣")
@DiffLogField(name = "整单折扣")
private Integer discountPercent;
@Schema(description = "产品总金额", example = "19510")
@DiffLogField(name = "产品总金额")
private Integer productPrice;
@Schema(description = "联系人编号", example = "18546")
@DiffLogField(name = "联系人", function = CrmContactParseFunction.NAME)
private Long contactId;
@Schema(description = "公司签约人", example = "14036")
@DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME)
private Long signUserId;
@Schema(description = "最后跟进时间")
@DiffLogField(name = "最后跟进时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactLastTime;
@Schema(description = "备注", example = "你猜")
@DiffLogField(name = "备注")
private String remark;
// TODO @dhb52增加一个 status 字段具体有哪些值你来枚举下主要页面上有个草稿提交审核的流程可以看看然后要对接工作流这块也可以看看不确定的地方问我
}

View File

@ -1,20 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - CRM 合同更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmContractUpdateReqVO extends CrmContractBaseVO {
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "合同编号不能为空")
private Long id;
}

View File

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

View File

@ -28,6 +28,4 @@ public class CrmCustomerTransferReqVO {
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer oldOwnerPermissionLevel;
// TODO @puhui999联系人商机合同的转移
}

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysDeptParseFunction;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@ -7,9 +9,6 @@ import lombok.Data;
import java.util.List;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_DEPT_BY_ID;
@Schema(description = "管理后台 - 客户限制配置创建/更新 Request VO")
@Data
public class CrmCustomerLimitConfigSaveReqVO {
@ -23,11 +22,11 @@ public class CrmCustomerLimitConfigSaveReqVO {
private Integer type;
@Schema(description = "规则适用人群")
@DiffLogField(name = "规则适用人群", function = GET_ADMIN_USER_BY_ID)
@DiffLogField(name = "规则适用人群", function = SysAdminUserParseFunction.NAME)
private List<Long> userIds;
@Schema(description = "规则适用部门")
@DiffLogField(name = "规则适用部门", function = GET_DEPT_BY_ID)
@DiffLogField(name = "规则适用部门", function = SysDeptParseFunction.NAME)
private List<Long> deptIds;
@Schema(description = "数量上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384")

View File

@ -1,31 +1,33 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
@Tag(name = "管理后台 - 跟进记录")
@ -36,6 +38,10 @@ public class CrmFollowUpRecordController {
@Resource
private CrmFollowUpRecordService crmFollowUpRecordService;
@Resource
private CrmContactService contactService;
@Resource
private CrmBusinessService businessService;
@PostMapping("/create")
@Operation(summary = "创建跟进记录")
@ -75,20 +81,19 @@ public class CrmFollowUpRecordController {
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')")
public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
PageResult<CrmFollowUpRecordDO> pageResult = crmFollowUpRecordService.getFollowUpRecordPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出跟进记录 Excel")
@PreAuthorize("@ss.hasPermission('crm:follow-up-record:export')")
@OperateLog(type = EXPORT)
public void exportFollowUpRecordExcel(@Valid CrmFollowUpRecordPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CrmFollowUpRecordDO> list = crmFollowUpRecordService.getFollowUpRecordPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "跟进记录.xls", "数据", CrmFollowUpRecordRespVO.class,
BeanUtils.toBean(list, CrmFollowUpRecordRespVO.class));
/// 拼接数据
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);
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())));
});
return success(voPageResult);
}
}

View File

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

View File

@ -1,13 +1,14 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_FOLLOW_UP_TYPE;
@Schema(description = "管理后台 - 跟进记录 Response VO")
@Data
@ -15,40 +16,35 @@ import java.time.LocalDateTime;
public class CrmFollowUpRecordRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800")
@ExcelProperty("编号")
private Long id;
@Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("数据类型")
private Integer bizType;
@Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564")
@ExcelProperty("数据编号")
private Long bizId;
@Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty(value = "跟进类型", converter = DictConvert.class)
@DictFormat("crm_follow_up_type") // TODO 代码优化建议设置到对应的 DictTypeConstants 枚举类中
@DictFormat(CRM_FOLLOW_UP_TYPE)
private Integer type;
@Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("跟进内容")
private String content;
@Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("下次联系时间")
private LocalDateTime nextTime;
@Schema(description = "关联的商机编号数组")
@ExcelProperty("关联的商机编号数组")
private String businessIds;
private List<Long> businessIds;
@Schema(description = "关联的商机名称数组")
private List<String> businessNames;
@Schema(description = "关联的联系人编号数组")
@ExcelProperty("关联的联系人编号数组")
private String contactIds;
private List<Long> contactIds;
@Schema(description = "关联的联系人名称数组")
private List<String> contactNames;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 跟进记录新增/修改 Request VO")
@Data
@ -35,9 +36,9 @@ public class CrmFollowUpRecordSaveReqVO {
private LocalDateTime nextTime;
@Schema(description = "关联的商机编号数组")
private String businessIds;
private List<Long> businessIds;
@Schema(description = "关联的联系人编号数组")
private String contactIds;
private List<Long> contactIds;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.crm.controller.admin.message;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.message.CrmMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - CRM消息")
@RestController
@RequestMapping("/crm/message")
@Validated
public class CrmMessageController {
@Resource
private CrmMessageService crmMessageService;
// TODO 芋艿未来可能合并到 CrmCustomerController
@GetMapping("/todayCustomer") // TODO @dbh52优先级低url 使用中划线项目规范然后叫 today-customer-page通过 page 体现出它是个分页接口
@Operation(summary = "今日需联系客户")
@PreAuthorize("@ss.hasPermission('crm:message:todayCustomer')")
public CommonResult<PageResult<CrmCustomerRespVO>> getTodayCustomerPage(@Valid CrmTodayCustomerPageReqVO pageReqVO) {
PageResult<CrmCustomerDO> pageResult = crmMessageService.getTodayCustomerPage(pageReqVO, getLoginUserId());
return success(BeanUtils.toBean(pageResult, CrmCustomerRespVO.class));
}
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.crm.controller.admin.message.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
import cn.iocoder.yudao.module.crm.enums.message.CrmContactStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 今日需联系客户 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmTodayCustomerPageReqVO extends PageParam {
// TODO @dbh52CrmContactStatusEnum 可以直接枚举三个 Integer一般来说枚举类尽量给数据模型用这样枚举类少更聚焦这里的枚举更多是专门给这个接口用的哈
@Schema(description = "联系状态", example = "1")
@InEnum(CrmContactStatusEnum.class)
private Integer contactStatus;
@Schema(description = "场景类型", example = "1")
@InEnum(CrmSceneTypeEnum.class)
private Integer sceneType;
}

View File

@ -42,6 +42,7 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
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.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_PRODUCT_TYPE;
@Tag(name = "管理后台 - CRM 产品")
@ -102,7 +103,7 @@ public class CrmProductController {
@Operation(summary = "获得产品分页")
@PreAuthorize("@ss.hasPermission('crm:product:query')")
public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) {
PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO);
PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO, getLoginUserId());
return success(new PageResult<>(getProductDetailList(pageResult.getList()), pageResult.getTotal()));
}
@ -113,7 +114,7 @@ public class CrmProductController {
public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO,
HttpServletResponse response) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CrmProductDO> list = productService.getProductPage(exportReqVO).getList();
List<CrmProductDO> list = productService.getProductPage(exportReqVO, getLoginUserId()).getList();
// 导出 Excel
ExcelUtils.write(response, "产品.xls", "数据", CrmProductRespVO.class,
getProductDetailList(list));

View File

@ -2,7 +2,8 @@ package cn.iocoder.yudao.module.crm.convert.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
@ -27,15 +28,6 @@ public interface CrmBusinessConvert {
CrmBusinessConvert INSTANCE = Mappers.getMapper(CrmBusinessConvert.class);
CrmBusinessDO convert(CrmBusinessSaveReqVO bean);
CrmBusinessDO convert(CrmBusinessUpdateReqVO bean);
CrmBusinessRespVO convert(CrmBusinessDO bean);
List<CrmBusinessRespVO> convert(List<CrmBusinessDO> bean);
List<CrmBusinessExcelVO> convertList02(List<CrmBusinessDO> list);
@Mapping(target = "bizId", source = "reqVO.id")
CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId);

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.convert.businessproduct;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@ -16,5 +16,5 @@ import org.mapstruct.factory.Mappers;
public interface CrmBusinessProductConvert {
CrmBusinessProductConvert INSTANCE = Mappers.getMapper(CrmBusinessProductConvert.class);
CrmBusinessProductDO convert(CrmProductSaveReqVO product);
CrmBusinessProductDO convert(CrmBusinessProductSaveReqVO product);
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.convert.businessstatus;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@ -19,10 +18,6 @@ public interface CrmBusinessStatusConvert {
CrmBusinessStatusConvert INSTANCE = Mappers.getMapper(CrmBusinessStatusConvert.class);
CrmBusinessStatusDO convert(CrmBusinessStatusSaveReqVO bean);
CrmBusinessStatusRespVO convert(CrmBusinessStatusDO bean);
List<CrmBusinessStatusRespVO> convertList(List<CrmBusinessStatusDO> list);
PageResult<CrmBusinessStatusRespVO> convertPage(PageResult<CrmBusinessStatusDO> page);

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.convert.businessstatustype;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@ -25,7 +24,6 @@ public interface CrmBusinessStatusTypeConvert {
CrmBusinessStatusTypeConvert INSTANCE = Mappers.getMapper(CrmBusinessStatusTypeConvert.class);
CrmBusinessStatusTypeDO convert(CrmBusinessStatusTypeSaveReqVO bean);
CrmBusinessStatusTypeRespVO convert(CrmBusinessStatusTypeDO bean);

View File

@ -4,7 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
@ -31,10 +32,6 @@ public interface CrmContactConvert {
CrmContactRespVO convert(CrmContactDO bean);
List<CrmContactRespVO> convertList(List<CrmContactDO> list);
PageResult<CrmContactRespVO> convertPage(PageResult<CrmContactDO> page);
@Mapping(target = "bizId", source = "reqVO.id")
CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId);

View File

@ -2,7 +2,8 @@ package cn.iocoder.yudao.module.crm.convert.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
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.permission.bo.CrmPermissionTransferReqBO;
@ -27,24 +28,12 @@ public interface CrmContractConvert {
CrmContractConvert INSTANCE = Mappers.getMapper(CrmContractConvert.class);
CrmContractDO convert(CrmContractCreateReqVO bean);
CrmContractDO convert(CrmContractUpdateReqVO bean);
ContractRespVO convert(CrmContractDO bean);
List<ContractRespVO> convertList(List<CrmContractDO> list);
PageResult<ContractRespVO> convertPage(PageResult<CrmContractDO> page);
List<CrmContractExcelVO> convertList02(List<CrmContractDO> list);
@Mapping(target = "bizId", source = "reqVO.id")
CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId);
default PageResult<ContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
List<CrmCustomerDO> customerList) {
PageResult<ContractRespVO> voPageResult = BeanUtils.toBean(pageResult, ContractRespVO.class);
default PageResult<CrmContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
List<CrmCustomerDO> customerList) {
PageResult<CrmContractRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmContractRespVO.class);
// 拼接关联字段
Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
voPageResult.getList().forEach(contract -> {

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
@ -14,8 +15,6 @@ import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
// TODO @puhui999界面做成一个 list 列表字段是 id跟进人跟进方式跟进时间跟进内容下次联系时间关联联系人关联商机
// TODO @puhui999界面记录时弹窗表单字段是跟进方式跟进内容下次联系时间关联联系人关联商机其中关联联系人关联商机要做成对应的组件列
/**
* 跟进记录 DO
*
@ -55,7 +54,7 @@ public class CrmFollowUpRecordDO extends BaseDO {
/**
* 跟进类型
*
* TODO @puhui999可以搞个数据字典打电话发短信上门拜访微信邮箱QQ
* 关联 {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} 字典
*/
private Integer type;
/**

View File

@ -5,13 +5,17 @@ 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.customer.vo.CrmCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.message.CrmContactStatusEnum;
import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.lang.Nullable;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
@ -63,4 +67,57 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
return selectJoinList(CrmCustomerDO.class, query);
}
/**
* 待办事项 - 今日需联系客户
*
* @param pageReqVO 分页请求参数
* @param userId 当前用户ID
* @return 分页结果
*/
default PageResult<CrmCustomerDO> selectTodayCustomerPage(CrmTodayCustomerPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null);
query.selectAll(CrmCustomerDO.class)
.leftJoin(CrmFollowUpRecordDO.class, CrmFollowUpRecordDO::getBizId, CrmCustomerDO::getId)
.eq(CrmFollowUpRecordDO::getType, CrmBizTypeEnum.CRM_CUSTOMER.getType());
// 拼接自身的查询条件
// TODO @dbh52这里不仅仅要获得 todaytomorrow而是 today 要获取今天的 00:00:00 这种
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate yesterday = today.minusDays(1);
if (pageReqVO.getContactStatus().equals(CrmContactStatusEnum.NEEDED_TODAY.getType())) {
// 今天需联系
// 1.客户下一次联系时间 今天
// 2. 无法找到今天创建的跟进记录
query.between(CrmCustomerDO::getContactNextTime, today, tomorrow)
// TODO @dbh52是不是查询 CrmCustomerDO::contactLastTime < today因为今天联系过应该会更新该字段减少链表查询
.between(CrmFollowUpRecordDO::getCreateTime, today, tomorrow)
.isNull(CrmFollowUpRecordDO::getId);
} else if (pageReqVO.getContactStatus().equals(CrmContactStatusEnum.EXPIRED.getType())) {
// 已逾期
// 1. 客户下一次联系时间 <= 昨天
// 2. 无法找到今天创建的跟进记录
// TODO @dbh52是不是 contactNextTime 在当前时间之前 contactLastTime < contactNextTime说白了就是下次联系时间超过当前时间且最后联系时间没去联系
query.le(CrmCustomerDO::getContactNextTime, yesterday)
.between(CrmFollowUpRecordDO::getCreateTime, today, tomorrow)
.isNull(CrmFollowUpRecordDO::getId);
} else if (pageReqVO.getContactStatus().equals(CrmContactStatusEnum.ALREADY_CONTACT.getType())) {
// 已联系
// 1.客户下一次联系时间 今天
// 2. 找到今天创建的跟进记录
query.between(CrmCustomerDO::getContactNextTime, today, tomorrow)
// TODO @dbh52contactLastTime 是今天
.between(CrmFollowUpRecordDO::getCreateTime, today, tomorrow)
.isNotNull(CrmFollowUpRecordDO::getId);
} else {
// TODO: 参数错误是不是要兜一下底直接抛出异常就好啦
}
return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
}
}

View File

@ -19,9 +19,6 @@ public interface CrmFollowUpRecordMapper extends BaseMapperX<CrmFollowUpRecordDO
return selectPage(reqVO, new LambdaQueryWrapperX<CrmFollowUpRecordDO>()
.eqIfPresent(CrmFollowUpRecordDO::getBizType, reqVO.getBizType())
.eqIfPresent(CrmFollowUpRecordDO::getBizId, reqVO.getBizId())
.eqIfPresent(CrmFollowUpRecordDO::getType, reqVO.getType())
.betweenIfPresent(CrmFollowUpRecordDO::getNextTime, reqVO.getNextTime())
.betweenIfPresent(CrmFollowUpRecordDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CrmFollowUpRecordDO::getId));
}

View File

@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.crm.dal.mysql.product;
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.product.vo.product.CrmProductPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
import org.apache.ibatis.annotations.Mapper;
/**
@ -15,11 +17,17 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CrmProductMapper extends BaseMapperX<CrmProductDO> {
default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CrmProductDO>()
default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO, Long userId) {
MPJLambdaWrapperX<CrmProductDO> query = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_PRODUCT.getType(),
CrmProductDO::getId, userId, null, Boolean.FALSE);
// 拼接自身的查询条件
query.selectAll(CrmProductDO.class)
.likeIfPresent(CrmProductDO::getName, reqVO.getName())
.eqIfPresent(CrmProductDO::getStatus, reqVO.getStatus())
.orderByDesc(CrmProductDO::getId));
.orderByDesc(CrmProductDO::getId);
return selectJoinPage(reqVO, CrmProductDO.class, query);
}
default CrmProductDO selectByNo(String no) {

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import com.mzt.logapi.service.IParseFunction;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* CRM 商机的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Component
@Slf4j
public class CrmBusinessParseFunction implements IParseFunction {
public static final String NAME = "getBusinessById";
@Resource
private CrmBusinessService businessService;
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
}
@Override
public String functionName() {
return NAME;
}
@Override
public String apply(Object value) {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
CrmBusinessDO businessDO = businessService.getBusiness(Long.parseLong(value.toString()));
return businessDO == null ? "" : businessDO.getName();
}
}

View File

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

View File

@ -8,8 +8,6 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTRACT_BY_ID;
/**
* CRM 合同的 {@link IParseFunction} 实现类
*
@ -19,6 +17,8 @@ import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameC
@Slf4j
public class CrmContractParseFunction implements IParseFunction {
public static final String NAME = "getContractById";
@Resource
private CrmContractService contractService;
@ -29,7 +29,7 @@ public class CrmContractParseFunction implements IParseFunction {
@Override
public String functionName() {
return GET_CONTRACT_BY_ID;
return NAME;
}
@Override

View File

@ -7,7 +7,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_INDUSTRY;
/**
* 行业的 {@link IParseFunction} 实现类
@ -18,6 +17,8 @@ import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameC
@Slf4j
public class CrmCustomerIndustryParseFunction implements IParseFunction {
public static final String NAME = "getCustomerIndustry";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
@ -25,7 +26,7 @@ public class CrmCustomerIndustryParseFunction implements IParseFunction {
@Override
public String functionName() {
return GET_CUSTOMER_INDUSTRY;
return NAME;
}
@Override

View File

@ -7,7 +7,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;
import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_LEVEL;
/**
* 客户等级的 {@link IParseFunction} 实现类
@ -18,6 +17,8 @@ import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameC
@Slf4j
public class CrmCustomerLevelParseFunction implements IParseFunction {
public static final String NAME = "getCustomerLevel";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
@ -25,7 +26,7 @@ public class CrmCustomerLevelParseFunction implements IParseFunction {
@Override
public String functionName() {
return GET_CUSTOMER_LEVEL;
return NAME;
}
@Override

View File

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

View File

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

View File

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.mzt.logapi.service.IParseFunction;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 管理员名字的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Slf4j
@Component
public class SysAdminUserParseFunction implements IParseFunction {
public static final String NAME = "getAdminUserById";
@Resource
private AdminUserApi adminUserApi;
@Override
public String functionName() {
return NAME;
}
@Override
public String apply(Object value) {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
// 获取用户信息
AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString()));
if (user == null) {
log.warn("[apply][获取用户{{}}为空", value);
return "";
}
// 返回格式 芋道源码(13888888888)
String nickname = user.getNickname();
if (StrUtil.isEmpty(user.getMobile())) {
return nickname;
}
return StrUtil.format("{}({})", nickname, user.getMobile());
}
}

View File

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
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;
/**
* 地名的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Slf4j
@Component
public class SysAreaParseFunction implements IParseFunction {
public static final String NAME = "getArea";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
}
@Override
public String functionName() {
return NAME;
}
@Override
public String apply(Object value) {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
return AreaUtils.format(Integer.parseInt(value.toString()));
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 是否类型的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Component
@Slf4j
public class SysBooleanParseFunction implements IParseFunction {
public static final String NAME = "getBoolean";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
}
@Override
public String functionName() {
return NAME;
}
@Override
public String apply(Object value) {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
return DictFrameworkUtils.getDictDataLabel(DictTypeConstants.BOOLEAN_STRING, value.toString());
}
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import com.mzt.logapi.service.IParseFunction;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 管理员名字的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Slf4j
@Component
public class SysDeptParseFunction implements IParseFunction {
public static final String NAME = "getDeptById";
@Resource
private DeptApi deptApi;
@Override
public String functionName() {
return NAME;
}
@Override
public String apply(Object value) {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
// 获取部门信息
DeptRespDTO dept = deptApi.getDept(Long.parseLong(value.toString()));
if (dept == null) {
log.warn("[apply][获取部门{{}}为空", value);
return "";
}
return dept.getName();
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.crm.framework.operatelog.core;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 行业的 {@link IParseFunction} 实现类
*
* @author HUIHUI
*/
@Component
@Slf4j
public class SysSexParseFunction implements IParseFunction {
public static final String NAME = "getSex";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
}
@Override
public String functionName() {
return NAME;
}
@Override
public String apply(Object value) {
if (StrUtil.isEmptyIfStr(value)) {
return "";
}
return DictFrameworkUtils.getDictDataLabel(DictTypeConstants.USER_SEX, value.toString());
}
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.crm.service.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@ -56,6 +58,14 @@ public interface CrmBusinessService {
*/
List<CrmBusinessDO> getBusinessList(Collection<Long> ids, Long userId);
/**
* 获得商机列表
*
* @param ids 编号
* @return 商机列表
*/
List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
/**
* 获得商机分页
*

View File

@ -4,7 +4,10 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
import cn.iocoder.yudao.module.crm.convert.businessproduct.CrmBusinessProductConvert;
@ -70,13 +73,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}",
success = CRM_BUSINESS_CREATE_SUCCESS)
public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
createReqVO.setId(null);
// 1. 插入商机
CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO);
CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class)
.setOwnerUserId(userId);
businessMapper.insert(business);
// TODO 商机待定插入商机与产品的关联表校验商品存在
verifyCrmBusinessProduct(business.getId());
if (!createReqVO.getProduct().isEmpty()) {
createBusinessProducts(createReqVO.getProduct(), business.getId());
if (!createReqVO.getProducts().isEmpty()) {
createBusinessProducts(createReqVO.getProducts(), business.getId());
}
// TODO 商机待定在联系人的详情页如果直接新建商机则需要关联下这里要搞个 CrmContactBusinessDO
createContactBusiness(business.getId(), createReqVO.getContactId());
@ -110,13 +115,11 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
* @description 插入商机产品关联表
* @author lzxhqs
*/
private void createBusinessProducts(List<CrmProductSaveReqVO> products, Long businessId) {
private void createBusinessProducts(List<CrmBusinessProductSaveReqVO> products, Long businessId) {
List<CrmBusinessProductDO> list = new ArrayList<>();
for (CrmProductSaveReqVO product : products) {
CrmBusinessProductDO businessProductDO = new CrmBusinessProductDO();
businessProductDO.setBusinessId(businessId)
.setProductId(product.getId())
.setPrice(BigDecimal.valueOf(product.getPrice()));
for (CrmBusinessProductSaveReqVO product : products) {
CrmBusinessProductDO businessProductDO = CrmBusinessProductConvert.INSTANCE.convert(product);
businessProductDO.setBusinessId(businessId);
list.add(businessProductDO);
}
businessProductMapper.insertBatch(list);
@ -146,17 +149,17 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
// 2. 更新商机
CrmBusinessDO updateObj = CrmBusinessConvert.INSTANCE.convert(updateReqVO);
CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
businessMapper.updateById(updateObj);
// TODO 商机待定插入商机与产品的关联表校验商品存在
verifyCrmBusinessProduct(updateReqVO.getId());
if (!updateReqVO.getProduct().isEmpty()) {
createBusinessProducts(updateReqVO.getProduct(), updateReqVO.getId());
if (!updateReqVO.getProducts().isEmpty()) {
createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId());
}
// TODO @商机待定如果状态发生变化插入商机状态变更记录表
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessUpdateReqVO.class));
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class));
LogRecordContext.putVariable("businessName", oldBusiness.getName());
}
@ -237,6 +240,14 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
return businessMapper.selectBatchIds(ids, userId);
}
@Override
public List<CrmBusinessDO> getBusinessList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return businessMapper.selectBatchIds(ids);
}
@Override
public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
return businessMapper.selectPage(pageReqVO, userId);

View File

@ -27,9 +27,12 @@ import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
/**
@ -132,23 +135,35 @@ public class CrmClueServiceImpl implements CrmClueService {
@Transactional(rollbackFor = Exception.class)
public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) {
// 校验线索都存在
List<CrmClueDO> clues = getClueList(reqVO.getIds(), userId);
if (CollUtil.isEmpty(clues)) {
throw exception(CLUE_NOT_EXISTS);
Set<Long> clueIds = reqVO.getIds();
List<CrmClueDO> clues = getClueList(clueIds, userId);
if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) {
clueIds.removeAll(convertSet(clues, CrmClueDO::getId));
// TODO @min可以使用 StrUtil.join(",", clueIds) 简化这种常见操作
throw exception(CLUE_ANY_CLUE_NOT_EXISTS, clueIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
}
// TODO @min如果已经转化则不能重复转化
// 遍历线索(过滤掉已转化的线索)创建对应的客户
clues.stream().filter(clue -> ObjectUtil.notEqual(Boolean.TRUE, clue.getTransformStatus()))
.forEach(clue -> {
// 1. 创建客户
CrmCustomerSaveReqVO customerSaveReqVO = BeanUtils.toBean(clue, CrmCustomerSaveReqVO.class).setId(null);
Long customerId = customerService.createCustomer(customerSaveReqVO, userId);
// TODO @puhui999如果有跟进记录需要一起转过去
// 2. 更新线索
clueMapper.updateById(new CrmClueDO().setId(clue.getId())
.setTransformStatus(Boolean.TRUE).setCustomerId(customerId));
});
// 过滤出未转化的客户
// TODO @min1存在已经转化的直接提示哈避免操作的用户以为都转化成功了2常见的过滤逻辑可以使用 CollectionUtils.filterList()
List<CrmClueDO> unTransformClues = clues.stream()
.filter(clue -> ObjectUtil.notEqual(Boolean.TRUE, clue.getTransformStatus())).toList();
// 传入的线索中包含已经转化的情况抛出业务异常
if (ObjectUtil.notEqual(clues.size(), unTransformClues.size())) {
// TODO @min可以使用 StrUtil.join(",", clueIds) 简化这种常见操作
clueIds.removeAll(convertSet(unTransformClues, CrmClueDO::getId));
throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, clueIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
}
// 遍历线索(未转化的线索)创建对应的客户
unTransformClues.forEach(clue -> {
// 1. 创建客户
CrmCustomerSaveReqVO customerSaveReqVO = BeanUtils.toBean(clue, CrmCustomerSaveReqVO.class).setId(null);
Long customerId = customerService.createCustomer(customerSaveReqVO, userId);
// TODO @puhui999如果有跟进记录需要一起转过去
// 2. 更新线索
clueMapper.updateById(new CrmClueDO().setId(clue.getId())
.setTransformStatus(Boolean.TRUE).setCustomerId(customerId));
});
}
private void validateRelationDataExists(CrmClueSaveReqVO reqVO) {

View File

@ -74,6 +74,14 @@ public interface CrmContactService {
*/
List<CrmContactDO> getContactList(Collection<Long> ids, Long userId);
/**
* 获得联系人列表
*
* @param ids 编号
* @return 联系人列表
*/
List<CrmContactDO> getContactList(Collection<Long> ids);
/**
* 获得联系人列表
*

View File

@ -68,6 +68,7 @@ public class CrmContactServiceImpl implements CrmContactService {
@LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_CREATE_SUB_TYPE, bizNo = "{{#contact.id}}",
success = CRM_CONTACT_CREATE_SUCCESS)
public Long createContact(CrmContactSaveReqVO createReqVO, Long userId) {
createReqVO.setId(null);
// 1. 校验
validateRelationDataExists(createReqVO);
@ -207,6 +208,14 @@ public class CrmContactServiceImpl implements CrmContactService {
return contactMapper.selectBatchIds(ids, userId);
}
@Override
public List<CrmContactDO> getContactList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return ListUtil.empty();
}
return contactMapper.selectBatchIds(ids);
}
@Override
public List<CrmContactDO> getContactList() {
return contactMapper.selectList();

View File

@ -1,10 +1,9 @@
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.CrmContractCreateReqVO;
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.CrmContractUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import jakarta.validation.Valid;
@ -26,14 +25,14 @@ public interface CrmContractService {
* @param userId 用户编号
* @return 编号
*/
Long createContract(@Valid CrmContractCreateReqVO createReqVO, Long userId);
Long createContract(@Valid CrmContractSaveReqVO createReqVO, Long userId);
/**
* 更新合同
*
* @param updateReqVO 更新信息
*/
void updateContract(@Valid CrmContractUpdateReqVO updateReqVO);
void updateContract(@Valid CrmContractSaveReqVO updateReqVO);
/**
* 删除合同

View File

@ -4,10 +4,9 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
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.CrmContractConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
@ -50,10 +49,11 @@ public class CrmContractServiceImpl implements CrmContractService {
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
success = CRM_CONTRACT_CREATE_SUCCESS)
public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) {
public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
createReqVO.setId(null);
// TODO @合同待定插入合同商品需要搞个 BusinessProductDO
// 插入合同
CrmContractDO contract = CrmContractConvert.INSTANCE.convert(createReqVO);
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class);
contractMapper.insert(contract);
// 创建数据权限
@ -71,17 +71,17 @@ public class CrmContractServiceImpl implements CrmContractService {
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
success = CRM_CONTRACT_UPDATE_SUCCESS)
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
public void updateContract(CrmContractUpdateReqVO updateReqVO) {
public void updateContract(CrmContractSaveReqVO updateReqVO) {
// TODO @合同待定只有草稿审批中可以编辑
// 校验存在
CrmContractDO oldContract = validateContractExists(updateReqVO.getId());
// 更新合同
CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO);
CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
contractMapper.updateById(updateObj);
// TODO @合同待定插入合同商品需要搞个 BusinessProductDO
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractUpdateReqVO.class));
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class));
LogRecordContext.putVariable("contractName", oldContract.getName());
}

View File

@ -27,10 +27,10 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
@Override
public Long createFollowUpRecord(CrmFollowUpRecordSaveReqVO createReqVO) {
// 插入
CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);
crmFollowUpRecordMapper.insert(followUpRecord);
// 返回
// TODO @puhui999需要更新 bizId 对应的记录
// TODO @puhui999需要更新 businessIdscontactIds 对应的记录
return followUpRecord.getId();
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.crm.service.message;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import jakarta.validation.Valid;
/**
* CRM 代办消息 Service 接口
*
* @author dhb52
*/
public interface CrmMessageService {
/**
* TODO @dbh52注释要写下
*
* @param pageReqVO
* @return
*/
PageResult<CrmCustomerDO> getTodayCustomerPage(@Valid CrmTodayCustomerPageReqVO pageReqVO, Long userId);
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.crm.service.message;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
// TODO @dbh52注释要写下
@Component
@Validated
public class CrmMessageServiceImpl implements CrmMessageService {
@Resource
private CrmCustomerMapper customerMapper;
@Override
public PageResult<CrmCustomerDO> getTodayCustomerPage(CrmTodayCustomerPageReqVO pageReqVO, Long userId) {
return customerMapper.selectTodayCustomerPage(pageReqVO, userId);
}
}

View File

@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import jakarta.validation.Valid;
import java.util.Collection;
import java.util.List;
@ -60,7 +60,7 @@ public interface CrmProductService {
* @param pageReqVO 分页查询
* @return 产品分页
*/
PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO);
PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO, Long userId);
/**
* 获得产品

View File

@ -93,7 +93,7 @@ public class CrmProductServiceImpl implements CrmProductService {
productMapper.updateById(updateObj);
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(crmProductDO,CrmProductSaveReqVO.class));
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(crmProductDO, CrmProductSaveReqVO.class));
}
private CrmProductDO validateProductExists(Long id) {
@ -145,10 +145,9 @@ public class CrmProductServiceImpl implements CrmProductService {
return productMapper.selectBatchIds(ids);
}
// TODO @anhaohao可以接入数据权限参考 CrmCustomerService getCustomerPage
@Override
public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO) {
return productMapper.selectPage(pageReqVO);
public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO, Long userId) {
return productMapper.selectPage(pageReqVO, userId);
}
@Override

View File

@ -1,17 +1,14 @@
package cn.iocoder.yudao.module.crm.service.business;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
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.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;

View File

@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.crm.service.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractUpdateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
import jakarta.annotation.Resource;
@ -38,7 +37,7 @@ public class ContractServiceImplTest extends BaseDbUnitTest {
@Test
public void testCreateContract_success() {
// 准备参数
CrmContractCreateReqVO reqVO = randomPojo(CrmContractCreateReqVO.class);
CrmContractSaveReqVO reqVO = randomPojo(CrmContractSaveReqVO.class);
// 调用
Long contractId = contractService.createContract(reqVO, getLoginUserId());
@ -55,7 +54,7 @@ public class ContractServiceImplTest extends BaseDbUnitTest {
CrmContractDO dbContract = randomPojo(CrmContractDO.class);
contractMapper.insert(dbContract);// @Sql: 先插入出一条存在的数据
// 准备参数
CrmContractUpdateReqVO reqVO = randomPojo(CrmContractUpdateReqVO.class, o -> {
CrmContractSaveReqVO reqVO = randomPojo(CrmContractSaveReqVO.class, o -> {
o.setId(dbContract.getId()); // 设置更新的 ID
});
@ -69,7 +68,7 @@ public class ContractServiceImplTest extends BaseDbUnitTest {
@Test
public void testUpdateContract_notExists() {
// 准备参数
CrmContractUpdateReqVO reqVO = randomPojo(CrmContractUpdateReqVO.class);
CrmContractSaveReqVO reqVO = randomPojo(CrmContractSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> contractService.updateContract(reqVO), CONTRACT_NOT_EXISTS);

View File

@ -168,6 +168,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
if (ObjectUtil.notEqual(spuDO.getStatus(), ProductSpuStatusEnum.RECYCLE.getStatus())) {
throw exception(SPU_NOT_RECYCLE);
}
// TODO 芋艿可选参与活动中的商品不允许删除
// 删除 SPU
productSpuMapper.deleteById(id);
@ -235,6 +236,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
public void updateSpuStatus(ProductSpuUpdateStatusReqVO updateReqVO) {
// 校验存在
validateSpuExists(updateReqVO.getId());
// TODO 芋艿可选参与活动中的商品不允许下架
// 更新状态
ProductSpuDO productSpuDO = productSpuMapper.selectById(updateReqVO.getId()).setStatus(updateReqVO.getStatus());

View File

@ -1,18 +0,0 @@
package cn.iocoder.yudao.module.system.enums.operatelog;
/**
* functionName 常量枚举
* 方便别的模块调用
*
* @author HUIHUI
*/
// TODO @puhui999这个枚举还是放在对应的 Function 里好主要考虑 Function 实现可以更近一点哈
public interface SysParseFunctionNameConstants {
String GET_ADMIN_USER_BY_ID = "getAdminUserById"; // 获取用户信息
String GET_DEPT_BY_ID = "getDeptById"; // 获取部门信息
String GET_AREA = "getArea"; // 获取区域信息
String GET_SEX = "getSex"; // 获取性别
String GET_BOOLEAN = "getBoolean"; // 获取是否
}

View File

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

View File

@ -6,8 +6,6 @@ import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_AREA;
/**
* 地名的 {@link IParseFunction} 实现类
*
@ -17,6 +15,8 @@ import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNa
@Component
public class AreaParseFunction implements IParseFunction {
public static final String NAME = "getArea";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
@ -24,7 +24,7 @@ public class AreaParseFunction implements IParseFunction {
@Override
public String functionName() {
return GET_AREA;
return NAME;
}
@Override

View File

@ -7,8 +7,6 @@ import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_BOOLEAN;
/**
* 是否类型的 {@link IParseFunction} 实现类
*
@ -18,6 +16,8 @@ import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNa
@Slf4j
public class BooleanParseFunction implements IParseFunction {
public static final String NAME = "getBoolean";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
@ -25,7 +25,7 @@ public class BooleanParseFunction implements IParseFunction {
@Override
public String functionName() {
return GET_BOOLEAN;
return NAME;
}
@Override

View File

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

View File

@ -7,8 +7,6 @@ import com.mzt.logapi.service.IParseFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_SEX;
/**
* 行业的 {@link IParseFunction} 实现类
*
@ -18,6 +16,8 @@ import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNa
@Slf4j
public class SexParseFunction implements IParseFunction {
public static final String NAME = "getSex";
@Override
public boolean executeBefore() {
return true; // 先转换值后对比
@ -25,7 +25,7 @@ public class SexParseFunction implements IParseFunction {
@Override
public String functionName() {
return GET_SEX;
return NAME;
}
@Override