# Conflicts:
#	dl_admin/ruoyi-admin/src/main/java/com/ruoyi/cus/service/ICusContactsService.java
#	dl_admin/ruoyi-admin/src/main/java/com/ruoyi/cus/service/impl/CusContactsServiceImpl.java
#	dl_admin/ruoyi-admin/src/main/java/com/ruoyi/cus/service/impl/CusMainServiceImpl.java
#	dl_vue/src/views/cus/main/index.vue
#	dl_vue/src/views/cus/main/viewForm.vue
This commit is contained in:
Vinjor 2025-11-22 14:14:32 +08:00
commit 447e1e93b7
29 changed files with 1223 additions and 144 deletions

View File

@ -25,4 +25,12 @@ public interface IBaseCountryService extends IService<BaseCountry> {
* @date 15:47 2025/11/17
**/
IPage<BaseCountryVO> queryListPage(BaseCountry pageReqVO, Page<BaseCountry> page);
/**
* 通过名称查询
* @author PQZ
* @date 11:32 2025/11/22
* @param nameCn 中文名称
* @return com.ruoyi.base.domain.BaseCountry
**/
BaseCountry queryByName(String nameCn);
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.base.service.impl;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.base.vo.BaseCountryVO;
@ -36,4 +37,19 @@ public class BaseCountryServiceImpl extends ServiceImpl<BaseCountryMapper, BaseC
public IPage<BaseCountryVO> queryListPage(BaseCountry pageReqVO, Page<BaseCountry> page) {
return baseCountryMapper.queryListPage(pageReqVO, page);
}
/**
* 通过名称查询
*
* @param nameCn 中文名称
* @return com.ruoyi.base.domain.BaseCountry
* @author PQZ
* @date 11:32 2025/11/22
**/
@Override
public BaseCountry queryByName(String nameCn) {
LambdaQueryWrapper<BaseCountry> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(BaseCountry::getNameCn,nameCn);
return this.getOne(lambdaQueryWrapper);
}
}

View File

@ -4,8 +4,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cus.domain.CusTimeAxis;
import com.ruoyi.cus.service.ICusTimeAxisService;
import com.ruoyi.cus.vo.CusFollowVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
@ -39,6 +43,8 @@ public class CusFollowController extends BaseController
{
@Autowired
private ICusFollowService cusFollowService;
@Autowired
private ICusTimeAxisService cusTimeAxisService;
/**
* 查询客户跟进记录列表
@ -83,8 +89,15 @@ public class CusFollowController extends BaseController
@PreAuthorize("@ss.hasPermi('cus:follow:add')")
@Log(title = "客户跟进记录", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody CusFollow cusFollow)
{
public AjaxResult add(@RequestBody CusFollow cusFollow) {
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("跟进");
cusTimeAxis.setBusiCatg("新增");
cusTimeAxis.setContent("新增客户跟进:"+cusFollow.getFollowContent()+";跟进方式:"+
cusFollow.getFollowWay()+";跟进阶段:"+cusFollow.getFollowStep()+";跟进时间:"+ DateUtil.formatDateTime(cusFollow.getFollowTime()));
cusTimeAxis.setCusId(cusFollow.getCusId());
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return toAjax(cusFollowService.save(cusFollow));
}
@ -94,8 +107,18 @@ public class CusFollowController extends BaseController
@PreAuthorize("@ss.hasPermi('cus:follow:edit')")
@Log(title = "客户跟进记录", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody CusFollow cusFollow)
{
public AjaxResult edit(@RequestBody CusFollow cusFollow) {
CusFollow old = cusFollowService.getById(cusFollow.getId());
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("跟进");
cusTimeAxis.setBusiCatg("修改");
String oldContent = "原客户跟进:"+old.getFollowContent()+";跟进方式:"+
old.getFollowWay()+";跟进阶段:"+old.getFollowStep()+";跟进时间:"+ DateUtil.formatDateTime(old.getFollowTime());
cusTimeAxis.setContent(oldContent+";修改后客户跟进:"+cusFollow.getFollowContent()+";跟进方式:"+
cusFollow.getFollowWay()+";跟进阶段:"+cusFollow.getFollowStep()+";跟进时间:"+ DateUtil.formatDateTime(cusFollow.getFollowTime()));
cusTimeAxis.setCusId(cusFollow.getCusId());
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return toAjax(cusFollowService.updateById(cusFollow));
}
@ -105,9 +128,17 @@ public class CusFollowController extends BaseController
@PreAuthorize("@ss.hasPermi('cus:follow:remove')")
@Log(title = "客户跟进记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
public AjaxResult remove(@PathVariable String[] ids) {
List<String> list = new ArrayList<>(Arrays.asList(ids));
CusFollow old = cusFollowService.getById(ids[0]);
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("跟进");
cusTimeAxis.setBusiCatg("删除");
cusTimeAxis.setContent("删除客户跟进:"+old.getFollowContent()+";跟进方式:"+
old.getFollowWay()+";跟进阶段:"+old.getFollowStep()+";跟进时间:"+ DateUtil.formatDateTime(old.getFollowTime()));
cusTimeAxis.setCusId(old.getCusId());
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return toAjax(cusFollowService.removeByIds(list));
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.cus.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@ -7,11 +8,16 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.cus.domain.CusTimeAxis;
import com.ruoyi.cus.service.ICusImportService;
import com.ruoyi.cus.service.ICusTimeAxisService;
import com.ruoyi.cus.vo.CusMainVO;
import com.ruoyi.cus.vo.MainVO;
import com.ruoyi.utils.CodeGenerator;
@ -34,21 +40,26 @@ import com.ruoyi.cus.domain.CusMain;
import com.ruoyi.cus.service.ICusMainService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 客户信息Controller
*
*
* @author vinjor-m
* @date 2025-11-03
*/
@RestController
@RequestMapping("/cus/main")
public class CusMainController extends BaseController
{
public class CusMainController extends BaseController {
@Autowired
private ICusMainService cusMainService;
@Autowired
private CodeGenerator codeGenerator;
@Autowired
private ICusImportService cusImportService;
@Autowired
private ICusTimeAxisService cusTimeAxisService;
/**
* 生成客户编码
*/
@ -77,6 +88,7 @@ public class CusMainController extends BaseController
return AjaxResult.error(e.getMessage());
}
}
/**
* 查询客户信息列表
*/
@ -84,10 +96,9 @@ public class CusMainController extends BaseController
@GetMapping("/list")
public AjaxResult list(MainVO cusMain,
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize)
{
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<CusMain> page = new Page<>(pageNum, pageSize);
IPage<MainVO> list = cusMainService.queryListPage(cusMain,page);
IPage<MainVO> list = cusMainService.queryListPage(cusMain, page);
return success(list);
}
@ -97,19 +108,35 @@ public class CusMainController extends BaseController
@PreAuthorize("@ss.hasPermi('cus:main:export')")
@Log(title = "客户信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CusMain cusMain)
{
public void export(HttpServletResponse response, CusMain cusMain) {
List<CusMain> list = cusMainService.list();
ExcelUtil<CusMain> util = new ExcelUtil<CusMain>(CusMain.class);
util.exportExcel(response, list, "客户信息数据");
}
/**
* 客户信息导入
*
* @param file TODO
* @return com.ruoyi.common.core.domain.AjaxResult
* @author PQZ
* @date 14:29 2025/11/19
**/
@Log(title = "客户信息导入", businessType = BusinessType.IMPORT)
@PostMapping("/importCus")
public AjaxResult importCusAndContacts(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return AjaxResult.error("上传文件不能为空");
}
return cusImportService.importCusAndContacts(file);
}
/**
* 获取客户信息详细信息--编辑
*/
@PreAuthorize("@ss.hasPermi('cus:main:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id){
public AjaxResult getInfo(@PathVariable("id") String id) {
return success(cusMainService.getDataById(id));
}
@ -118,7 +145,7 @@ public class CusMainController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('cus:main:query')")
@GetMapping(value = "/viewData")
public AjaxResult viewData(String id){
public AjaxResult viewData(String id) {
return success(cusMainService.viewData(id));
}
@ -128,8 +155,8 @@ public class CusMainController extends BaseController
@PreAuthorize("@ss.hasPermi('cus:main:add')")
@Log(title = "客户信息", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody CusMainVO cusMainVO){
if(StringUtils.isEmpty(cusMainVO.getMainInfo().getId())){
public AjaxResult add(@RequestBody CusMainVO cusMainVO) {
if (StringUtils.isEmpty(cusMainVO.getMainInfo().getId())) {
cusMainVO.getMainInfo().setCusCode(codeGenerator.generate());
}
cusMainService.saveNewCus(cusMainVO);
@ -145,6 +172,37 @@ public class CusMainController extends BaseController
cusMain.setId(cusMainVO.getId());
cusMain.setCusLabels(JSON.toJSONString(cusMainVO.getCusLabelList()));
cusMainService.updateById(cusMain);
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("客户");
cusTimeAxis.setBusiCatg("修改");
List<String> cusLabelList = new ArrayList<>();
JSONArray cusLabelArray = JSON.parseArray(cusMain.getCusLabels());
for(int i = 0; i < cusLabelArray.size(); i++){
JSONObject jsonObj = cusLabelArray.getJSONObject(i);
cusLabelList.add(jsonObj.getString("name"));
}
cusTimeAxis.setContent("更新客户标签:" + String.join(",", cusLabelList));
cusTimeAxis.setCusId(cusMain.getId());
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return success();
}
/**
* 更新是否星标客户
*/
@PostMapping("/setIfStar")
public AjaxResult setIfStar(@RequestBody CusMain cusMain){
cusMainService.updateById(cusMain);
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("客户");
cusTimeAxis.setBusiCatg("修改");
cusTimeAxis.setContent(cusMain.getIfStar()?"设为星标":"取消星标");
cusTimeAxis.setCusId(cusMain.getId());
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return success();
}
@ -154,10 +212,16 @@ public class CusMainController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('cus:main:remove')")
@Log(title = "客户信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids) {
List<String> list = new ArrayList<>(Arrays.asList(ids));
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("客户");
cusTimeAxis.setBusiCatg("修改");
cusTimeAxis.setContent("删除客户信息");
cusTimeAxis.setCusId(list.get(0));
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return toAjax(cusMainService.removeByIds(list));
}
@ -165,7 +229,7 @@ public class CusMainController extends BaseController
* 获取客户资料信息--公司信息管理信息
*/
@GetMapping(value = "/getOtherInfo")
public AjaxResult getOtherInfo(String cusId){
public AjaxResult getOtherInfo(String cusId) {
return success(cusMainService.getOtherInfo(cusId));
}

View File

@ -34,6 +34,10 @@ public class CusBank extends DlBaseEntity
@Excel(name = "客户ID")
private String cusId;
/** 客户名称 */
@Excel(name = "客户名称")
private String cusName;
/** 银行账号 */
@Excel(name = "银行账号")
private String bankAccount;

View File

@ -30,6 +30,10 @@ public class CusCompany extends DlBaseEntity
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/** 客户名称 */
@Excel(name = "客户名称")
private String cusName;
/** 客户ID */
@Excel(name = "客户ID")
private String cusId;

View File

@ -36,6 +36,10 @@ public class CusContacts extends DlBaseEntity
@Excel(name = "客户ID")
private String cusId;
/** 客户名称 */
@Excel(name = "客户名称")
private String cusName;
/** 姓名 */
@Excel(name = "姓名")
private String name;

View File

@ -34,6 +34,10 @@ public class CusManager extends DlBaseEntity
@Excel(name = "客户ID")
private String cusId;
/** 客户名称 */
@Excel(name = "客户名称")
private String cusName;
/** 业务员ID */
@Excel(name = "业务员ID")
private Long userId;

View File

@ -0,0 +1,37 @@
package com.ruoyi.cus.dto;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class CusBankImportDTO {
@Excel(name = "客户名称")
private String cusName;
@Excel(name = "税号")
private String taxId;
@Excel(name = "银行名称")
private String bankName;
@Excel(name = "银行账号")
private String bankAccount;
@Excel(name = "分支机构代码")
private String branchCode;
@Excel(name = "银行代码")
private String bankCode;
@Excel(name = "币种")
private String currency;
@Excel(name = "银行swift代码")
private String swiftCode;
@Excel(name = "银行地址")
private String bankAddress;
}

View File

@ -0,0 +1,77 @@
package com.ruoyi.cus.dto;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class CusImportDTO {
// 客户主表字段
@Excel(name = "客户代码")
private String cusCode;
@Excel(name = "客户名称")
private String fullName;
@Excel(name = "客户简称")
private String shortName;
@Excel(name = "客户类型")
private String cusType;
@Excel(name = "国家/地区")
private String country;
@Excel(name = "时区")
private String zoneName;
@Excel(name = "主营产品")
private String mainProds;
@Excel(name = "企业网站")
private String siteUrl;
@Excel(name = "创建人")
private String createBy;
@Excel(name = "创建日期")
private String createTime;
@Excel(name = "修改日期")
private String updateTime;
@Excel(name = "客户备注")
private String cusRemark;
// 客户公司信息表字段
@Excel(name = "客户来源")
private String cusSource;
@Excel(name = "客户等级")
private String cusLevel;
@Excel(name = "业务类型")
private String businessType;
@Excel(name = "联系地址")
private String contactAddress;
// 客户管理信息表字段
@Excel(name = "部门")
private String dept;
@Excel(name = "业务员")
private String userName;
@Excel(name = "跟进阶段")
private String followStep;
@Excel(name = "预计转入公海原因")
private String seasReason;
@Excel(name = "公海组")
private String seasGroup;
@Excel(name = "原部门")
private String oldDept;
// 客户联系人表字段
@Excel(name = "姓名")
private String name;
@Excel(name = "昵称")
private String nickName;
@Excel(name = "邮箱")
private String email;
@Excel(name = "手机")
private String telephone;
@Excel(name = "性别")
private String sex;
@Excel(name = "生日")
private String birthday;
@Excel(name = "whatsApp")
private String whatsApp;
@Excel(name = "联系人备注")
private String contactRemark;
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.cus.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -8,11 +9,20 @@ import com.ruoyi.cus.domain.CusBank;
/**
* 客户银行资料Service接口
*
*
* @author vinjor-m
* @date 2025-11-03
*/
public interface ICusBankService extends IService<CusBank>
{
public interface ICusBankService extends IService<CusBank> {
IPage<CusBank> queryListPage(CusBank pageReqVO, Page<CusBank> page);
/**
* 批量保存银行卡信息
*
* @param cusBankList 银行集合
* @return void
* @author PQZ
* @date 15:58 2025/11/20
**/
void saveOrUpdateByAccount(List<CusBank> cusBankList);
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.cus.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -8,11 +9,20 @@ import com.ruoyi.cus.domain.CusCompany;
/**
* 客户公司信息Service接口
*
*
* @author vinjor-m
* @date 2025-11-03
*/
public interface ICusCompanyService extends IService<CusCompany>
{
public interface ICusCompanyService extends IService<CusCompany> {
IPage<CusCompany> queryListPage(CusCompany pageReqVO, Page<CusCompany> page);
/**
* 批量保存客户公司信息
*
* @param cusCompanyList 客户公司信息集合
* @return void
* @author PQZ
* @date 15:15 2025/11/20
**/
void saveOrUpdateByCusName(List<CusCompany> cusCompanyList);
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.cus.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -8,19 +9,27 @@ import com.ruoyi.cus.domain.CusContacts;
/**
* 客户联系人Service接口
*
*
* @author vinjor-m
* @date 2025-11-03
*/
public interface ICusContactsService extends IService<CusContacts>
{
public interface ICusContactsService extends IService<CusContacts> {
IPage<CusContacts> queryListPage(CusContacts pageReqVO, Page<CusContacts> page);
/**
* 批量保存客户联系人信息
*
* @param cusContactsList List<CusContacts>
* @author PQZ
* @date 9:32 2025/11/21
**/
void saveOrUpdateByCusName(List<CusContacts> cusContactsList);
/**
* 查客户所有联系人列表
* @author vinjor-M
* @author vinjor-M
* @date 17:58 2025/11/21
* @param cusContacts TODO
* @param cusContacts TODO
* @return java.util.List<com.ruoyi.cus.domain.CusContacts>
**/
List<CusContacts> selectAllList(CusContacts cusContacts);

View File

@ -0,0 +1,16 @@
package com.ruoyi.cus.service;
import com.ruoyi.common.core.domain.AjaxResult;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
public interface ICusImportService {
/**
* 导入客户及联系人信息
* @param file 导入文件
* @return 导入结果
*/
AjaxResult importCusAndContacts(MultipartFile file) throws IOException;
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.cus.service;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -14,48 +15,62 @@ import com.ruoyi.cus.vo.ViewTimeVO;
/**
* 客户信息Service接口
*
*
* @author vinjor-m
* @date 2025-11-03
*/
public interface ICusMainService extends IService<CusMain>
{
public interface ICusMainService extends IService<CusMain> {
IPage<MainVO> queryListPage(MainVO pageReqVO, Page<CusMain> page);
/**
* 新增客户信息
*
* @param cusMainVO TODO
* @author vinjor-M
* @date 15:56 2025/11/6
* @param cusMainVO TODO
**/
**/
void saveNewCus(CusMainVO cusMainVO);
/**
* 根据id查询客户信息
* @author vinjor-M
* @date 15:41 2025/11/12
*
* @param id 客户id
* @return com.ruoyi.cus.vo.CusMainVO
**/
* @author vinjor-M
* @date 15:41 2025/11/12
**/
CusMainVO getDataById(String id);
/**
* 查看客户信息面板数据对象
* @author vinjor-M
* @date 10:34 2025/11/18
*
* @param id 数据id
* @return com.ruoyi.cus.vo.CusViewVO
**/
* @author vinjor-M
* @date 10:34 2025/11/18
**/
CusViewVO viewData(String id);
/**
* 获取客户其他信息
*
* @param cusId 客户id
* @return com.ruoyi.cus.vo.CusViewVO
* @author vinjor-M
* @date 13:14 2025/11/18
* @param id 客户id
* @return com.ruoyi.cus.vo.CusViewVO
**/
**/
CusViewVO getOtherInfo(String cusId);
/**
* 通过名称保存客户主表信息
*
* @param cusMainList 客户信息集合
* @return void
* @author PQZ
* @date 14:44 2025/11/20
**/
void saveOrUpdateByCusName(List<CusMain> cusMainList);
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.cus.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -8,11 +9,20 @@ import com.ruoyi.cus.domain.CusManager;
/**
* 客户管理信息Service接口
*
*
* @author vinjor-m
* @date 2025-11-03
*/
public interface ICusManagerService extends IService<CusManager>
{
public interface ICusManagerService extends IService<CusManager> {
IPage<CusManager> queryListPage(CusManager pageReqVO, Page<CusManager> page);
/**
* 批量保存客户管理信息
*
* @param cusManagerList 客户管理信息集合
* @return void
* @author PQZ
* @date 15:15 2025/11/20
**/
void saveOrUpdateByCusName(List<CusManager> cusManagerList);
}

View File

@ -1,27 +1,33 @@
package com.ruoyi.cus.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.DlBaseEntity;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cus.mapper.CusBankMapper;
import com.ruoyi.cus.domain.CusBank;
import com.ruoyi.cus.service.ICusBankService;
import org.springframework.transaction.annotation.Transactional;
/**
* 客户银行资料Service业务层处理
*
*
* @author vinjor-m
* @date 2025-11-03
*/
@Service
public class CusBankServiceImpl extends ServiceImpl<CusBankMapper,CusBank> implements ICusBankService
{
public class CusBankServiceImpl extends ServiceImpl<CusBankMapper, CusBank> implements ICusBankService {
@Autowired
private CusBankMapper cusBankMapper;
@ -29,4 +35,67 @@ public class CusBankServiceImpl extends ServiceImpl<CusBankMapper,CusBank> impl
public IPage<CusBank> queryListPage(CusBank pageReqVO, Page<CusBank> page) {
return cusBankMapper.queryListPage(pageReqVO, page);
}
/**
* 根据银行账号保存或更新客户银行信息
*
* @param cusBankList 客户银行信息列表
* @author lingma
* @date 2025/11/20
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateByAccount(List<CusBank> cusBankList) {
if (cusBankList == null || cusBankList.isEmpty()) {
return;
}
// 1. 提取所有有效的 bankAccount 并去重
List<String> bankAccounts = cusBankList.stream()
.map(CusBank::getBankAccount)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
// 2. 查询数据库中已有的银行信息
Map<String, CusBank> existingBankMap = new HashMap<>();
if (!bankAccounts.isEmpty()) {
List<CusBank> existingBanks = this.list(new LambdaQueryWrapper<CusBank>()
.in(CusBank::getBankAccount, bankAccounts));
existingBankMap = existingBanks.stream()
.collect(Collectors.toMap(CusBank::getBankAccount, Function.identity()));
}
// 3. 分离需要新增和更新的数据
List<CusBank> toSaveList = new ArrayList<>();
List<CusBank> toUpdateList = new ArrayList<>();
for (CusBank bank : cusBankList) {
if (StringUtils.isEmpty(bank.getBankAccount())) {
continue; // 忽略没有 bankAccount 的数据
}
CusBank existing = existingBankMap.get(bank.getBankAccount());
if (existing != null) {
// 存在就更新
bank.setId(existing.getId());
//保留原来的客户id
bank.setCusId(existing.getCusId());
toUpdateList.add(bank);
} else {
// 不存在就新增
toSaveList.add(bank);
}
}
// 4. 批量保存与更新
if (!toSaveList.isEmpty()) {
this.saveBatch(toSaveList);
}
if (!toUpdateList.isEmpty()) {
this.updateBatchById(toUpdateList);
}
}
}

View File

@ -1,24 +1,32 @@
package com.ruoyi.cus.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cus.mapper.CusCompanyMapper;
import com.ruoyi.cus.domain.CusCompany;
import com.ruoyi.cus.service.ICusCompanyService;
import org.springframework.transaction.annotation.Transactional;
/**
* 客户公司信息Service业务层处理
*
*
* @author vinjor-m
* @date 2025-11-03
*/
@Service
public class CusCompanyServiceImpl extends ServiceImpl<CusCompanyMapper,CusCompany> implements ICusCompanyService
{
public class CusCompanyServiceImpl extends ServiceImpl<CusCompanyMapper, CusCompany> implements ICusCompanyService {
@Autowired
private CusCompanyMapper cusCompanyMapper;
@ -26,4 +34,67 @@ public class CusCompanyServiceImpl extends ServiceImpl<CusCompanyMapper,CusCompa
public IPage<CusCompany> queryListPage(CusCompany pageReqVO, Page<CusCompany> page) {
return cusCompanyMapper.queryListPage(pageReqVO, page);
}
/**
* 批量保存客户公司信息
*
* @param cusCompanyList 客户公司信息集合
* @return void
* @author PQZ
* @date 15:15 2025/11/20
**/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateByCusName(List<CusCompany> cusCompanyList) {
if (cusCompanyList == null || cusCompanyList.isEmpty()) {
return;
}
// 1. 提取所有有效的 cusName 并去重
List<String> cusNames = cusCompanyList.stream()
.map(CusCompany::getCusName)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
// 2. 查询数据库中已有的公司信息
Map<String, CusCompany> existingCompanyMap = new HashMap<>();
if (!cusNames.isEmpty()) {
List<CusCompany> existingCompanies = this.list(new LambdaQueryWrapper<CusCompany>()
.in(CusCompany::getCusName, cusNames));
existingCompanyMap = existingCompanies.stream()
.collect(Collectors.toMap(CusCompany::getCusName, Function.identity()));
}
// 3. 分离需要新增和更新的数据
List<CusCompany> toSaveList = new ArrayList<>();
List<CusCompany> toUpdateList = new ArrayList<>();
for (CusCompany company : cusCompanyList) {
if (StringUtils.isEmpty(company.getCusName())) {
continue; // 忽略没有 cusName 的数据
}
CusCompany existing = existingCompanyMap.get(company.getCusName());
if (existing != null) {
// 存在就更新
company.setId(existing.getId());
//保留原来的客户id
company.setCusId(existing.getCusId());
toUpdateList.add(company);
} else {
// 不存在就新增
toSaveList.add(company);
}
}
// 4. 批量保存与更新
if (!toSaveList.isEmpty()) {
this.saveBatch(toSaveList);
}
if (!toUpdateList.isEmpty()) {
this.updateBatchById(toUpdateList);
}
}
}

View File

@ -1,11 +1,18 @@
package com.ruoyi.cus.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.DlBaseEntity;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -57,4 +64,60 @@ public class CusContactsServiceImpl extends ServiceImpl<CusContactsMapper,CusCon
public void updateOtherDefault(String cusId) {
cusContactsMapper.updateOtherDefault(cusId);
}
/**
* 批量保存客户联系人信息
*
* @param cusContactsList List<CusContacts>
* @author PQZ
* @date 9:32 2025/11/21
**/
@Override
public void saveOrUpdateByCusName(List<CusContacts> cusContactsList) {
if(cusContactsList == null || cusContactsList.isEmpty()) {
return;
}
// 1. 提取有效的客户名称并去重
List<String> cusNames = cusContactsList.stream()
.map(CusContacts::getCusName)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
//2. 查询数据库中已有的客户联系人信息,并按照客户名称+姓名分组
Map<String,CusContacts> existingCusContactMap = new HashMap<>();
List<CusContacts> existingContactsList = this.list(new LambdaQueryWrapper<CusContacts>()
.in(CusContacts::getCusName, cusNames));
existingCusContactMap = existingContactsList.stream()
.collect(Collectors.toMap(
contact -> contact.getCusName() + "_" + contact.getName(),
contact -> contact,
// 保留第一个忽略后续重复项
(existing, replacement) -> existing
));
// 3. 分离需要新增和更新的数据
List<CusContacts> toSaveList = new ArrayList<>();
List<CusContacts> toUpdateList = new ArrayList<>();
for(CusContacts cusContacts : cusContactsList) {
String key = cusContacts.getCusName() + "_" + cusContacts.getName();
CusContacts existing = existingCusContactMap.get(key);
if(existing != null) {
// 存在就更新
cusContacts.setId(existing.getId());
//保留原来的客户id
cusContacts.setCusId(existing.getCusId());
toUpdateList.add(cusContacts);
} else {
// 不存在就新增
toSaveList.add(cusContacts);
}
}
// 4. 批量保存与更新
if(!toSaveList.isEmpty()) {
this.saveBatch(toSaveList);
}
if(!toUpdateList.isEmpty()) {
this.updateBatchById(toUpdateList);
}
}
}

View File

@ -0,0 +1,217 @@
package com.ruoyi.cus.service.impl;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.cus.domain.*;
import com.ruoyi.cus.dto.CusBankImportDTO;
import com.ruoyi.cus.dto.CusImportDTO;
import com.ruoyi.cus.service.*;
import com.ruoyi.utils.SnowflakeIdGenerator;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class CusImportServiceImpl implements ICusImportService {
@Autowired
private ICusMainService cusMainService;
@Autowired
private ICusCompanyService cusCompanyService;
@Autowired
private ICusManagerService cusManagerService;
@Autowired
private ICusContactsService cusContactsService;
@Autowired
private ICusBankService cusBankService;
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
/**
* 导入客户及联系人信息
*
* @param file 导入文件
* @return 导入结果
*/
@Override
public AjaxResult importCusAndContacts(MultipartFile file) throws IOException {
try {
// 先打印所有sheet页名称
Workbook workbook = new XSSFWorkbook(file.getInputStream());
int numberOfSheets = workbook.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
Sheet sheet = workbook.getSheetAt(i);
}
// 关闭workbook
workbook.close();
// 重新获取输入流用于数据读取
InputStream inputStream = file.getInputStream();
// 读取第二个sheet页数据索引为1
ExcelUtil<CusImportDTO> cusUtil = new ExcelUtil<>(CusImportDTO.class);
List<CusImportDTO> secondSheetList = cusUtil.importExcel("客户联系人", inputStream, 1);
// 重新获取输入流用于读取第三个sheet页数据
InputStream thirdSheetInputStream = file.getInputStream();
// 读取第三个sheet页数据索引为2
ExcelUtil<CusBankImportDTO> bankUtil = new ExcelUtil<>(CusBankImportDTO.class);
List<CusBankImportDTO> thirdSheetList = bankUtil.importExcel("银行资料", thirdSheetInputStream, 0);
saveCusAndBank(secondSheetList, thirdSheetList);
return AjaxResult.success("导入成功");
} catch (Exception e) {
return AjaxResult.error("导入失败:" + e.getMessage());
}
}
/**
* 数据处理
*
* @param cusList List<CusImportDTO>
* @param bankList List<CusBankImportDTO>
* @author PQZ
* @date 15:30 2025/11/19
**/
private void saveCusAndBank(List<CusImportDTO> cusList, List<CusBankImportDTO> bankList) {
if (cusList == null || cusList.isEmpty()) {
return;
}
// 使用Map按客户代码分组保留每个客户的所有联系人信息
Map<String, List<CusImportDTO>> customerGroupMap = cusList.stream()
.collect(Collectors.groupingBy(CusImportDTO::getFullName));
// 准备批量插入的集合
List<CusMain> cusMainList = new ArrayList<>();
List<CusCompany> cusCompanyList = new ArrayList<>();
List<CusManager> cusManagerList = new ArrayList<>();
List<CusContacts> cusContactsList = new ArrayList<>();
List<CusBank> cusBankList = new ArrayList<>();
// 遍历每个客户的联系人列表只保存一次客户信息但保存所有联系人
for (Map.Entry<String, List<CusImportDTO>> entry : customerGroupMap.entrySet()) {
List<CusImportDTO> customerContacts = entry.getValue();
if (customerContacts.isEmpty()) {
continue;
}
// 取第一个联系人记录作为客户基本信息因为同一客户的基本信息是重复的
CusImportDTO customerInfo = customerContacts.get(0);
// 为客户提供UUID
String customerId = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
// 提取客户主表信息 CusMain
CusMain customer = new CusMain();
//绑定客户id
customer.setId(customerId);
customer.setCusCode(customerInfo.getCusCode());
customer.setFullName(customerInfo.getFullName());
customer.setShortName(customerInfo.getShortName());
customer.setCusType(customerInfo.getCusType());
customer.setCountry(customerInfo.getCountry());
customer.setZoneName(customerInfo.getZoneName());
customer.setMainProds(customerInfo.getMainProds());
customer.setSiteUrl(customerInfo.getSiteUrl());
customer.setCreator(customerInfo.getCreateBy());
customer.setCreateTime(new Date());
customer.setUpdateTime(new Date());
customer.setRemark(customerInfo.getCusRemark());
cusMainList.add(customer);
// 提取客户公司信息表 CusCompany
CusCompany companyInfo = new CusCompany();
//绑定客户id
companyInfo.setCusId(customerId);
companyInfo.setCusName(customerInfo.getFullName());
companyInfo.setCusFrom(customerInfo.getCusSource());
companyInfo.setCusLevel(customerInfo.getCusLevel());
companyInfo.setBusiType(customerInfo.getBusinessType());
companyInfo.setContactAddress(customerInfo.getContactAddress());
companyInfo.setCreator(customerInfo.getCreateBy());
companyInfo.setCreateTime(new Date());
cusCompanyList.add(companyInfo);
// 提取客户管理信息表 CusManager
CusManager managementInfo = new CusManager();
//绑定客户id
managementInfo.setCusId(customerId);
managementInfo.setCusName(customerInfo.getFullName());
managementInfo.setUserName(customerInfo.getUserName());
managementInfo.setFollowStep(customerInfo.getFollowStep());
managementInfo.setSeasReason(customerInfo.getSeasReason());
managementInfo.setSeasGroup(customerInfo.getSeasGroup());
managementInfo.setOldDept(customerInfo.getOldDept());
managementInfo.setCreator(customerInfo.getCreateBy());
managementInfo.setCreateTime(new Date());
cusManagerList.add(managementInfo);
// 提取客户的所有联系人信息 CusContacts
for (CusImportDTO contact : customerContacts) {
CusContacts customerContact = new CusContacts();
customerContact.setId(snowflakeIdGenerator.generateId());
//绑定客户id
customerContact.setCusId(customerId);
customerContact.setCusName(contact.getFullName());
customerContact.setName(contact.getName());
customerContact.setNickName(contact.getNickName());
customerContact.setEmail(contact.getEmail());
customerContact.setTelephone(contact.getTelephone());
customerContact.setSex(contact.getSex());
customerContact.setBirthday(new Date());
customerContact.setWhatsApp(contact.getWhatsApp());
customerContact.setCreator(contact.getCreateBy());
customerContact.setCreateTime(new Date());
cusContactsList.add(customerContact);
}
//提取银行信息
for (CusBankImportDTO bank : bankList) {
if (bank.getCusName().equals(customerInfo.getFullName())) {
CusBank bankInfo = new CusBank();
//绑定客户id
bankInfo.setCusId(customerId);
bankInfo.setCusName(bank.getCusName());
bankInfo.setBankAccount(bank.getBankAccount());
bankInfo.setBankName(bank.getBankName());
bankInfo.setCurrency(bank.getCurrency());
bankInfo.setBankCode(bank.getBankCode());
bankInfo.setSwiftCode(bank.getSwiftCode());
bankInfo.setBankAddress(bank.getBankAddress());
bankInfo.setBranchCode(bank.getBranchCode());
bankInfo.setTaxId(bank.getTaxId());
cusBankList.add(bankInfo);
}
}
}
// 批量保存客户主表信息
if (!cusMainList.isEmpty()) {
cusMainService.saveOrUpdateByCusName(cusMainList);
}
// 批量保存客户公司信息表
if (!cusCompanyList.isEmpty()) {
cusCompanyService.saveOrUpdateByCusName(cusCompanyList);
}
// 批量保存客户管理信息表
if (!cusManagerList.isEmpty()) {
cusManagerService.saveOrUpdateByCusName(cusManagerList);
}
// 批量保存客户联系人信息
if (!cusContactsList.isEmpty()) {
cusContactsService.saveOrUpdateByCusName(cusContactsList);
}
//批量保存银行账户信息
if (!cusBankList.isEmpty()) {
cusBankService.saveOrUpdateByAccount(cusBankList);
}
}
}

View File

@ -1,12 +1,15 @@
package com.ruoyi.cus.service.impl;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.base.service.IBaseCountryService;
import com.ruoyi.common.core.domain.DlBaseEntity;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
@ -49,6 +52,8 @@ public class CusMainServiceImpl extends ServiceImpl<CusMainMapper,CusMain> impl
private ICusMarkService cusMarkService;
@Autowired
private ICusTimeAxisService cusTimeAxisService;
@Autowired
private IBaseCountryService baseCountryService;
@Override
public IPage<MainVO> queryListPage(MainVO pageReqVO, Page<CusMain> page) {
@ -244,6 +249,10 @@ public class CusMainServiceImpl extends ServiceImpl<CusMainMapper,CusMain> impl
CusContacts contacts = new CusContacts();
contacts.setCusId(id);
cusViewVO.setContacts(cusContactsService.selectAllList(contacts));
//3.国旗信息
if (StringUtils.isNotEmpty(cusMain.getCountry())){
cusViewVO.setCountryImg(baseCountryService.queryByName(cusMain.getCountry()).getImg());
}
return cusViewVO;
}
@ -268,4 +277,60 @@ public class CusMainServiceImpl extends ServiceImpl<CusMainMapper,CusMain> impl
return cusViewVO;
}
/**
* 通过名称保存客户主表信息
*
* @param cusMainList List<CusMain>
* @return void
* @author PQZ
* @date 14:44 2025/11/20
**/
@Override
public void saveOrUpdateByCusName(List<CusMain> cusMainList) {
if (cusMainList == null || cusMainList.isEmpty()) {
return;
}
// 提取所有需要处理的 fullName
List<String> fullNames = cusMainList.stream()
.map(CusMain::getFullName)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
// 一次性查询数据库中已存在的客户信息
Map<String, CusMain> existingCusMap = new HashMap<>();
if (!fullNames.isEmpty()) {
List<CusMain> existingCusList = this.list(
new LambdaQueryWrapper<CusMain>()
.in(CusMain::getFullName, fullNames)
);
existingCusMap = existingCusList.stream()
.collect(Collectors.toMap(CusMain::getFullName, Function.identity()));
}
// 分离需要新增和更新的客户
List<CusMain> toSaveList = new ArrayList<>();
List<CusMain> toUpdateList = new ArrayList<>();
for (CusMain cusMain : cusMainList) {
if (StringUtils.isEmpty(cusMain.getFullName())) {
continue;
}
CusMain existingCus = existingCusMap.get(cusMain.getFullName());
if (existingCus != null) {
// 如果存在设置ID并加入更新列表
cusMain.setId(existingCus.getId());
toUpdateList.add(cusMain);
} else {
// 如果不存在则生成新的ID并加入保存列表
toSaveList.add(cusMain);
}
}
// 批量保存和更新
if (!toSaveList.isEmpty()) {
this.saveBatch(toSaveList);
}
if (!toUpdateList.isEmpty()) {
this.updateBatchById(toUpdateList);
}
}
}

View File

@ -1,24 +1,32 @@
package com.ruoyi.cus.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cus.mapper.CusManagerMapper;
import com.ruoyi.cus.domain.CusManager;
import com.ruoyi.cus.service.ICusManagerService;
import org.springframework.transaction.annotation.Transactional;
/**
* 客户管理信息Service业务层处理
*
*
* @author vinjor-m
* @date 2025-11-03
*/
@Service
public class CusManagerServiceImpl extends ServiceImpl<CusManagerMapper,CusManager> implements ICusManagerService
{
public class CusManagerServiceImpl extends ServiceImpl<CusManagerMapper, CusManager> implements ICusManagerService {
@Autowired
private CusManagerMapper cusManagerMapper;
@ -26,4 +34,68 @@ public class CusManagerServiceImpl extends ServiceImpl<CusManagerMapper,CusManag
public IPage<CusManager> queryListPage(CusManager pageReqVO, Page<CusManager> page) {
return cusManagerMapper.queryListPage(pageReqVO, page);
}
/**
* 批量保存客户管理信息
*
* @param cusManagerList 客户管理信息集合
* @return void
* @author PQZ
* @date 15:15 2025/11/20
**/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateByCusName(List<CusManager> cusManagerList) {
if (cusManagerList == null || cusManagerList.isEmpty()) {
return;
}
// 1. 提取所有有效的 cusName 并去重
List<String> cusNames = cusManagerList.stream()
.map(CusManager::getCusName)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
// 2. 查询数据库中已有的管理信息
Map<String, CusManager> existingManagerMap = new HashMap<>();
if (!cusNames.isEmpty()) {
List<CusManager> existingManagers = this.list(new LambdaQueryWrapper<CusManager>()
.in(CusManager::getCusName, cusNames));
existingManagerMap = existingManagers.stream()
.collect(Collectors.toMap(CusManager::getCusName, Function.identity()));
}
// 3. 分离需要新增和更新的数据
List<CusManager> toSaveList = new ArrayList<>();
List<CusManager> toUpdateList = new ArrayList<>();
for (CusManager manager : cusManagerList) {
if (StringUtils.isEmpty(manager.getCusName())) {
continue; // 忽略没有 cusName 的数据
}
CusManager existing = existingManagerMap.get(manager.getCusName());
if (existing != null) {
// 存在就更新
manager.setId(existing.getId());
//保留原来的客户id
manager.setCusId(existing.getCusId());
toUpdateList.add(manager);
} else {
// 不存在就新增
toSaveList.add(manager);
}
}
// 4. 批量保存与更新
if (!toSaveList.isEmpty()) {
this.saveBatch(toSaveList);
}
if (!toUpdateList.isEmpty()) {
this.updateBatchById(toUpdateList);
}
}
}

View File

@ -20,6 +20,8 @@ public class CusViewVO {
private CusCompany company;
/** 管理信息 */
private CusManager manager;
/** 国旗信息*/
private String countryImg;
/** 银行信息 */
private List<CusBank> bankList;
}

View File

@ -13,7 +13,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LEFT JOIN base_time_zone btz ON main.zone_id = btz.id
<where>
<if test="entity.nameEn != null and entity.nameEn != ''"> and name_en = #{entity.nameEn}</if>
<if test="entity.nameCn != null and entity.nameCn != ''"> and name_cn = #{entity.nameCn}</if>
<if test="entity.nameCn != null and entity.nameCn != ''"> and name_cn like concat('%', #{entity.nameCn}, '%') </if>
</where>
</select>
</mapper>

View File

@ -50,7 +50,7 @@ export function updateMain(data) {
data: data
})
}
// 修改客户信息
// 设置客户标签
export function setLabels(data) {
return request({
url: '/cus/main/setLabels',
@ -58,6 +58,14 @@ export function setLabels(data) {
data: data
})
}
// 设置是否星标客户
export function setIfStar(data) {
return request({
url: '/cus/main/setIfStar',
method: 'post',
data: data
})
}
// 删除客户信息
export function delMain(id) {

View File

@ -57,12 +57,20 @@
<el-row>
<el-col :span="12">
<el-form-item label="国家/地区" label-width="100px">
<el-select style="width: 100%" v-model="formData.mainInfo.country" filterable placeholder="国家/地区" clearable>
<el-select style="width: 100%"
filterable
remote
v-model="formData.mainInfo.country"
placeholder="请输入国家/地区"
:remote-method="remoteMethod"
@change="countryChange"
:loading="countryLoading"
clearable>
<el-option
v-for="dict in countryList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
v-for="item in countryList"
:key="item.nameCn"
:label="item.nameCn+'('+item.nameEn+')'"
:value="item.nameCn"
/>
</el-select>
</el-form-item>
@ -71,10 +79,10 @@
<el-form-item label="时区" label-width="100px">
<el-select style="width: 100%" v-model="formData.mainInfo.zoneName" filterable placeholder="时区" clearable>
<el-option
v-for="dict in timeZoneList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
v-for="item in timeZoneList"
:key="item.zoneName"
:label="item.zoneName"
:value="item.zoneName"
/>
</el-select>
</el-form-item>
@ -255,6 +263,9 @@
<script>
import { getCode, addMain } from "@/api/cus/main";
import { listCountry } from "@/api/base/country";
import { zoneList } from "@/api/base/zone";
export default {
name: 'drawForm',
dicts: ['cus_main_product', 'cus_label', 'cus_type','sys_user_sex','cus_from','cus_level','cus_busi_type','cus_follow_step'],
@ -306,6 +317,7 @@ export default {
//
//
countryList: [],
countryLoading: false,
//
timeZoneList: [],
//
@ -327,7 +339,48 @@ export default {
}
};
},
created() {
this.getZoneList()
},
methods: {
countryChange(row){
// countryListzoneName
const selectedCountry = this.countryList.find(country => country.nameCn === row);
if (selectedCountry) {
this.formData.mainInfo.zoneName = selectedCountry.zoneName;
} else {
this.formData.mainInfo.zoneName = null;
}
},
remoteMethod(query){
if (query !== ''){
this.countryLoading = true;
const queryParams = {
pageNum: 1,
pageSize: 10,
nameCn: query,
}
listCountry(queryParams).then(response => {
this.countryList = response.data.records;
this.total = response.data.total;
this.countryLoading = false;
});
} else {
this.countryList = []
}
},
/**查询时区*/
getZoneList(){
zoneList().then(res => {
this.timeZoneList = res.data
})
},
/**
* 新增联系人
*/

View File

@ -44,7 +44,7 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-dropdown v-hasPermi="['cus:main:add']" @command="handleCommand">
<el-button type="primary" plain size="mini">
<el-button type="primary" plain size="mini">
新建客户<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
@ -61,7 +61,19 @@
size="mini"
@click="handleExport"
v-hasPermi="['cus:main:export']"
>导出</el-button>
>导出
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-upload2"
size="mini"
@click="handleImport"
>导入
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@ -81,10 +93,12 @@
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="星标" align="center" prop="ifStar" >
<el-table-column label="星标" align="center" prop="ifStar">
<template slot-scope="scope">
<i v-if="scope.row.ifStar" class="el-icon-star-on" style="color: rgb(64, 158, 255);font-size: 18px;cursor: pointer"></i>
<i v-else class="el-icon-star-off" style="cursor: pointer"></i>
<i v-if="scope.row.ifStar" title="取消星标" class="el-icon-star-on"
style="color: #E6A23C;font-size: 18px;cursor: pointer"
@click="setIfStar(scope.row,false)"></i>
<i v-else class="el-icon-star-off" title="星标" style="cursor: pointer" @click="setIfStar(scope.row,true)"></i>
</template>
</el-table-column>
<el-table-column label="客户简称" align="center" prop="shortName" width="130">
@ -164,7 +178,7 @@
</template>
</el-table-column>
<el-table-column label="业务类型" align="center" prop="busiType" width="120"/>
<el-table-column label="企业网站" align="center" prop="siteUrl" >
<el-table-column label="企业网站" align="center" prop="siteUrl">
<template slot-scope="scope">
<el-tooltip :content="scope.row.siteUrl" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.siteUrl }}</div>
@ -209,12 +223,14 @@
type="text"
@click="handleUpdate(scope.row)"
v-hasPermi="['cus:main:edit']"
>修改</el-button>
>修改
</el-button>
<el-button
size="mini"
type="text"
@click="handleSendEmail(scope.row)"
>发邮件</el-button>
>发邮件
</el-button>
<el-dropdown @command="handleOpt">
<span class="el-dropdown-link">
更多<i class="el-icon-arrow-down el-icon--right"></i>
@ -222,8 +238,9 @@
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="beforeCommand(scope.row,'newFollow')">新建跟进</el-dropdown-item>
<el-dropdown-item :command="beforeCommand(scope.row,'changeUser')">移交客户</el-dropdown-item>
<el-dropdown-item :command="beforeCommand(scope.row,'goSea')" >转入公海</el-dropdown-item>
<el-dropdown-item v-hasPermi="['cus:main:remove']" :command="beforeCommand(scope.row,'del')" >删除</el-dropdown-item>
<el-dropdown-item :command="beforeCommand(scope.row,'goSea')">转入公海</el-dropdown-item>
<el-dropdown-item v-hasPermi="['cus:main:remove']" :command="beforeCommand(scope.row,'del')">删除
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
@ -240,23 +257,42 @@
<!-- 添加或修改客户信息对话框 -->
<draw-form ref="drawForm"></draw-form>
<!-- 客户信息导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading"
:on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
<!-- 新建跟进-->
<follow-form ref="followForm" />
<!-- 给客户设置标签-->
<follow-form ref="followForm"/>
<!-- 给客户设置标签-->
<label-form ref="labelForm" @ok="getList"/>
</div>
</template>
<script>
import { listMain, getMain, delMain, addMain, updateMain } from "@/api/cus/main";
import { listMain, getMain, delMain, addMain, updateMain ,setIfStar} from '@/api/cus/main'
import DrawForm from './drawForm'
import { getToken } from '@/utils/auth'
import FollowForm from '../follow/followForm'
import LabelForm from './labelForm'
export default {
name: "Main",
components: { LabelForm, DrawForm ,FollowForm},
dicts: ['cus_main_product', 'cus_label', 'cus_type','sys_user_sex','cus_from','cus_level','cus_busi_type','cus_follow_step'],
name: 'Main',
components: { LabelForm, DrawForm, FollowForm },
dicts: ['cus_main_product', 'cus_label', 'cus_type', 'sys_user_sex', 'cus_from', 'cus_level', 'cus_busi_type', 'cus_follow_step'],
data() {
return {
//
@ -279,100 +315,136 @@ export default {
shortName: null,
cusType: null,
country: null,
mainProds: null,
mainProds: null
},
//
contactList:[],
chooseCusId:null,
contactList: [],
chooseCusId: null,
//
cusMain:{
id:"",
fullName:"",
cusCode:"",
cusMain: {
id: '',
fullName: '',
cusCode: ''
},
//
upload: {
//
open: false,
//
title: '',
//
isUploading: false,
//
updateSupport: 0,
//
headers: { Authorization: 'Bearer ' + getToken() },
//
url: process.env.VUE_APP_BASE_API + '/cus/main/importCus'
}
};
}
},
activated() {
//
this.getList();
this.getList()
},
created() {
this.getList();
this.getList()
},
methods: {
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false
this.upload.isUploading = false
this.$refs.upload.clearFiles()
this.$alert('<div style=\'overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;\'>' + response.msg + '</div>', '导入结果', { dangerouslyUseHTMLString: true })
this.getList()
},
//
submitFileForm() {
this.$refs.upload.submit()
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = '用户导入'
this.upload.open = true
},
/**
* 给客户设置标签
*/
setCusLabel(row){
setCusLabel(row) {
this.$refs.labelForm.setTags(row)
},
/** 客户代码点击事件 */
handleCodeClick(row) {
this.$router.push({path:'/cus/viewForm',query:{id:row.id}})
this.$router.push({ path: '/cus/viewForm', query: { id: row.id } })
},
/** 查询客户信息列表 */
getList() {
this.loading = true;
this.loading = true
listMain(this.queryParams).then(response => {
this.mainList = response.data.records;
this.total = response.data.total;
this.loading = false;
});
this.mainList = response.data.records
this.total = response.data.total
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
this.resetForm('queryForm')
this.handleQuery()
},
/**
* 新建操作集成按钮
*/
handleCommand(command){
if("quick"==command){
handleCommand(command) {
if ('quick' == command) {
this.handleAddQuick()
}else if("add"){
} else if ('add') {
this.handleAdd()
}
},
/**
* 更多操作按钮前置方法
*/
beforeCommand(row,command){
beforeCommand(row, command) {
return {
row:row,
command:command
row: row,
command: command
}
},
/**
* 更多操作集成按钮
*/
handleOpt(dataObj){
switch ( dataObj.command) {
case "del":
this.handleDelete(dataObj.row);
break;
case "newFollow":
handleOpt(dataObj) {
switch (dataObj.command) {
case 'del':
this.handleDelete(dataObj.row)
break
case 'newFollow':
//
this.newFollow(dataObj.row);
break;
case "changeUser":
this.newFollow(dataObj.row)
break
case 'changeUser':
//
break;
case "goSea":
break
case 'goSea':
//
break;
break
default:
break;
break
}
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({path:'/cus/newForm'})
this.$router.push({ path: '/cus/newForm' })
},
/** 快速新增按钮操作 */
handleAddQuick() {
@ -381,42 +453,56 @@ export default {
/** 修改按钮操作 */
handleUpdate(row) {
const id = row.id
this.$router.push({path:'/cus/newForm',query:{id:id}})
this.$router.push({ path: '/cus/newForm', query: { id: id } })
},
/**
* 发邮件
*/
handleSendEmail(row){
handleSendEmail(row) {
},
/**
* 新建跟进
*/
newFollow(row){
this.$nextTick(()=>{
newFollow(row) {
this.$nextTick(() => {
this.cusMain = row
this.chooseCusId = row.id
this.$refs.followForm.handleAdd(row.id,row.fullName+"("+row.cusCode+")")
this.$refs.followForm.handleAdd(row.id, row.fullName + '(' + row.cusCode + ')')
})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id
this.$modal.confirm('是否确认删除编号为"' + row.cusCode + '"的客户?').then(function() {
return delMain(id);
return delMain(id)
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {
})
},
/** 导出按钮操作 */
handleExport() {
this.download('cus/main/export', {
...this.queryParams
}, `main_${new Date().getTime()}.xlsx`)
},
/**
* 设置客户为星标客户
*/
setIfStar(row, ifStar){
setIfStar({id:row.id, ifStar:ifStar}).then((resp) => {
if(resp.code==200){
this.$message.success("操作成功");
this.getList()
}else{
this.$message.error(resp.msg)
}
})
}
}
};
}
</script>
<style scoped>
/* 可点击的客户代码样式 */
@ -429,6 +515,7 @@ export default {
.clickable-code:hover {
color: #40a9ff;
}
/* 核心优化:使用固定容器 + 绝对定位避免布局抖动 */
.tag-cell-container {
display: flex;

View File

@ -95,13 +95,21 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="国家/地区">
<el-select style="width: 100%" v-model="formData.mainInfo.country" filterable placeholder="国家/地区" clearable>
<el-form-item label="国家/地区" label-width="100px">
<el-select style="width: 100%"
filterable
remote
v-model="formData.mainInfo.country"
placeholder="请输入国家/地区"
:remote-method="remoteMethod"
@change="countryChange"
:loading="countryLoading"
clearable>
<el-option
v-for="dict in countryList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
v-for="item in countryList"
:key="item.nameCn"
:label="item.nameCn+'('+item.nameEn+')'"
:value="item.nameCn"
/>
</el-select>
</el-form-item>
@ -533,6 +541,8 @@ function debounce(fn, delay = 100) {
import { getCode, addMain,getMain } from "@/api/cus/main";
//
import { mapGetters } from 'vuex'
import { listCountry } from "@/api/base/country";
import { zoneList } from "@/api/base/zone";
export default {
name: 'CustomerForm',
@ -649,6 +659,7 @@ export default {
},
//
countryList: [],
countryLoading: false,
//
timeZoneList: [],
//
@ -671,6 +682,7 @@ export default {
}
},
mounted() {
this.getZoneList()
if(this.$route.query.id){
this.id = this.$route.query.id;
this.getDataById();
@ -695,6 +707,42 @@ export default {
contentContainer.removeEventListener('scroll', this.debouncedScroll)
},
methods: {
countryChange(row){
// countryListzoneName
const selectedCountry = this.countryList.find(country => country.nameCn === row);
if (selectedCountry) {
this.formData.mainInfo.zoneName = selectedCountry.zoneName;
} else {
this.formData.mainInfo.zoneName = null;
}
},
remoteMethod(query){
if (query !== ''){
this.countryLoading = true;
const queryParams = {
pageNum: 1,
pageSize: 10,
nameCn: query,
}
listCountry(queryParams).then(response => {
this.countryList = response.data.records;
this.total = response.data.total;
this.countryLoading = false;
});
} else {
this.countryList = []
}
},
/**查询时区*/
getZoneList(){
zoneList().then(res => {
this.timeZoneList = res.data
})
},
/**
* 根据id查询客户信息
*/

View File

@ -20,7 +20,7 @@
<i class="el-icon-copy-document dl-canclick" title="复制"></i>
<el-divider direction="vertical"></el-divider>
<div class="country-container">
<img class="country-flag" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHzSURBVHjaYkxOP8IAB//+Mfz7w8Dwi4HhP5CcJb/n/7evb16/APL/gRFQDiAAw3JuAgAIBEDQ/iswEERjGzBQLEru97ll0g0+3HvqMn1SpqlqGsZMsZsIe0SICA5gt5a/AGIEarCPtFh+6N/ffwxA9OvP/7//QYwff/6fZahmePeB4dNHhi+fGb59Y4zyvHHmCEAAAW3YDzQYaJJ93a+vX79aVf58//69fvEPlpIfnz59+vDhw7t37968efP3b/SXL59OnjwIEEAsDP+YgY53b2b89++/awvLn98MDi2cVxl+/vl6mituCtBghi9f/v/48e/XL86krj9XzwEEEENy8g6gu22rfn78+NGs5Ofr16+ZC58+fvyYwX8rxOxXr169fPny+fPn1//93bJlBUAAsQADZMEBxj9/GBxb2P/9+S/R8u3vzxuyaX8ZHv3j8/YGms3w8ycQARmi2eE37t4ACCDGR4/uSkrKAS35B3TT////wADOgLOBIaXIyjBlwxKAAGKRXjCB0SOEaeu+/y9fMnz4AHQxCP348R/o+l+//sMZQBNLEvif3AcIIMZbty7Ly6t9ZmXl+fXj/38GoHH/UcGfP79//BBiYHjy9+8/oUkNAAHEwt1V/vI/KBY/QSISFqM/GBg+MzB8A6PfYC5EFiDAABqgW776MP0rAAAAAElFTkSuQmCC"/>
<img v-if="mainInfo.countryImg != null" :src="getImg(mainInfo.countryImg)" style="width: 30px; height: 20px;"/>
<span class="country-name">{{mainInfo.cusMain.country}}</span>
</div>
<el-divider direction="vertical"></el-divider>
@ -265,6 +265,11 @@ export default {
/**
* 获取客户信息
*/
/** 获取国旗图标 */
getImg(imgPath) {
return require(`@/assets/flags/${imgPath}`);
},
getMainInfo() {
viewData({id:this.id}).then(res => {
if(res.code==200){