mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-29 09:18:42 +08:00 
			
		
		
		
	CRM-客户: 完善客户导入
This commit is contained in:
		| @@ -26,8 +26,8 @@ public interface ErrorCodeConstants { | ||||
|  | ||||
|     // ========== 联系人管理 1-020-003-000 ========== | ||||
|     ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在"); | ||||
|     ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode( 1_020_003_001, "联系人商机关联不存在"); | ||||
|     ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode( 1_020_003_002, "联系人已关联合同,不能删除"); | ||||
|     ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在"); | ||||
|     ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除"); | ||||
|  | ||||
|     // ========== 回款 1-020-004-000 ========== | ||||
|     ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在"); | ||||
| @@ -48,6 +48,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, "数据权限不存在"); | ||||
|   | ||||
| @@ -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; | ||||
| @@ -169,6 +172,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)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @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; | ||||
|  | ||||
| } | ||||
| @@ -107,4 +107,8 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { | ||||
|                 .gt(CrmCustomerDO::getOwnerUserId, 0)); | ||||
|     } | ||||
|  | ||||
|     default CrmCustomerDO selectByCustomerName(String name) { | ||||
|         return selectOne(CrmCustomerDO::getName, name); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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.service.customer.bo.CrmCustomerCreateReqBO; | ||||
| import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; | ||||
| @@ -108,6 +105,15 @@ public interface CrmCustomerService { | ||||
|      */ | ||||
|     Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId); | ||||
|  | ||||
|     /** | ||||
|      * 批量导入客户 | ||||
|      * | ||||
|      * @param importCustomers 导入客户列表 | ||||
|      * @param isUpdateSupport 是否支持更新 | ||||
|      * @return 导入结果 | ||||
|      */ | ||||
|     CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport); | ||||
|  | ||||
|     // ==================== 公海相关操作 ==================== | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -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,10 +40,7 @@ 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; | ||||
| @@ -236,7 +232,50 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { | ||||
|         return customer.getId(); | ||||
|     } | ||||
|  | ||||
| // ==================== 公海相关操作 ==================== | ||||
|     @Override | ||||
|     public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport) { | ||||
|         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) { | ||||
|                 customerMapper.insert(BeanUtils.toBean(importCustomer, CrmCustomerDO.class)); | ||||
|                 respVO.getCreateCustomerNames().add(importCustomer.getName()); | ||||
|                 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 | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 puhui999
					puhui999