mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-31 09:30:05 +08:00
commit
acb8d3f23b
@ -6,16 +6,15 @@ import cn.iocoder.yudao.framework.common.util.io.IoUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
|
||||
import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
@ -44,6 +43,15 @@ public class BpmModelController {
|
||||
return success(model);
|
||||
}
|
||||
|
||||
@GetMapping("/get-by-key")
|
||||
@Operation(summary = "获得模型")
|
||||
@Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:model:query')")
|
||||
public CommonResult<BpmModelRespVO> getModelByKey(@RequestParam("key") String key) {
|
||||
BpmModelRespVO model = modelService.getBpmnModelByKey(key);
|
||||
return success(model);
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "新建模型")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:model:create')")
|
||||
|
@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.bpm.service.definition;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
|
||||
/**
|
||||
* Flowable流程模型接口
|
||||
@ -38,6 +37,14 @@ public interface BpmModelService {
|
||||
*/
|
||||
BpmModelRespVO getModel(String id);
|
||||
|
||||
/**
|
||||
* 获得流程模块
|
||||
*
|
||||
* @param key 流程标识
|
||||
* @return 流程模型
|
||||
*/
|
||||
BpmModelRespVO getBpmnModelByKey(String key);
|
||||
|
||||
/**
|
||||
* 修改流程模型
|
||||
*
|
||||
|
@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
@ -29,8 +31,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@ -134,6 +134,19 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||
return modelRespVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmModelRespVO getBpmnModelByKey(String key) {
|
||||
Model model = getModelByKey(key);
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
BpmModelRespVO modelRespVO = BpmModelConvert.INSTANCE.convert(model);
|
||||
// 拼接 bpmn XML
|
||||
byte[] bpmnBytes = repositoryService.getModelEditorSource(model.getId());
|
||||
modelRespVO.setBpmnXml(StrUtil.utf8Str(bpmnBytes));
|
||||
return modelRespVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
|
||||
public void updateModel(@Valid BpmModelUpdateReqVO updateReqVO) {
|
||||
|
@ -11,6 +11,7 @@ public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 合同管理 1-020-000-000 ==========
|
||||
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
|
||||
ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑");
|
||||
|
||||
// ========== 线索管理 1-020-001-000 ==========
|
||||
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
|
||||
@ -48,6 +49,9 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限");
|
||||
ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限");
|
||||
ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关联{}");
|
||||
ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数据不能为空!");
|
||||
ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户名称不能为空!");
|
||||
ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在名为【{}】的客户!");
|
||||
|
||||
// ========== 权限管理 1_020_007_000 ==========
|
||||
ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
|
||||
|
@ -27,6 +27,11 @@
|
||||
<artifactId>yudao-module-crm-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-bpm-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
@ -37,6 +42,10 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
@ -70,11 +79,5 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO @puhui999:放的位置,要整齐哈。 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -140,4 +140,12 @@ public class CrmContractController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/approve")
|
||||
@Operation(summary = "发起合同审批流程")
|
||||
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
|
||||
public CommonResult<Boolean> transfer(@RequestParam("id") Long id) {
|
||||
contractService.handleApprove(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFu
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ -35,10 +38,6 @@ public class CrmContractSaveReqVO {
|
||||
@DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME)
|
||||
private Long businessId;
|
||||
|
||||
@Schema(description = "工作流编号", example = "1043")
|
||||
@DiffLogField(name = "工作流编号")
|
||||
private Long processInstanceId;
|
||||
|
||||
@Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@DiffLogField(name = "下单日期")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ -86,16 +85,33 @@ public class CrmContractSaveReqVO {
|
||||
@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 字段:具体有哪些值,你来枚举下;主要页面上有个【草稿】【提交审核】的流程,可以看看。然后要对接工作流,这块也可以看看,不确定的地方问我。
|
||||
@Schema(description = "审批状态", example = "1")
|
||||
private Integer auditStatus;
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
private List<CrmContractProductItem> productItems;
|
||||
|
||||
@Schema(description = "商品属性")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class CrmContractProductItem {
|
||||
|
||||
@Schema(description = "产品编号", example = "20529")
|
||||
@NotNull(message = "产品编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
|
||||
@NotNull(message = "产品数量不能为空")
|
||||
private Integer count;
|
||||
|
||||
@Schema(description = "产品折扣")
|
||||
private Integer discountPercent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@ -29,9 +30,11 @@ import org.mapstruct.ap.internal.util.Collections;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
@ -198,6 +201,36 @@ public class CrmCustomerController {
|
||||
BeanUtils.toBean(list, CrmCustomerRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get-import-template")
|
||||
@Operation(summary = "获得导入客户模板")
|
||||
public void importTemplate(HttpServletResponse response) throws IOException {
|
||||
// 手动创建导出 demo
|
||||
List<CrmCustomerImportExcelVO> list = Arrays.asList(
|
||||
CrmCustomerImportExcelVO.builder().name("芋道").industryId(1).level(1).source(1).mobile("15601691300").telephone("")
|
||||
.website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("")
|
||||
.areaId(null).detailAddress("").build(),
|
||||
CrmCustomerImportExcelVO.builder().name("源码").industryId(1).level(1).source(1).mobile("15601691300").telephone("")
|
||||
.website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("")
|
||||
.areaId(null).detailAddress("").build()
|
||||
);
|
||||
// 输出
|
||||
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入客户")
|
||||
@Parameters({
|
||||
@Parameter(name = "file", description = "Excel 文件", required = true),
|
||||
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('system:customer:import')")
|
||||
public CommonResult<CrmCustomerImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
|
||||
List<CrmCustomerImportExcelVO> list = ExcelUtils.read(file, CrmCustomerImportExcelVO.class);
|
||||
return success(customerService.importCustomerList(list, updateSupport, getLoginUserId()));
|
||||
}
|
||||
|
||||
|
||||
@PutMapping("/transfer")
|
||||
@Operation(summary = "转移客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||
|
@ -0,0 +1,74 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
|
||||
|
||||
/**
|
||||
* 客户 Excel 导入 VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
|
||||
public class CrmCustomerImportExcelVO {
|
||||
|
||||
@ExcelProperty("客户名称")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty(value = "所属行业", converter = DictConvert.class)
|
||||
@DictFormat(CRM_CUSTOMER_INDUSTRY)
|
||||
private Integer industryId;
|
||||
|
||||
@ExcelProperty(value = "客户等级", converter = DictConvert.class)
|
||||
@DictFormat(CRM_CUSTOMER_LEVEL)
|
||||
private Integer level;
|
||||
|
||||
@ExcelProperty(value = "客户来源", converter = DictConvert.class)
|
||||
@DictFormat(CRM_CUSTOMER_SOURCE)
|
||||
private Integer source;
|
||||
|
||||
@ExcelProperty("手机")
|
||||
private String mobile;
|
||||
|
||||
@ExcelProperty("电话")
|
||||
private String telephone;
|
||||
|
||||
@ExcelProperty("网址")
|
||||
private String website;
|
||||
|
||||
@Size(max = 20, message = "QQ长度不能超过 20 个字符")
|
||||
@ExcelProperty("QQ")
|
||||
private String qq;
|
||||
|
||||
@Size(max = 255, message = "微信长度不能超过 255 个字符")
|
||||
@ExcelProperty("微信")
|
||||
private String wechat;
|
||||
|
||||
@Size(max = 255, message = "邮箱长度不能超过 255 个字符")
|
||||
@ExcelProperty("邮箱")
|
||||
private String email;
|
||||
|
||||
@Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
|
||||
@ExcelProperty("客户描述")
|
||||
private String description;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("地区编号")
|
||||
private Integer areaId;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
private String detailAddress;
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 客户导入 Response VO")
|
||||
@Data
|
||||
@Builder
|
||||
public class CrmCustomerImportRespVO {
|
||||
|
||||
@Schema(description = "创建成功的客户名数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> createCustomerNames;
|
||||
|
||||
@Schema(description = "更新成功的客户名数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> updateCustomerNames;
|
||||
|
||||
@Schema(description = "导入失败的客户集合,key 为客户名,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Map<String, String> failureCustomerNames;
|
||||
|
||||
}
|
@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 商机产品关联表 DO
|
||||
*
|
||||
@ -29,14 +28,12 @@ public class CrmBusinessProductDO extends BaseDO {
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 商机编号
|
||||
*
|
||||
* 关联 {@link CrmBusinessDO#getId()}
|
||||
*/
|
||||
private Long businessId;
|
||||
|
||||
/**
|
||||
* 产品编号
|
||||
*
|
||||
@ -50,29 +47,27 @@ public class CrmBusinessProductDO extends BaseDO {
|
||||
private Integer price;
|
||||
|
||||
/**
|
||||
* 销售价格
|
||||
* 销售价格, 单位:分
|
||||
*/
|
||||
private BigDecimal salesPrice;
|
||||
|
||||
private Integer salesPrice;
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
private BigDecimal count;
|
||||
|
||||
// TODO @lzxhqs:改成 discountPercent
|
||||
private Integer count;
|
||||
/**
|
||||
* 折扣
|
||||
*/
|
||||
private BigDecimal discountPercent;
|
||||
|
||||
// TODO @lzxhqs:改成 totalPrice;总计价格,和现有项目风格一致;
|
||||
private Integer discountPercent;
|
||||
/**
|
||||
* 小计(折扣后价格)
|
||||
* 总计价格(折扣后价格)
|
||||
*/
|
||||
private BigDecimal totalPrice;
|
||||
private Integer totalPrice;
|
||||
|
||||
/**
|
||||
* 单位
|
||||
*
|
||||
* 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
|
||||
*/
|
||||
private String unit;
|
||||
private Integer unit;
|
||||
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class CrmContractDO extends BaseDO {
|
||||
/**
|
||||
* 工作流编号
|
||||
*/
|
||||
private Long processInstanceId;
|
||||
private String processInstanceId;
|
||||
/**
|
||||
* 下单日期
|
||||
*/
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.dataobject.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
|
||||
import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@ -38,7 +39,7 @@ public class CrmProductDO extends BaseDO {
|
||||
/**
|
||||
* 单位
|
||||
*
|
||||
* 字典 {@link cn.iocoder.yudao.module.crm.enums.DictTypeConstants#CRM_PRODUCT_UNIT}
|
||||
* 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
|
||||
*/
|
||||
private Integer unit;
|
||||
/**
|
||||
|
@ -41,7 +41,7 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
|
||||
default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
|
||||
MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(),
|
||||
CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTRACT.getType(),
|
||||
CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
|
||||
// 拼接自身的查询条件
|
||||
mpjLambdaWrapperX.selectAll(CrmContractDO.class)
|
||||
@ -56,7 +56,7 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
|
||||
default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) {
|
||||
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
|
||||
// 构建数据权限连表条件
|
||||
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
|
||||
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), ids, userId);
|
||||
// 拼接自身的查询条件
|
||||
query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId);
|
||||
return selectJoinList(CrmContractDO.class, query);
|
||||
|
@ -101,11 +101,15 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
|
||||
return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
|
||||
}
|
||||
|
||||
default List<CrmCustomerDO> selectListByLockStatusAndOwnerUserIdNotNull(Boolean lockStatus) {
|
||||
default List<CrmCustomerDO> selectListByLockAndDealStatusAndNotPool(Boolean lockStatus, Boolean dealStatus) {
|
||||
return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
|
||||
.eq(CrmCustomerDO::getLockStatus, lockStatus)
|
||||
// TODO @puhui999:not null 可以转化成大于 0
|
||||
.isNotNull(CrmCustomerDO::getOwnerUserId));
|
||||
.eq(CrmCustomerDO::getDealStatus, dealStatus)
|
||||
.gt(CrmCustomerDO::getOwnerUserId, 0));
|
||||
}
|
||||
|
||||
default CrmCustomerDO selectByCustomerName(String name) {
|
||||
return selectOne(CrmCustomerDO::getName, name);
|
||||
}
|
||||
|
||||
default PageResult<CrmCustomerDO> selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
|
||||
|
@ -1,17 +1,9 @@
|
||||
package cn.iocoder.yudao.module.crm.framework.permission.core.util;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
@ -30,22 +22,6 @@ public class CrmPermissionUtils {
|
||||
return SingletonManager.getPermissionApi().hasAnyRoles(getLoginUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode());
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个貌似直接放到 CrmPermissionService 会更好?
|
||||
/**
|
||||
* 校验权限
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @param userId 用户编号
|
||||
* @param levelEnum 权限级别
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) {
|
||||
List<CrmPermissionDO> permissionList = SingletonManager.getCrmPermissionService().getPermissionListByBiz(bizType, bizId);
|
||||
return anyMatch(permissionList, permission ->
|
||||
ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态内部类实现单例获取
|
||||
*
|
||||
@ -54,16 +30,11 @@ public class CrmPermissionUtils {
|
||||
private static class SingletonManager {
|
||||
|
||||
private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
|
||||
private static final CrmPermissionService CRM_PERMISSION_SERVICE = SpringUtil.getBean(CrmPermissionService.class);
|
||||
|
||||
public static PermissionApi getPermissionApi() {
|
||||
return PERMISSION_API;
|
||||
}
|
||||
|
||||
public static CrmPermissionService getCrmPermissionService() {
|
||||
return CRM_PERMISSION_SERVICE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class CrmCustomerAutoPutPoolJob implements JobHandler {
|
||||
@Override
|
||||
@TenantJob
|
||||
public String execute(String param) {
|
||||
int count = customerService.customerAutoPutPoolBySystem();
|
||||
int count = customerService.autoPutCustomerPool();
|
||||
return String.format("掉入公海客户 %s 个", count);
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,14 @@ public interface CrmContractService {
|
||||
*/
|
||||
void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO);
|
||||
|
||||
/**
|
||||
* 发起合同审批流程
|
||||
*
|
||||
* @param id 合同编号
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void handleApprove(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 获得合同
|
||||
*
|
||||
@ -111,9 +119,11 @@ public interface CrmContractService {
|
||||
Long getContractCountByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 根据商机ID获取关联客户的合同数量 TODO @lzxhqs:1)方法注释,和参数注释之间要有空行;2)中英文之间有空格,更清晰,例如说 商机 ID
|
||||
* @param businessId 商机ID
|
||||
* 根据商机ID获取关联客户的合同数量
|
||||
*
|
||||
* @param businessId 商机编号
|
||||
* @return 数量
|
||||
*/
|
||||
Long selectCountByBusinessId(Long businessId);
|
||||
|
||||
}
|
||||
|
@ -2,20 +2,33 @@ package cn.iocoder.yudao.module.crm.service.contract;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import com.mzt.logapi.context.LogRecordContext;
|
||||
import com.mzt.logapi.service.impl.DiffParseFunction;
|
||||
import com.mzt.logapi.starter.annotation.LogRecord;
|
||||
@ -26,10 +39,14 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* CRM 合同 Service 实现类
|
||||
@ -40,21 +57,36 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
@Validated
|
||||
public class CrmContractServiceImpl implements CrmContractService {
|
||||
|
||||
public static final String CONTRACT_APPROVE = "contract-approve"; // 合同审批流程标识
|
||||
|
||||
@Resource
|
||||
private CrmContractMapper contractMapper;
|
||||
|
||||
@Resource
|
||||
private CrmPermissionService crmPermissionService;
|
||||
@Resource
|
||||
private CrmBusinessProductService businessProductService;
|
||||
@Resource
|
||||
private CrmProductService productService;
|
||||
@Resource
|
||||
private BpmProcessInstanceApi bpmProcessInstanceApi;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmContactService contactService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
|
||||
success = CRM_CONTRACT_CREATE_SUCCESS)
|
||||
public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
|
||||
createReqVO.setId(null);
|
||||
// TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO
|
||||
validateRelationDataExists(createReqVO);
|
||||
// 插入合同
|
||||
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class);
|
||||
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
|
||||
contractMapper.insert(contract);
|
||||
|
||||
// 创建数据权限
|
||||
@ -62,6 +94,9 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
.setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
|
||||
// 插入商机关联商品
|
||||
List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO);
|
||||
businessProductService.insertBatch(businessProduct);
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("contract", contract);
|
||||
return contract.getId();
|
||||
@ -74,12 +109,21 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateContract(CrmContractSaveReqVO updateReqVO) {
|
||||
// TODO @合同待定:只有草稿、审批中,可以编辑;
|
||||
if (ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) ||
|
||||
ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
|
||||
throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED);
|
||||
}
|
||||
validateRelationDataExists(updateReqVO);
|
||||
// 校验存在
|
||||
CrmContractDO oldContract = validateContractExists(updateReqVO.getId());
|
||||
// 更新合同
|
||||
CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
|
||||
contractMapper.updateById(updateObj);
|
||||
// TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO
|
||||
|
||||
// TODO puhui999: @芋艿:合同变更关联的商机后商品怎么处理?
|
||||
//List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(updateReqVO);
|
||||
//businessProductService.selectListByBusinessId()
|
||||
//diffList()
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class));
|
||||
@ -88,8 +132,55 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
|
||||
// TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
|
||||
|
||||
// TODO @合同待定:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum
|
||||
private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO) {
|
||||
// 校验商品存在
|
||||
Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId);
|
||||
List<CrmProductDO> productList = productService.getProductList(productIds);
|
||||
if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
|
||||
throw exception(PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
|
||||
return convertList(reqVO.getProductItems(), productItem -> {
|
||||
CrmBusinessProductDO businessProduct = BeanUtils.toBean(productMap.get(productItem.getId()), CrmBusinessProductDO.class);
|
||||
businessProduct.setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId())
|
||||
.setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()).setTotalPrice(calculator(businessProduct));
|
||||
return businessProduct;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算商品总价
|
||||
*
|
||||
* @param businessProduct 关联商品
|
||||
* @return 商品总价
|
||||
*/
|
||||
private Integer calculator(CrmBusinessProductDO businessProduct) {
|
||||
int price = businessProduct.getPrice() * businessProduct.getCount();
|
||||
if (businessProduct.getDiscountPercent() == null) {
|
||||
return price;
|
||||
}
|
||||
return MoneyUtils.calculateRatePriceFloor(price, (double) (businessProduct.getDiscountPercent() / 100));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验关联数据是否存在
|
||||
*
|
||||
* @param reqVO 请求
|
||||
*/
|
||||
private void validateRelationDataExists(CrmContractSaveReqVO reqVO) {
|
||||
// 1. 校验客户
|
||||
if (reqVO.getCustomerId() != null && customerService.getCustomer(reqVO.getCustomerId()) == null) {
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
}
|
||||
// 2. 校验负责人
|
||||
if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
// 4. 如果有关联商机,则需要校验存在
|
||||
if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) {
|
||||
throw exception(BUSINESS_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@ -141,6 +232,18 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void handleApprove(Long id, Long userId) {
|
||||
// 创建合同审批流程实例
|
||||
String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
|
||||
.setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id)));
|
||||
|
||||
// 更新合同工作流编号
|
||||
contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId)
|
||||
.setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
|
||||
}
|
||||
|
||||
//======================= 查询相关 =======================
|
||||
|
||||
@Override
|
||||
@ -182,6 +285,5 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
public Long selectCountByBusinessId(Long businessId) {
|
||||
return contractMapper.selectCountByBusinessId(businessId);
|
||||
}
|
||||
|
||||
// TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒;
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package cn.iocoder.yudao.module.crm.service.contract.listener;
|
||||
|
||||
public class CrmContractResultListener {
|
||||
// TODO puhui999: @芋艿: 艿艿写一下这个,没研究明白哈哈
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
package cn.iocoder.yudao.module.crm.service.customer;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
|
||||
@ -109,6 +106,16 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId);
|
||||
|
||||
/**
|
||||
* 批量导入客户
|
||||
*
|
||||
* @param importCustomers 导入客户列表
|
||||
* @param isUpdateSupport 是否支持更新
|
||||
* @param userId 用户编号
|
||||
* @return 导入结果
|
||||
*/
|
||||
CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport, Long userId);
|
||||
|
||||
// ==================== 公海相关操作 ====================
|
||||
|
||||
/**
|
||||
@ -127,13 +134,12 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive);
|
||||
|
||||
// TODO @puhui999:autoPutCustomerPool,注释说明是系统就好哈;
|
||||
/**
|
||||
* 【系统】客户自动掉入公海
|
||||
*
|
||||
* @return 掉入公海数量
|
||||
*/
|
||||
int customerAutoPutPoolBySystem();
|
||||
int autoPutCustomerPool();
|
||||
|
||||
PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
|
@ -3,15 +3,14 @@ package cn.iocoder.yudao.module.crm.service.customer;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
|
||||
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
|
||||
@ -41,12 +40,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
|
||||
@ -96,7 +93,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
|
||||
|
||||
// 2. 插入客户
|
||||
CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class)
|
||||
CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class).setOwnerUserId(userId)
|
||||
.setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
|
||||
customerMapper.insert(customer);
|
||||
|
||||
@ -235,6 +232,54 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
return customer.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport, Long userId) {
|
||||
if (CollUtil.isEmpty(importCustomers)) {
|
||||
throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
|
||||
}
|
||||
CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>())
|
||||
.updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build();
|
||||
importCustomers.forEach(importCustomer -> {
|
||||
// 校验,判断是否有不符合的原因
|
||||
try {
|
||||
validateCustomerForCreate(importCustomer);
|
||||
} catch (ServiceException ex) {
|
||||
respVO.getFailureCustomerNames().put(importCustomer.getName(), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
// 判断如果不存在,在进行插入
|
||||
CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());
|
||||
if (existCustomer == null) {
|
||||
CrmCustomerDO customer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class).setOwnerUserId(userId)
|
||||
.setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
|
||||
customerMapper.insert(customer);
|
||||
respVO.getCreateCustomerNames().add(importCustomer.getName());
|
||||
// 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
|
||||
return;
|
||||
}
|
||||
// 如果存在,判断是否允许更新
|
||||
if (!isUpdateSupport) {
|
||||
respVO.getFailureCustomerNames().put(importCustomer.getName(),
|
||||
StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName()));
|
||||
return;
|
||||
}
|
||||
CrmCustomerDO updateCustomer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class);
|
||||
updateCustomer.setId(existCustomer.getId());
|
||||
customerMapper.updateById(updateCustomer);
|
||||
respVO.getUpdateCustomerNames().add(importCustomer.getName());
|
||||
});
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) {
|
||||
// 校验客户名称不能为空
|
||||
if (StrUtil.isEmptyIfStr(importCustomer.getName())) {
|
||||
throw exception(CUSTOMER_CREATE_NAME_NOT_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 公海相关操作 ====================
|
||||
|
||||
@Override
|
||||
@ -313,23 +358,22 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int customerAutoPutPoolBySystem() {
|
||||
public int autoPutCustomerPool() {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (poolConfig == null || !poolConfig.getEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
// 1. 获取没有锁定的不在公海的客户
|
||||
List<CrmCustomerDO> customerList = customerMapper.selectListByLockStatusAndOwnerUserIdNotNull(Boolean.FALSE);
|
||||
List<CrmCustomerDO> poolCustomerList = CollectionUtils.filterList(customerList, customer -> {
|
||||
// TODO @puhui999:建议这里作为一个查询条件哈,不放内存里过滤;
|
||||
// 1.1 未成交放入公海
|
||||
if (!customer.getDealStatus()) {
|
||||
return (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0;
|
||||
}
|
||||
// 1.2 未跟进放入公海
|
||||
// 1.1 获取没有锁定的不在公海的客户且没有成交的
|
||||
List<CrmCustomerDO> notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE);
|
||||
// 1.2 获取没有锁定的不在公海的客户且成交的
|
||||
List<CrmCustomerDO> dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE);
|
||||
List<CrmCustomerDO> poolCustomerList = new ArrayList<>();
|
||||
poolCustomerList.addAll(filterList(notDealCustomerList, customer ->
|
||||
(poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0));
|
||||
poolCustomerList.addAll(filterList(dealCustomerList, customer -> {
|
||||
LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
|
||||
return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
|
||||
});
|
||||
}));
|
||||
|
||||
// 2. 逐个放入公海
|
||||
int count = 0;
|
||||
|
@ -33,7 +33,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils.hasPermission;
|
||||
|
||||
/**
|
||||
* 跟进记录 Service 实现类
|
||||
@ -119,7 +118,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
|
||||
// 校验存在
|
||||
CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);
|
||||
// 校验权限
|
||||
if (!hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) {
|
||||
if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) {
|
||||
throw exception(FOLLOW_UP_RECORD_DELETE_DENIED);
|
||||
}
|
||||
|
||||
|
@ -108,4 +108,15 @@ public interface CrmPermissionService {
|
||||
*/
|
||||
List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId);
|
||||
|
||||
/**
|
||||
* 校验权限
|
||||
*
|
||||
* @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
|
||||
* @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
|
||||
* @param userId 用户编号
|
||||
* @param levelEnum 权限级别
|
||||
* @return boolean
|
||||
*/
|
||||
boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum);
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner;
|
||||
@ -211,4 +212,11 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
|
||||
return permissionMapper.selectListByBizTypeAndUserId(bizType, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) {
|
||||
List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
|
||||
return anyMatch(permissionList, permission ->
|
||||
ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel()));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user