From 803dbf2aa5fbe58660af36c978ed35c7e91f902b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=B9=90=E8=A8=80?= Date: Tue, 7 Apr 2026 18:58:11 +0800 Subject: [PATCH] =?UTF-8?q?0407-=E5=8C=97=E4=BB=91=E5=AE=A2=E7=BE=A4+?= =?UTF-8?q?=E5=AE=A2=E7=BE=A4=E4=B8=9A=E7=BB=A9=E7=BB=9F=E8=AE=A1+?= =?UTF-8?q?=E7=BD=91=E6=A0=BC=E6=95=B4=E4=BD=93=E4=B8=9A=E7=BB=A9=E4=BF=AE?= =?UTF-8?q?=E6=94=B9+=E9=9D=92=E7=94=B0=E8=B4=B7=E6=AC=BE=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=BB=8F=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 + .../group/controller/CustGroupController.java | 53 +- .../controller/CustGroupMemberController.java | 2 +- .../GroupPerformanceController.java | 125 ++ .../domain/dto/CustGroupMemberQueryDTO.java | 24 + .../group/domain/dto/CustGroupQueryDTO.java | 6 + .../ruoyi/group/domain/entity/CustGroup.java | 29 +- .../group/domain/entity/CustGroupMember.java | 48 + .../entity/GroupCmpmCountGongsi825.java | 199 +++ .../entity/GroupCmpmCountLingshou825.java | 158 +++ .../entity/GroupCustCountGongsi825.java | 138 +++ .../entity/GroupCustCountLingshou825.java | 111 ++ .../group/domain/vo/CustGroupMemberVO.java | 42 + .../ruoyi/group/domain/vo/CustGroupVO.java | 28 +- .../ruoyi/group/mapper/CustGroupMapper.java | 7 + .../group/mapper/CustGroupMemberMapper.java | 25 + .../group/mapper/GroupPerformanceMapper.java | 22 + .../group/service/ICustGroupService.java | 52 +- .../service/IGroupPerformanceService.java | 19 + .../impl/CustGroupMemberServiceImpl.java | 64 +- .../service/impl/CustGroupServiceImpl.java | 1075 +++++++++-------- .../impl/GroupPerformanceServiceImpl.java | 104 ++ .../mapper/group/CustGroupMapper.xml | 24 +- .../mapper/group/CustGroupMemberMapper.xml | 59 +- .../mapper/group/GroupPerformanceMapper.xml | 245 ++++ .../cmpm/controller/GridCmpmController.java | 9 - .../ruoyi/ibs/cmpm/mapper/GridCmpmMapper.java | 27 +- .../ibs/cmpm/service/GridCmpmService.java | 28 +- .../entity/GridCmpmCountLingshouNew825.java | 4 +- .../entity/GridCustCountLingshouNew825.java | 4 +- .../resources/mapper/cmpm/GridCmpmMapper.xml | 26 +- .../resources/mapper/grid/GridCountMapper.xml | 3 +- ruoyi-ui/src/api/group/custGroup.js | 53 +- ruoyi-ui/src/api/group/performance.js | 69 ++ ruoyi-ui/src/router/index.js | 26 +- .../charts/360charts/commercial/index.vue | 9 +- .../customer/charts/360charts/firm/index.vue | 6 +- .../charts/360charts/indexcharts/index.vue | 8 +- .../src/views/grid/performance/list/list.js | 6 +- .../custGroup/components/create-dialog.vue | 702 ++++++----- ruoyi-ui/src/views/group/custGroup/detail.vue | 84 +- ruoyi-ui/src/views/group/custGroup/index.vue | 87 +- .../views/group/performance/custom/index.vue | 275 +++++ .../views/group/performance/list/index.vue | 294 +++++ .../src/views/group/performance/list/list.js | 194 +++ 45 files changed, 3457 insertions(+), 1119 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 ibs-group/src/main/java/com/ruoyi/group/controller/GroupPerformanceController.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountGongsi825.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountLingshou825.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountGongsi825.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountLingshou825.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/mapper/GroupPerformanceMapper.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/service/IGroupPerformanceService.java create mode 100644 ibs-group/src/main/java/com/ruoyi/group/service/impl/GroupPerformanceServiceImpl.java create mode 100644 ibs-group/src/main/resources/mapper/group/GroupPerformanceMapper.xml create mode 100644 ruoyi-ui/src/api/group/performance.js create mode 100644 ruoyi-ui/src/views/group/performance/custom/index.vue create mode 100644 ruoyi-ui/src/views/group/performance/list/index.vue create mode 100644 ruoyi-ui/src/views/group/performance/list/list.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5480842 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "kiroAgent.configureMCP": "Disabled" +} \ No newline at end of file diff --git a/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupController.java b/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupController.java index 0179481..4195e03 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupController.java +++ b/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupController.java @@ -9,7 +9,6 @@ import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.group.domain.dto.CustGroupMemberTemplate; import com.ruoyi.group.domain.dto.CustGroupQueryDTO; -import com.ruoyi.group.domain.dto.GridImportDTO; import com.ruoyi.group.domain.entity.CustGroup; import com.ruoyi.group.domain.vo.CustGroupVO; import com.ruoyi.group.service.ICustGroupService; @@ -60,48 +59,31 @@ public class CustGroupController extends BaseController { return AjaxResult.success(custGroup); } - /** - * 异步创建客群(网格导入) - */ - @ApiOperation("异步创建客群(网格导入)") - @Log(title = "客群管理-网格导入创建客群", businessType = BusinessType.INSERT) - @PostMapping("/createByGrid") - public AjaxResult createCustGroupByGrid(@RequestBody @Valid GridImportDTO gridImportDTO) { - String id = custGroupService.createCustGroupByGrid(gridImportDTO); - return AjaxResult.success(id); - } - /** * 异步创建客群(模板导入) + * gridType、regionGridIds、drawGridIds 包含在 dto 中 */ @ApiOperation("异步创建客群(模板导入)") @Log(title = "客群管理-异步创建客群", businessType = BusinessType.INSERT) @PostMapping(value = "/createByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public AjaxResult createCustGroupByTemplate(@RequestPart("dto") @Valid String dtoJson, - @RequestPart("file") MultipartFile file) { + public AjaxResult createCustGroupByTemplate( + @RequestPart("dto") @Valid String dtoJson, + @RequestPart("file") MultipartFile file) { CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class); - return AjaxResult.success(custGroupService.createCustGroupByTemplate(custGroup, file)); - } - - /** - * 更新客群(网格导入) - */ - @ApiOperation("更新客群(网格导入)") - @Log(title = "客群管理-更新客群(网格导入)", businessType = BusinessType.UPDATE) - @PostMapping("/updateByGrid") - public AjaxResult updateCustGroupByGrid(@RequestBody @Valid GridImportDTO gridImportDTO) { - String result = custGroupService.updateCustGroupByGrid(gridImportDTO); - return AjaxResult.success(result); + return AjaxResult.success("操作成功", custGroupService.createCustGroupByTemplate(custGroup, file)); } /** * 更新客群(模板导入) + * gridType、regionGridIds、drawGridIds 包含在 dto 中 + * file 参数可选:不传文件则只更新客群信息,传文件则追加客户 */ @ApiOperation("更新客群(模板导入)") @Log(title = "客群管理-更新客群(模板导入)", businessType = BusinessType.UPDATE) @PostMapping(value = "/updateByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public AjaxResult updateCustGroupByTemplate(@RequestPart("dto") @Valid String dtoJson, - @RequestPart("file") MultipartFile file) { + public AjaxResult updateCustGroupByTemplate( + @RequestPart("dto") @Valid String dtoJson, + @RequestPart(value = "file", required = false) MultipartFile file) { CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class); return AjaxResult.success(custGroupService.updateCustGroupByTemplate(custGroup, file)); } @@ -114,7 +96,7 @@ public class CustGroupController extends BaseController { @GetMapping("/createStatus/{id}") public AjaxResult getCreateStatus(@PathVariable Long id) { String status = custGroupService.getCreateStatus(id); - return AjaxResult.success(status); + return AjaxResult.success("操作成功", status); } /** @@ -139,4 +121,15 @@ public class CustGroupController extends BaseController { return AjaxResult.success(result); } -} \ No newline at end of file + /** + * 获取所有已有的客群标签列表 + */ + @ApiOperation("获取所有客群标签") + @Log(title = "客群管理-获取客群标签") + @GetMapping("/tags") + public AjaxResult getAllGroupTags() { + List tags = custGroupService.getAllGroupTags(); + return AjaxResult.success(tags); + } + +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupMemberController.java b/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupMemberController.java index a27ce83..9883286 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupMemberController.java +++ b/ibs-group/src/main/java/com/ruoyi/group/controller/CustGroupMemberController.java @@ -36,7 +36,7 @@ public class CustGroupMemberController extends BaseController { @GetMapping("/list/{groupId}") public TableDataInfo listCustGroupMembers(@PathVariable Long groupId, CustGroupMemberQueryDTO dto) { - startPage(); + // 注意:startPage()在Service内部调用,因为权限检查会先执行SQL导致分页失效 List list = custGroupMemberService.listCustGroupMembers(groupId, dto); return getDataTable(list); } diff --git a/ibs-group/src/main/java/com/ruoyi/group/controller/GroupPerformanceController.java b/ibs-group/src/main/java/com/ruoyi/group/controller/GroupPerformanceController.java new file mode 100644 index 0000000..53eb99b --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/controller/GroupPerformanceController.java @@ -0,0 +1,125 @@ +package com.ruoyi.group.controller; + +import com.github.pagehelper.Page; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.TableDataPageInfo; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825; +import com.ruoyi.group.domain.entity.GroupCustCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCustCountLingshou825; +import com.ruoyi.group.service.IGroupPerformanceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +@Api(tags = "客群业绩统计") +@RestController +@RequestMapping("/group/performance") +public class GroupPerformanceController extends BaseController { + + @Resource + private IGroupPerformanceService groupPerformanceService; + + @ApiOperation("查询零售客群业绩汇总列表") + @GetMapping("/ls/list") + public TableDataPageInfo selectLsCountList(String dt, String groupName) { + Page page = startPage(); + List list = groupPerformanceService.selectLsCountList(dt, groupName); + return getDataTable(list, page); + } + + @ApiOperation("查询公司客群业绩汇总列表") + @GetMapping("/gs/list") + public TableDataPageInfo selectGsCountList(String dt, String groupName) { + Page page = startPage(); + List list = groupPerformanceService.selectGsCountList(dt, groupName); + return getDataTable(list, page); + } + + @ApiOperation("查询零售客群客户明细") + @GetMapping("/ls/custList") + public TableDataPageInfo selectLsCustList(@RequestParam String groupId, + String custName, + String custIdc, + String dt) { + Page page = startPage(); + List list = groupPerformanceService.selectLsCustList(groupId, custName, custIdc, dt); + return getDataTable(list, page); + } + + @ApiOperation("查询公司客群客户明细") + @GetMapping("/gs/custList") + public TableDataPageInfo selectGsCustList(@RequestParam String groupId, + String custName, + String socialCreditCode, + String dt) { + Page page = startPage(); + List list = groupPerformanceService.selectGsCustList(groupId, custName, socialCreditCode, dt); + return getDataTable(list, page); + } + + @Log(title = "导出零售客群业绩汇总") + @ApiOperation(value = "导出零售客群业绩汇总", produces = "application/octet-stream") + @GetMapping("/exportLs") + public void exportLs(HttpServletResponse response, String dt, String groupName) { + try { + ExcelUtil util = new ExcelUtil<>(GroupCmpmCountLingshou825.class); + util.exportExcel(response, groupPerformanceService.selectLsCountList(dt, groupName), "零售客群业绩汇总"); + } catch (Exception e) { + logger.error("导出零售客群业绩汇总失败", e); + } + } + + @Log(title = "导出公司客群业绩汇总") + @ApiOperation(value = "导出公司客群业绩汇总", produces = "application/octet-stream") + @GetMapping("/exportGs") + public void exportGs(HttpServletResponse response, String dt, String groupName) { + try { + ExcelUtil util = new ExcelUtil<>(GroupCmpmCountGongsi825.class); + util.exportExcel(response, groupPerformanceService.selectGsCountList(dt, groupName), "公司客群业绩汇总"); + } catch (Exception e) { + logger.error("导出公司客群业绩汇总失败", e); + } + } + + @Log(title = "导出零售客群客户明细") + @ApiOperation(value = "导出零售客群客户明细", produces = "application/octet-stream") + @GetMapping("/exportLsCust") + public void exportLsCust(HttpServletResponse response, + @RequestParam String groupId, + String custName, + String custIdc, + String dt) { + try { + ExcelUtil util = new ExcelUtil<>(GroupCustCountLingshou825.class); + util.exportExcel(response, groupPerformanceService.selectLsCustList(groupId, custName, custIdc, dt), "零售客群客户明细"); + } catch (Exception e) { + logger.error("导出零售客群客户明细失败", e); + } + } + + @Log(title = "导出公司客群客户明细") + @ApiOperation(value = "导出公司客群客户明细", produces = "application/octet-stream") + @GetMapping("/exportGsCust") + public void exportGsCust(HttpServletResponse response, + @RequestParam String groupId, + String custName, + String socialCreditCode, + String dt) { + try { + ExcelUtil util = new ExcelUtil<>(GroupCustCountGongsi825.class); + util.exportExcel(response, groupPerformanceService.selectGsCustList(groupId, custName, socialCreditCode, dt), "公司客群客户明细"); + } catch (Exception e) { + logger.error("导出公司客群客户明细失败", e); + } + } +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupMemberQueryDTO.java b/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupMemberQueryDTO.java index 703fc97..d45ecff 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupMemberQueryDTO.java +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupMemberQueryDTO.java @@ -13,6 +13,18 @@ import lombok.Data; @ApiModel(description = "客群客户查询条件") public class CustGroupMemberQueryDTO { + /** + * 页码 + */ + @ApiModelProperty(value = "页码", hidden = true) + private Integer pageNum; + + /** + * 每页数量 + */ + @ApiModelProperty(value = "每页数量", hidden = true) + private Integer pageSize; + /** * 客户类型:0=个人, 1=商户, 2=企业 */ @@ -24,4 +36,16 @@ public class CustGroupMemberQueryDTO { */ @ApiModelProperty(value = "客户姓名") private String custName; + + /** + * 客户经理柜员号 + */ + @ApiModelProperty(value = "客户经理柜员号") + private String userName; + + /** + * 客户经理姓名 + */ + @ApiModelProperty(value = "客户经理姓名") + private String nickName; } diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupQueryDTO.java b/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupQueryDTO.java index 8cda0c9..af398f1 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupQueryDTO.java +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/dto/CustGroupQueryDTO.java @@ -39,6 +39,12 @@ public class CustGroupQueryDTO implements Serializable { @ApiModelProperty(value = "客群状态", name = "groupStatus") private String groupStatus; + /** + * 客群标签(模糊匹配) + */ + @ApiModelProperty(value = "客群标签", name = "groupTags") + private String groupTags; + /** * 视图类型:mine=我创建的,sharedToMe=下发给我的 */ diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroup.java b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroup.java index 106c839..4e79535 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroup.java +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroup.java @@ -38,10 +38,10 @@ public class CustGroup { private String groupMode; /** - * 创建方式:1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格 + * 客群类型:0=零售类(个人+商户), 1=公司类(企业) */ - @ApiModelProperty(value = "创建方式:1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格", name = "createMode") - private String createMode; + @ApiModelProperty(value = "客群类型:0=零售类(个人+商户), 1=公司类(企业)", name = "groupType") + private String groupType; /** * 柜员号 @@ -123,23 +123,18 @@ public class CustGroup { private String remark; /** - * 网格类型:0=绩效网格, 1=地理网格, 2=绘制网格(动态客群使用) + * 客群标签(多个标签用逗号分隔) */ - @ApiModelProperty(value = "网格类型", name = "gridType") + @ApiModelProperty(value = "客群标签(多个标签用逗号分隔)", name = "groupTags") + @TableField("group_tags") + private String groupTags; + + /** + * 网格类型:0=绩效网格, 1=地理网格, 2=绘制网格(管户关系来源) + */ + @ApiModelProperty(value = "网格类型(管户关系来源):0=绩效网格, 1=地理网格, 2=绘制网格", name = "gridType") private String gridType; - /** - * 绩效业务类型:retail=零售, corporate=公司(动态客群使用) - */ - @ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType") - private String cmpmBizType; - - /** - * 客户经理列表(逗号分隔,动态客群使用) - */ - @ApiModelProperty(value = "客户经理列表", name = "gridUserNames") - private String gridUserNames; - /** * 地理网格ID列表(逗号分隔,动态客群使用) */ diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroupMember.java b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroupMember.java index ef0da18..36cce20 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroupMember.java +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/CustGroupMember.java @@ -60,6 +60,48 @@ public class CustGroupMember { @ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode") private String socialCreditCode; + /** + * 客户经理柜员号 + */ + @ApiModelProperty(value = "客户经理柜员号", name = "userName") + @TableField("user_name") + private String userName; + + /** + * 客户经理姓名 + */ + @ApiModelProperty(value = "客户经理姓名", name = "nickName") + @TableField("nick_name") + private String nickName; + + /** + * 网点ID + */ + @ApiModelProperty(value = "网点ID", name = "outletId") + @TableField("outlet_id") + private Long outletId; + + /** + * 网点名称 + */ + @ApiModelProperty(value = "网点名称", name = "outletName") + @TableField("outlet_name") + private String outletName; + + /** + * 支行ID + */ + @ApiModelProperty(value = "支行ID", name = "branchId") + @TableField("branch_id") + private Long branchId; + + /** + * 支行名称 + */ + @ApiModelProperty(value = "支行名称", name = "branchName") + @TableField("branch_name") + private String branchName; + /** * 创建者 */ @@ -71,6 +113,12 @@ public class CustGroupMember { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + /** * 删除标识:0=正常, 1=删除 */ diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountGongsi825.java b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountGongsi825.java new file mode 100644 index 0000000..756b4a9 --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountGongsi825.java @@ -0,0 +1,199 @@ +package com.ruoyi.group.domain.entity; + +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.io.Serializable; + +/** + * 客群业绩汇总统计_公司825 + */ +@Data +public class GroupCmpmCountGongsi825 implements Serializable { + private static final long serialVersionUID = 1L; + + @Excel(name = "统计日期") + private String dt; + + private String groupId; + + @Excel(name = "客群名称") + private String groupName; + + @Excel(name = "客群模式") + private String groupMode; + + @Excel(name = "归属支行") + private String deptId; + + @Excel(name = "归属支行名称") + private String deptName; + + @Excel(name = "归属网点") + private String outletsId; + + @Excel(name = "归属网点名称") + private String outletsName; + + @Excel(name = "归属客户经理") + private String userName; + + @Excel(name = "入格客户数") + private Integer custNum; + + @Excel(name = "活期存款余额") + private String hqCurBalance; + + @Excel(name = "保证金存款余额") + private String bzCurBalance; + + @Excel(name = "贷款余额") + private String loanBalanceCny; + + @Excel(name = "贴现余额") + private String financeProd711Balance; + + @Excel(name = "承兑汇票余额") + private String financeProd716Balance; + + @Excel(name = "贷款年日均") + private String loanYearDailyaverage; + + @Excel(name = "签发承兑汇票率") + private String qfcdRat; + + @Excel(name = "贴现业务率") + private String txRat; + + @Excel(name = "保函业务率") + private String bhRat; + + @Excel(name = "有效代发工资率") + private String yxdfgzRat; + + @Excel(name = "代扣电费率") + private String dkdfRat; + + @Excel(name = "代扣水费率") + private String dksfRat; + + @Excel(name = "代扣税费率") + private String dkshfRat; + + @Excel(name = "票据宝签约率") + private String pjbRat; + + @Excel(name = "财资宝签约率") + private String czbRat; + + @Excel(name = "收付宝签约率") + private String sfbRat; + + @Excel(name = "贸融宝签约率") + private String mrbRat; + + @Excel(name = "数字生态产品签约率") + private String szstRat; + + @Excel(name = "开户率") + private String khRat; + + @Excel(name = "国际结算业务率") + private String gjjsywRat; + + @Excel(name = "远期结算汇业务率") + private String yqjshRat; + + @Excel(name = "签发承兑汇票数") + private Integer qfcdNum; + + @Excel(name = "贴现业务数") + private Integer txNum; + + @Excel(name = "保函业务数") + private Integer bhNum; + + @Excel(name = "有效代发工资数") + private Integer yxdfgzNum; + + @Excel(name = "月均代发工资笔数") + private String ustrCountPerM; + + @Excel(name = "月均代发工资金额(元)") + private String ustrBalM; + + @Excel(name = "代扣电费数") + private Integer dkdfNum; + + @Excel(name = "代扣水费数") + private Integer dksfNum; + + @Excel(name = "代扣税费数") + private Integer dkshfNum; + + @Excel(name = "票据宝签约数") + private Integer pjbNum; + + @Excel(name = "财资宝签约数") + private Integer czbNum; + + @Excel(name = "收付宝签约数") + private Integer sfbNum; + + @Excel(name = "贸融宝签约数") + private Integer mrbNum; + + @Excel(name = "数字生态产品签约数") + private Integer szstNum; + + @Excel(name = "开户数") + private Integer khNum; + + @Excel(name = "国际结算业务数") + private Integer gjjsywNum; + + @Excel(name = "远期结算汇业务数") + private Integer yqjshNum; + + private String regionCode; + + private String opsDept; + + @Excel(name = "合同签约率") + private String htqyRat; + + @Excel(name = "合同签约数") + private Integer htqyNum; + + private String deptType; + + @Excel(name = "近365天已走访人数") + private String zf365cnt; + + @Excel(name = "近180天已走访人数") + private String zf180cnt; + + @Excel(name = "近90天已走访人数") + private String zf90cnt; + + @Excel(name = "近30天已走访人数") + private String zf30cnt; + + @Excel(name = "近365天走访率") + private String zf365rt; + + @Excel(name = "近180天走访率") + private String zf180rt; + + @Excel(name = "近90天走访率") + private String zf90rt; + + @Excel(name = "近30天走访率") + private String zf30rt; + + @Excel(name = "建档率") + private String phRat; + + @Excel(name = "建档数") + private String phNum; +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountLingshou825.java b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountLingshou825.java new file mode 100644 index 0000000..8189451 --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCmpmCountLingshou825.java @@ -0,0 +1,158 @@ +package com.ruoyi.group.domain.entity; + +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.io.Serializable; + +/** + * 客群业绩汇总统计_零售825 + */ +@Data +public class GroupCmpmCountLingshou825 implements Serializable { + private static final long serialVersionUID = 1L; + + @Excel(name = "统计日期") + private String dt; + + private String groupId; + + @Excel(name = "客群名称") + private String groupName; + + @Excel(name = "客群模式") + private String groupMode; + + @Excel(name = "归属支行机构号") + private String deptId; + + @Excel(name = "归属支行名称") + private String deptName; + + @Excel(name = "归属网点机构号") + private String outletsId; + + @Excel(name = "归属网点名称") + private String outletsName; + + @Excel(name = "归属客户经理") + private String userName; + + @Excel(name = "入格客户数") + private Integer custNum; + + @Excel(name = "近365天已走访人数") + private String zf365cnt; + + @Excel(name = "近180天已走访人数") + private String zf180cnt; + + @Excel(name = "近90天已走访人数") + private String zf90cnt; + + @Excel(name = "近30天已走访人数") + private String zf30cnt; + + @Excel(name = "近365天走访率") + private String zf365rt; + + @Excel(name = "近180天走访率") + private String zf180rt; + + @Excel(name = "近90天走访率") + private String zf90rt; + + @Excel(name = "近30天走访率") + private String zf30rt; + + @Excel(name = "活期存款余额(元)") + private String curBalD; + + @Excel(name = "授信率(%)") + private String sxRat; + + @Excel(name = "用信覆盖率") + private String yxRat; + + @Excel(name = "授信户数") + private Integer sxNum; + + @Excel(name = "用信户数") + private Integer yxNum; + + @Excel(name = "授信金额(合同)") + private String sxBal; + + @Excel(name = "贷款余额(元)") + private String balLoan; + + @Excel(name = "贷款年日均(元)") + private String loanAve; + + @Excel(name = "合同签约率(%)") + private String yxhtRat; + + @Excel(name = "代扣电费覆盖率(%)") + private String dianRat; + + @Excel(name = "代扣水费率(%)") + private String shuiRat; + + @Excel(name = "代扣税费率(%)") + private String taxRat; + + @Excel(name = "开户率(%)") + private String openRat; + + @Excel(name = "合同签约客户数") + private Integer yxhtNum; + + @Excel(name = "代扣电费客户数") + private Integer dianNum; + + @Excel(name = "代扣水费客户数") + private Integer shuiNum; + + @Excel(name = "代扣税费数") + private Integer taxNum; + + @Excel(name = "开户数") + private Integer openNum; + + @Excel(name = "存款余额(元)") + private String depBal; + + @Excel(name = "财富余额(元)") + private String finBal; + + @Excel(name = "个人核心客户数") + private Integer grhxNum; + + @Excel(name = "财富有效客户数") + private Integer cfyxNum; + + @Excel(name = "有效信用卡数") + private Integer yxxykNum; + + @Excel(name = "有效社保卡户数") + private Integer yxsbkNum; + + @Excel(name = "二换三社保卡户数") + private Integer twoTo3SbkNum; + + @Excel(name = "养老金迁移至社保卡户数") + private Integer yljToSbkNum; + + @Excel(name = "丰收互联客户数") + private Integer fshlNum; + + @Excel(name = "有效收单商户") + private Integer yxsdNum; + + @Excel(name = "核心收单户数") + private String hxsdNum; + + private String regionCode; + + private String opsDept; +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountGongsi825.java b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountGongsi825.java new file mode 100644 index 0000000..3343510 --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountGongsi825.java @@ -0,0 +1,138 @@ +package com.ruoyi.group.domain.entity; + +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.io.Serializable; + +/** + * 客群客户明细统计_公司825 + */ +@Data +public class GroupCustCountGongsi825 implements Serializable { + private static final long serialVersionUID = 1L; + + private String groupId; + + @Excel(name = "客户名称") + private String custName; + + @Excel(name = "客户证件号") + private String socialCreditCode; + + @Excel(name = "客户内码") + private String custIsn; + + @Excel(name = "活期存款余额") + private String hqCurBalance; + + @Excel(name = "保证金存款余额") + private String bzCurBalance; + + @Excel(name = "是否授信") + private String isCredit; + + @Excel(name = "贷款余额") + private String loanBalanceCny; + + @Excel(name = "贷款年日均") + private String loanYearDailyaverage; + + @Excel(name = "是否有签发承兑汇票") + private String financeProd716OpenFlag; + + @Excel(name = "承兑汇票余额") + private String financeProd716Balance; + + @Excel(name = "是否有贴现业务") + private String financeProd711OpenFlag; + + @Excel(name = "贴现金额") + private String financeProd711Balance; + + @Excel(name = "是否有保函业务") + private String intlBussinessJcbhOpenFlag; + + @Excel(name = "是否为有效代发工资客户") + private String isUstr; + + @Excel(name = "月均代发工资笔数") + private String ustrCountPerM; + + @Excel(name = "月均代发工资金额(元)") + private String ustrBalM; + + @Excel(name = "是否代扣电费") + private String elecchargeSignFlag; + + @Excel(name = "是否代扣水费") + private String waterchargeSignFlag; + + @Excel(name = "是否代扣税费") + private String taxdeductionSignFlag; + + @Excel(name = "是否票据宝签约") + private String pjb; + + @Excel(name = "是否财资宝签约") + private String czb; + + @Excel(name = "是否收付宝签约") + private String sfb; + + @Excel(name = "是否贸融宝签约") + private String mrb; + + @Excel(name = "是否数字生态产品签约") + private String szst; + + @Excel(name = "是否开户") + private String isOpenSts; + + @Excel(name = "是否国际结算业务") + private String intlBussinessOpenFlag; + + @Excel(name = "是否有远期结算汇业务") + private String intlBussiness325OpenFlag; + + private String regionCode; + + private String opsDept; + + private String custType; + + @Excel(name = "是否合同签约") + private String isHtqy; + + @Excel(name = "归属支行名称") + private String deptName; + + @Excel(name = "归属网点id") + private String outletsId; + + @Excel(name = "归属网点名称") + private String outletsName; + + @Excel(name = "归属客户经理") + private String userName; + + @Excel(name = "归属支行id") + private String deptId; + + @Excel(name = "近365天有无走访") + private String is365zf; + + @Excel(name = "近180天有无走访") + private String is180zf; + + @Excel(name = "近90天有无走访") + private String is90zf; + + @Excel(name = "近30天有无走访") + private String is30zf; + + private String dt; + + @Excel(name = "是否建档") + private String isPh; +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountLingshou825.java b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountLingshou825.java new file mode 100644 index 0000000..0577641 --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/entity/GroupCustCountLingshou825.java @@ -0,0 +1,111 @@ +package com.ruoyi.group.domain.entity; + +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.io.Serializable; + +/** + * 客群客户明细统计_零售825 + */ +@Data +public class GroupCustCountLingshou825 implements Serializable { + private static final long serialVersionUID = 1L; + + private String groupId; + + @Excel(name = "客户名称") + private String custName; + + @Excel(name = "客户证件号") + private String custIdc; + + @Excel(name = "客户内码") + private String custIsn; + + @Excel(name = "活期存款余额") + private String curBalD; + + @Excel(name = "贷款余额") + private String balLoan; + + @Excel(name = "贷款年日均") + private String loanAve; + + @Excel(name = "是否授信") + private String isSx; + + @Excel(name = "是否用信") + private String isYx; + + @Excel(name = "授信金额") + private String sxBal; + + @Excel(name = "是否合同签约") + private String isYxht; + + @Excel(name = "是否持有信用卡") + private String isXyk; + + @Excel(name = "是否开通丰收互联") + private String fshl; + + @Excel(name = "是否办理收单") + private String isSd; + + @Excel(name = "是否代扣电费") + private String dian; + + @Excel(name = "是否代扣水费") + private String shui; + + @Excel(name = "是否代扣税费") + private String tax; + + @Excel(name = "开户数") + private String openNum; + + @Excel(name = "存款余额") + private String depBal; + + @Excel(name = "财富余额") + private String finBal; + + @Excel(name = "是否个人核心客户") + private String isGrhx; + + @Excel(name = "是否财富有效客户") + private String isCfyx; + + @Excel(name = "是否有效社保卡客户") + private String isYxsbk; + + @Excel(name = "是否二换三社保卡") + private String is2to3Sbk; + + @Excel(name = "是否养老金迁移至社保卡") + private String isYljToSbk; + + private String regionCode; + + private String opsDept; + + private String custType; + + @Excel(name = "近365天有无走访") + private String is365zf; + + @Excel(name = "近180天有无走访") + private String is180zf; + + @Excel(name = "近90天有无走访") + private String is90zf; + + @Excel(name = "近30天有无走访") + private String is30zf; + + private String dt; + + @Excel(name = "是否核心收单") + private String isHxsd; +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupMemberVO.java b/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupMemberVO.java index 581ac38..5100921 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupMemberVO.java +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupMemberVO.java @@ -57,10 +57,52 @@ public class CustGroupMemberVO { @ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode") private String socialCreditCode; + /** + * 客户经理柜员号 + */ + @ApiModelProperty(value = "客户经理柜员号", name = "userName") + private String userName; + + /** + * 客户经理姓名 + */ + @ApiModelProperty(value = "客户经理姓名", name = "nickName") + private String nickName; + + /** + * 网点ID + */ + @ApiModelProperty(value = "网点ID", name = "outletId") + private Long outletId; + + /** + * 网点名称 + */ + @ApiModelProperty(value = "网点名称", name = "outletName") + private String outletName; + + /** + * 支行ID + */ + @ApiModelProperty(value = "支行ID", name = "branchId") + private Long branchId; + + /** + * 支行名称 + */ + @ApiModelProperty(value = "支行名称", name = "branchName") + private String branchName; + /** * 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty(value = "创建时间", name = "createTime") private Date createTime; + + /** + * 存量状态:true=存量客户(系统存在),false=非存量客户(系统不存在) + */ + @ApiModelProperty(value = "存量状态:true=存量客户,false=非存量客户", name = "isExisting") + private Boolean isExisting; } \ No newline at end of file diff --git a/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupVO.java b/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupVO.java index e87d1a6..dafb783 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupVO.java +++ b/ibs-group/src/main/java/com/ruoyi/group/domain/vo/CustGroupVO.java @@ -35,10 +35,10 @@ public class CustGroupVO { private String groupMode; /** - * 创建方式:1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格 + * 客群类型:0=零售类(个人+商户), 1=公司类(企业) */ - @ApiModelProperty(value = "创建方式:1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格", name = "createMode") - private String createMode; + @ApiModelProperty(value = "客群类型:0=零售类(个人+商户), 1=公司类(企业)", name = "groupType") + private String groupType; /** * 柜员号 @@ -114,6 +114,12 @@ public class CustGroupVO { @ApiModelProperty(value = "备注", name = "remark") private String remark; + /** + * 客群标签(多个标签用逗号分隔) + */ + @ApiModelProperty(value = "客群标签(多个标签用逗号分隔)", name = "groupTags") + private String groupTags; + /** * 有效期截止时间 */ @@ -128,23 +134,11 @@ public class CustGroupVO { private String createStatus; /** - * 网格类型:0=绩效网格, 1=地理网格, 2=绘制网格 + * 网格类型(管户关系来源):0=绩效网格, 1=地理网格, 2=绘制网格 */ - @ApiModelProperty(value = "网格类型", name = "gridType") + @ApiModelProperty(value = "网格类型(管户关系来源):0=绩效网格, 1=地理网格, 2=绘制网格", name = "gridType") private String gridType; - /** - * 绩效业务类型:retail=零售, corporate=公司 - */ - @ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType") - private String cmpmBizType; - - /** - * 客户经理列表(逗号分隔) - */ - @ApiModelProperty(value = "客户经理列表", name = "gridUserNames") - private String gridUserNames; - /** * 地理网格ID列表(逗号分隔) */ diff --git a/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMapper.java b/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMapper.java index 5023f9c..d1b9495 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMapper.java +++ b/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMapper.java @@ -57,4 +57,11 @@ public interface CustGroupMapper extends BaseMapper { Long countVisibleCustGroup(@Param("id") Long id, @Param("userName") String userName, @Param("deptId") String deptId); + + /** + * 查询所有已有的客群标签 + * + * @return 标签列表 + */ + List selectAllGroupTags(); } diff --git a/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMemberMapper.java b/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMemberMapper.java index 5e35445..c1813d1 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMemberMapper.java +++ b/ibs-group/src/main/java/com/ruoyi/group/mapper/CustGroupMemberMapper.java @@ -33,4 +33,29 @@ public interface CustGroupMemberMapper extends BaseMapper { * @param memberList 客户列表 */ void batchInsertMembers(@Param("list") List memberList); + + /** + * 批量查询个人客户是否存在(根据客户号+身份证号) + * + * @param custIds 客户号列表 + * @return 存在的客户号列表 + */ + List selectExistingRetailCustIds(@Param("list") List custIds); + + /** + * 批量查询商户客户是否存在(根据客户号+统信码) + * + * @param custIds 客户号列表 + * @return 存在的客户号列表 + */ + List selectExistingMerchantCustIds(@Param("list") List custIds); + + /** + * 批量查询企业客户是否存在(根据客户号+统信码) + * + * @param custIds 客户号列表 + * @return 存在的客户号列表 + */ + List selectExistingBusinessCustIds(@Param("list") List custIds); + } \ No newline at end of file diff --git a/ibs-group/src/main/java/com/ruoyi/group/mapper/GroupPerformanceMapper.java b/ibs-group/src/main/java/com/ruoyi/group/mapper/GroupPerformanceMapper.java new file mode 100644 index 0000000..92250af --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/mapper/GroupPerformanceMapper.java @@ -0,0 +1,22 @@ +package com.ruoyi.group.mapper; + +import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825; +import com.ruoyi.group.domain.entity.GroupCustCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCustCountLingshou825; +import org.apache.ibatis.annotations.Mapper; + +import java.util.HashMap; +import java.util.List; + +@Mapper +public interface GroupPerformanceMapper { + + List selectLsCountList(HashMap paramMap); + + List selectGsCountList(HashMap paramMap); + + List selectLsCustList(HashMap paramMap); + + List selectGsCustList(HashMap paramMap); +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/service/ICustGroupService.java b/ibs-group/src/main/java/com/ruoyi/group/service/ICustGroupService.java index b0cb593..6766f85 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/service/ICustGroupService.java +++ b/ibs-group/src/main/java/com/ruoyi/group/service/ICustGroupService.java @@ -1,7 +1,6 @@ package com.ruoyi.group.service; import com.ruoyi.group.domain.dto.CustGroupQueryDTO; -import com.ruoyi.group.domain.dto.GridImportDTO; import com.ruoyi.group.domain.entity.CustGroup; import com.ruoyi.group.domain.vo.CustGroupVO; import org.springframework.web.multipart.MultipartFile; @@ -17,88 +16,56 @@ public interface ICustGroupService { /** * 查询客群列表 - * - * @param dto 查询条件 - * @return 客群VO列表 */ List listCustGroup(CustGroupQueryDTO dto); /** * 根据ID查询客群详情 - * - * @param id 客群ID - * @return 客群VO */ CustGroupVO getCustGroup(Long id); /** * 校验当前用户是否有客群查看权限 - * - * @param id 客群ID */ void checkCustGroupViewPermission(Long id); /** * 异步创建客群(模板导入) + * gridType、regionGridIds、drawGridIds 从 custGroup 中获取 * - * @param custGroup 客群实体 - * @param file Excel文件 + * @param custGroup 客群实体(包含 gridType、regionGridIds、drawGridIds) + * @param file Excel文件 * @return 客群ID */ String createCustGroupByTemplate(CustGroup custGroup, MultipartFile file); - /** - * 异步创建客群(网格导入) - * - * @param gridImportDTO 网格导入条件 - * @return 客群ID - */ - String createCustGroupByGrid(GridImportDTO gridImportDTO); - - /** - * 更新客群(网格导入) - * - * @param gridImportDTO 网格导入条件 - * @return 结果消息 - */ - String updateCustGroupByGrid(GridImportDTO gridImportDTO); - /** * 更新客群(模板导入) + * gridType、regionGridIds、drawGridIds 从 custGroup 中获取 * - * @param custGroup 客群实体 - * @param file Excel文件 + * @param custGroup 客群实体(包含 gridType、regionGridIds、drawGridIds) + * @param file Excel文件 * @return 结果消息 */ String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file); /** * 删除客群 - * - * @param idList 客群ID列表 - * @return 结果消息 */ String deleteCustGroup(List idList); /** * 检查客群名称是否存在 - * - * @param groupName 客群名称 - * @return true=存在, false=不存在 */ boolean checkGroupNameExist(String groupName); /** * 查询客群创建状态 - * - * @param id 客群ID - * @return 创建状态:0=创建中, 1=创建成功, 2=创建失败 */ String getCreateStatus(Long id); /** * 更新动态客群(定时任务调用) - * 根据原始导入条件重新查询并更新客户列表 */ void updateDynamicCustGroups(); @@ -107,4 +74,11 @@ public interface ICustGroupService { */ void checkAndDisableExpiredGroups(); + /** + * 获取所有已有的客群标签列表 + * + * @return 标签列表 + */ + List getAllGroupTags(); + } diff --git a/ibs-group/src/main/java/com/ruoyi/group/service/IGroupPerformanceService.java b/ibs-group/src/main/java/com/ruoyi/group/service/IGroupPerformanceService.java new file mode 100644 index 0000000..32a3ad0 --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/service/IGroupPerformanceService.java @@ -0,0 +1,19 @@ +package com.ruoyi.group.service; + +import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825; +import com.ruoyi.group.domain.entity.GroupCustCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCustCountLingshou825; + +import java.util.List; + +public interface IGroupPerformanceService { + + List selectLsCountList(String dt, String groupName); + + List selectGsCountList(String dt, String groupName); + + List selectLsCustList(String groupId, String custName, String custIdc, String dt); + + List selectGsCustList(String groupId, String custName, String socialCreditCode, String dt); +} diff --git a/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupMemberServiceImpl.java b/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupMemberServiceImpl.java index 0f52653..8385da3 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupMemberServiceImpl.java +++ b/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupMemberServiceImpl.java @@ -10,11 +10,16 @@ import com.ruoyi.group.mapper.CustGroupMapper; import com.ruoyi.group.mapper.CustGroupMemberMapper; import com.ruoyi.group.service.ICustGroupService; import com.ruoyi.group.service.ICustGroupMemberService; +import com.github.pagehelper.PageHelper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * 客群客户服务实现类 @@ -36,7 +41,64 @@ public class CustGroupMemberServiceImpl implements ICustGroupMemberService { @Override public List listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto) { custGroupService.checkCustGroupViewPermission(groupId); - return custGroupMemberMapper.selectCustGroupMemberList(groupId, dto); + // 在权限检查之后启动分页,避免权限检查SQL消耗分页设置 + int pageNum = dto.getPageNum() != null ? dto.getPageNum() : 1; + int pageSize = dto.getPageSize() != null ? dto.getPageSize() : 10; + PageHelper.startPage(pageNum, pageSize); + // 1. 查询客群成员列表(分页) + List memberList = custGroupMemberMapper.selectCustGroupMemberList(groupId, dto); + if (memberList == null || memberList.isEmpty()) { + return memberList; + } + + // 2. 按客户类型分组,批量查询存量状态 + // 个人客户 + List retailCustIds = memberList.stream() + .filter(m -> "0".equals(m.getCustType())) + .map(CustGroupMemberVO::getCustId) + .collect(Collectors.toList()); + // 商户客户 + List merchantCustIds = memberList.stream() + .filter(m -> "1".equals(m.getCustType())) + .map(CustGroupMemberVO::getCustId) + .collect(Collectors.toList()); + // 企业客户 + List businessCustIds = memberList.stream() + .filter(m -> "2".equals(m.getCustType())) + .map(CustGroupMemberVO::getCustId) + .collect(Collectors.toList()); + + // 3. 批量查询各类型存量客户 + Set existingRetailCustIds = new HashSet<>(); + Set existingMerchantCustIds = new HashSet<>(); + Set existingBusinessCustIds = new HashSet<>(); + + if (!retailCustIds.isEmpty()) { + existingRetailCustIds.addAll(custGroupMemberMapper.selectExistingRetailCustIds(retailCustIds)); + } + if (!merchantCustIds.isEmpty()) { + existingMerchantCustIds.addAll(custGroupMemberMapper.selectExistingMerchantCustIds(merchantCustIds)); + } + if (!businessCustIds.isEmpty()) { + existingBusinessCustIds.addAll(custGroupMemberMapper.selectExistingBusinessCustIds(businessCustIds)); + } + + // 4. 填充存量状态 + for (CustGroupMemberVO member : memberList) { + String custType = member.getCustType(); + String custId = member.getCustId(); + if ("0".equals(custType)) { + member.setIsExisting(existingRetailCustIds.contains(custId)); + } else if ("1".equals(custType)) { + member.setIsExisting(existingMerchantCustIds.contains(custId)); + } else if ("2".equals(custType)) { + member.setIsExisting(existingBusinessCustIds.contains(custId)); + } else { + member.setIsExisting(false); + } + } + + return memberList; } @Override diff --git a/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupServiceImpl.java b/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupServiceImpl.java index 14f471c..fbcd070 100644 --- a/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupServiceImpl.java +++ b/ibs-group/src/main/java/com/ruoyi/group/service/impl/CustGroupServiceImpl.java @@ -6,27 +6,25 @@ import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.mapper.SysDeptMapper; import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO; import com.ruoyi.ibs.cmpm.service.GridCmpmService; +import com.ruoyi.ibs.dashboard.service.NotificationService; import com.ruoyi.ibs.draw.mapper.DrawGridShapeRelateMapper; +import com.ruoyi.ibs.grid.domain.entity.RegionCustUser; import com.ruoyi.ibs.grid.service.RegionGridListService; import com.ruoyi.group.domain.dto.CustGroupMemberTemplate; import com.ruoyi.group.domain.dto.CustGroupQueryDTO; -import com.ruoyi.group.domain.dto.GridImportDTO; import com.ruoyi.group.domain.entity.CustGroup; import com.ruoyi.group.domain.entity.CustGroupMember; import com.ruoyi.group.domain.vo.CustGroupVO; import com.ruoyi.group.mapper.CustGroupMapper; import com.ruoyi.group.mapper.CustGroupMemberMapper; import com.ruoyi.group.service.ICustGroupService; -import com.ruoyi.ibs.grid.domain.entity.RegionCustUser; -import com.ruoyi.ibs.handler.DynamicTableNameHelper; import lombok.extern.slf4j.Slf4j; -import org.springframework.transaction.support.TransactionTemplate; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; @@ -61,6 +59,9 @@ public class CustGroupServiceImpl implements ICustGroupService { @Resource private DrawGridShapeRelateMapper drawGridShapeRelateMapper; + @Resource + private NotificationService notificationService; + @Resource private TransactionTemplate transactionTemplate; @@ -78,14 +79,6 @@ public class CustGroupServiceImpl implements ICustGroupService { return custGroup; } - @Override - public void checkCustGroupViewPermission(Long id) { - Long count = custGroupMapper.countVisibleCustGroup(id, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId())); - if (count == null || count <= 0) { - throw new ServiceException("客群不存在或无查看权限"); - } - } - @Override @Transactional(rollbackFor = Exception.class) public String createCustGroupByTemplate(CustGroup custGroup, MultipartFile file) { @@ -97,9 +90,11 @@ public class CustGroupServiceImpl implements ICustGroupService { SysUser user = SecurityUtils.getLoginUser().getUser(); custGroup.setUserName(user.getUserName()); custGroup.setNickName(user.getNickName()); + custGroup.setDeptId(SecurityUtils.getDeptId()); // 设置默认值 - custGroup.setGroupMode("0"); // 静态 - custGroup.setCreateMode("1"); // 模板导入 + if (StringUtils.isEmpty(custGroup.getGroupMode())) { + custGroup.setGroupMode("0"); // 静态 + } if (StringUtils.isEmpty(custGroup.getGroupStatus())) { custGroup.setGroupStatus("0"); } @@ -114,222 +109,6 @@ public class CustGroupServiceImpl implements ICustGroupService { return String.valueOf(custGroup.getId()); } - @Override - @Transactional(rollbackFor = Exception.class) - public String createCustGroupByGrid(GridImportDTO gridImportDTO) { - CustGroup custGroup = gridImportDTO.getCustGroup(); - // 检查客群名称是否存在 - if (checkGroupNameExist(custGroup.getGroupName())) { - throw new ServiceException("客群名称已存在"); - } - // 获取当前用户信息 - SysUser user = SecurityUtils.getLoginUser().getUser(); - custGroup.setUserName(user.getUserName()); - custGroup.setNickName(user.getNickName()); - // 默认状态为正常 - if (StringUtils.isEmpty(custGroup.getGroupStatus())) { - custGroup.setGroupStatus("0"); - } - // 设置创建模式和状态 - custGroup.setGroupMode(custGroup.getGroupMode()); - // 根据网格类型设置创建方式:0=模板导入, 2=绩效网格, 3=地理网格, 4=绘制网格 - String gridType = gridImportDTO.getGridType(); - if ("0".equals(gridType)) { - custGroup.setCreateMode("2"); // 绩效网格 - } else if ("1".equals(gridType)) { - custGroup.setCreateMode("3"); // 地理网格 - } else if ("2".equals(gridType)) { - custGroup.setCreateMode("4"); // 绘制网格 - } else { - throw new ServiceException("无效的网格类型"); - } - - // 保存网格导入条件(用于动态客群后续更新) - custGroup.setGridType(gridImportDTO.getGridType()); - if ("0".equals(gridType)) { - custGroup.setCmpmBizType(gridImportDTO.getCmpmBizType()); - if (gridImportDTO.getUserNames() != null && !gridImportDTO.getUserNames().isEmpty()) { - custGroup.setGridUserNames(String.join(",", gridImportDTO.getUserNames())); - } - } else if ("1".equals(gridType)) { - if (gridImportDTO.getRegionGridIds() != null && !gridImportDTO.getRegionGridIds().isEmpty()) { - custGroup.setRegionGridIds(gridImportDTO.getRegionGridIds().stream() - .map(String::valueOf).collect(Collectors.joining(","))); - } - } else if ("2".equals(gridType)) { - if (gridImportDTO.getDrawGridIds() != null && !gridImportDTO.getDrawGridIds().isEmpty()) { - custGroup.setDrawGridIds(gridImportDTO.getDrawGridIds().stream() - .map(String::valueOf).collect(Collectors.joining(","))); - } - } - - // 设置创建状态为创建中 - custGroup.setCreateStatus("0"); - // 插入客群 - custGroupMapper.insert(custGroup); - // 重新设置回DTO(确保异步线程能获取到正确的ID和状态) - gridImportDTO.setCustGroup(custGroup); - // 获取当前用户部门编码(异步线程中无法获取) - String headId = SecurityUtils.getHeadId(); - // 异步导入客户 - executorService.submit(() -> doImportCustGroupByGrid(gridImportDTO, headId)); - return String.valueOf(custGroup.getId()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public String updateCustGroupByGrid(GridImportDTO gridImportDTO) { - CustGroup custGroup = gridImportDTO.getCustGroup(); - // 检查客群是否存在 - CustGroup existGroup = custGroupMapper.selectById(custGroup.getId()); - if (existGroup == null) { - throw new ServiceException("客群不存在"); - } - // 检查客群是否正在创建或更新 - if ("0".equals(existGroup.getCreateStatus())) { - throw new ServiceException("客群正在处理中,请稍后再试"); - } - // 更新客群基本信息 - if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) { - throw new ServiceException("客群名称已存在"); - } - // 检查网格条件是否发生变化 - boolean gridConditionChanged = isGridConditionChanged(existGroup, gridImportDTO); - - // 重新查询数据库,获取最新状态 - CustGroup latestGroup = custGroupMapper.selectById(custGroup.getId()); - latestGroup.setGroupName(custGroup.getGroupName()); - latestGroup.setGroupMode(custGroup.getGroupMode()); - latestGroup.setRemark(custGroup.getRemark()); - latestGroup.setValidTime(custGroup.getValidTime()); - latestGroup.setShareEnabled(custGroup.getShareEnabled()); - latestGroup.setShareDeptIds(custGroup.getShareDeptIds()); - - // 保存新的网格导入条件 - String gridType = gridImportDTO.getGridType(); - latestGroup.setGridType(gridType); - if ("0".equals(gridType)) { - latestGroup.setCmpmBizType(gridImportDTO.getCmpmBizType()); - if (gridImportDTO.getUserNames() != null && !gridImportDTO.getUserNames().isEmpty()) { - latestGroup.setGridUserNames(String.join(",", gridImportDTO.getUserNames())); - } else { - latestGroup.setGridUserNames(null); - } - } else if ("1".equals(gridType)) { - if (gridImportDTO.getRegionGridIds() != null && !gridImportDTO.getRegionGridIds().isEmpty()) { - latestGroup.setRegionGridIds(gridImportDTO.getRegionGridIds().stream() - .map(String::valueOf).collect(Collectors.joining(","))); - } else { - latestGroup.setRegionGridIds(null); - } - } else if ("2".equals(gridType)) { - if (gridImportDTO.getDrawGridIds() != null && !gridImportDTO.getDrawGridIds().isEmpty()) { - latestGroup.setDrawGridIds(gridImportDTO.getDrawGridIds().stream() - .map(String::valueOf).collect(Collectors.joining(","))); - } else { - latestGroup.setDrawGridIds(null); - } - } - - // 更新数据库 - latestGroup.setUpdateBy(SecurityUtils.getUsername()); - latestGroup.setUpdateTime(new Date()); - custGroupMapper.updateById(latestGroup); - - // 如果网格条件没有变化,直接返回成功 - if (!gridConditionChanged) { - log.info("客群网格条件未变化,跳过客户重新导入,客群ID:{}", custGroup.getId()); - return "客群更新成功(客户列表无需变更)"; - } - - // 网格条件发生变化,需要重新导入客户 - log.info("客群网格条件已变化,开始重新导入客户,客群ID:{}", custGroup.getId()); - // 设置更新状态为"更新中" - latestGroup.setCreateStatus("0"); - custGroupMapper.updateById(latestGroup); - // 重新设置回DTO(确保异步线程能获取到正确的ID和状态) - gridImportDTO.setCustGroup(latestGroup); - // 获取当前用户部门编码(异步线程中无法获取) - String headId = SecurityUtils.getHeadId(); - // 异步导入客户 - executorService.submit(() -> doImportCustGroupByGrid(gridImportDTO, headId)); - return "客群更新中"; - } - - /** - * 检查网格条件是否发生变化 - */ - private boolean isGridConditionChanged(CustGroup existGroup, GridImportDTO gridImportDTO) { - // 首先检查网格类型是否变化 - String newGridType = gridImportDTO.getGridType(); - String oldGridType = existGroup.getGridType(); - - // 如果旧记录没有网格类型信息,说明需要导入 - if (oldGridType == null) { - return true; - } - - // 网格类型变化 - if (!newGridType.equals(oldGridType)) { - log.info("网格类型变化:旧={}, 新={}", oldGridType, newGridType); - return true; - } - - // 根据网格类型检查具体条件 - if ("0".equals(newGridType)) { - // 绩效网格:比较业务类型和客户经理列表 - String oldBizType = existGroup.getCmpmBizType(); - String newBizType = gridImportDTO.getCmpmBizType(); - List newUserNames = gridImportDTO.getUserNames(); - - if (!Objects.equals(oldBizType, newBizType)) { - log.info("绩效业务类型变化:旧={}, 新={}", oldBizType, newBizType); - return true; - } - - // 比较客户经理列表 - String oldUserNames = existGroup.getGridUserNames(); - String newUserNamesStr = newUserNames == null || newUserNames.isEmpty() - ? null : String.join(",", newUserNames); - if (!Objects.equals(oldUserNames, newUserNamesStr)) { - log.info("客户经理列表变化:旧={}, 新={}", oldUserNames, newUserNamesStr); - return true; - } - } else if ("1".equals(newGridType)) { - // 地理网格:比较网格ID列表 - String oldRegionIds = existGroup.getRegionGridIds(); - List newRegionIds = gridImportDTO.getRegionGridIds(); - String newRegionIdsStr = newRegionIds == null || newRegionIds.isEmpty() - ? null : newRegionIds.stream().map(String::valueOf).sorted().collect(Collectors.joining(",")); - - // 归一化比较(排序后比较) - String oldRegionIdsSorted = oldRegionIds == null ? null : - Arrays.stream(oldRegionIds.split(",")).sorted().collect(Collectors.joining(",")); - - if (!Objects.equals(oldRegionIdsSorted, newRegionIdsStr)) { - log.info("地理网格ID列表变化:旧={}, 新={}", oldRegionIdsSorted, newRegionIdsStr); - return true; - } - } else if ("2".equals(newGridType)) { - // 绘制网格:比较网格ID列表 - String oldDrawIds = existGroup.getDrawGridIds(); - List newDrawIds = gridImportDTO.getDrawGridIds(); - String newDrawIdsStr = newDrawIds == null || newDrawIds.isEmpty() - ? null : newDrawIds.stream().map(String::valueOf).sorted().collect(Collectors.joining(",")); - - // 归一化比较(排序后比较) - String oldDrawIdsSorted = oldDrawIds == null ? null : - Arrays.stream(oldDrawIds.split(",")).sorted().collect(Collectors.joining(",")); - - if (!Objects.equals(oldDrawIdsSorted, newDrawIdsStr)) { - log.info("绘制网格ID列表变化:旧={}, 新={}", oldDrawIdsSorted, newDrawIdsStr); - return true; - } - } - - return false; - } - @Override @Transactional(rollbackFor = Exception.class) public String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file) { @@ -354,17 +133,31 @@ public class CustGroupServiceImpl implements ICustGroupService { latestGroup.setValidTime(custGroup.getValidTime()); latestGroup.setShareEnabled(custGroup.getShareEnabled()); latestGroup.setShareDeptIds(custGroup.getShareDeptIds()); - // 设置更新状态为"更新中" - latestGroup.setCreateStatus("0"); - // 更新数据库 + latestGroup.setGroupTags(custGroup.getGroupTags()); + // 保存网格参数(从 custGroup 中获取) + latestGroup.setGridType(custGroup.getGridType()); + latestGroup.setRegionGridIds(custGroup.getRegionGridIds()); + latestGroup.setDrawGridIds(custGroup.getDrawGridIds()); latestGroup.setUpdateBy(SecurityUtils.getUsername()); latestGroup.setUpdateTime(new Date()); - custGroupMapper.updateById(latestGroup); - // 获取当前用户部门编码(异步线程中无法获取) - String headId = SecurityUtils.getHeadId(); - // 异步导入客户(使用最新状态的对象) - executorService.submit(() -> doImportCustGroupByTemplate(latestGroup, file, headId)); - return "客群更新中"; + + // 判断是否需要导入客户文件 + if (file != null && !file.isEmpty()) { + // 有文件:设置状态为"更新中",异步导入客户 + latestGroup.setCreateStatus("0"); + custGroupMapper.updateById(latestGroup); + // 获取当前用户部门编码(异步线程中无法获取) + String headId = SecurityUtils.getHeadId(); + // 异步导入客户(使用最新状态的对象) + final CustGroup finalGroup = latestGroup; + executorService.submit(() -> doImportCustGroupByTemplate(finalGroup, file, headId)); + return "客群更新中"; + } else { + // 无文件:只更新客群信息,直接完成 + latestGroup.setCreateStatus("1"); + custGroupMapper.updateById(latestGroup); + return "客群更新成功"; + } } @Override @@ -388,6 +181,7 @@ public class CustGroupServiceImpl implements ICustGroupService { public boolean checkGroupNameExist(String groupName) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(CustGroup::getGroupName, groupName); + wrapper.ne(CustGroup::getCreateStatus, "2"); return custGroupMapper.selectCount(wrapper) > 0; } @@ -400,6 +194,14 @@ public class CustGroupServiceImpl implements ICustGroupService { return custGroup.getCreateStatus(); } + @Override + public void checkCustGroupViewPermission(Long id) { + Long count = custGroupMapper.countVisibleCustGroup(id, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId())); + if (count == null || count <= 0) { + throw new ServiceException("客群不存在或无查看权限"); + } + } + @Override public void updateDynamicCustGroups() { log.info("开始更新动态客群..."); @@ -480,105 +282,296 @@ public class CustGroupServiceImpl implements ICustGroupService { } /** - * 更新单个动态客群(增量更新方式) + * 更新单个动态客群的管户关系,保留客户集合本身不变。 */ private void updateDynamicCustGroup(CustGroup custGroup) { - // 从客群部门获取 headId(定时任务中无登录用户上下文) - // headId 是 deptId 的前三位 - String headId = ""; - if (custGroup.getDeptId() != null) { - String deptIdStr = String.valueOf(custGroup.getDeptId()); - headId = deptIdStr.substring(0, 3); - } - - // 查询现有客户(排除手动移除的客户,只查询正常状态的客户) - LambdaQueryWrapper existWrapper = new LambdaQueryWrapper<>(); - existWrapper.eq(CustGroupMember::getGroupId, custGroup.getId()) - .eq(CustGroupMember::getDelFlag, 0) - .eq(CustGroupMember::getManualRemove, 0) - .select(CustGroupMember::getCustId, CustGroupMember::getCustType); - List existMembers = custGroupMemberMapper.selectList(existWrapper); - Set existKeySet = existMembers.stream() - .map(m -> m.getCustId() + "_" + m.getCustType()) - .collect(Collectors.toSet()); - - // 构造 GridImportDTO 复用现有导入方法 - GridImportDTO gridImportDTO = new GridImportDTO(); - gridImportDTO.setCustGroup(custGroup); - gridImportDTO.setGridType(custGroup.getGridType()); - gridImportDTO.setCmpmBizType(custGroup.getCmpmBizType()); - - // 根据网格类型设置参数并查询 - List newMemberList = new ArrayList<>(); - String gridType = custGroup.getGridType(); - if ("0".equals(gridType)) { - // 绩效网格 - if (StringUtils.isNotEmpty(custGroup.getGridUserNames())) { - gridImportDTO.setUserNames(Arrays.asList(custGroup.getGridUserNames().split(","))); - } - newMemberList.addAll(importFromCmpmGrid(custGroup, gridImportDTO, headId)); - } else if ("1".equals(gridType)) { - // 地理网格 - if (StringUtils.isNotEmpty(custGroup.getRegionGridIds())) { - gridImportDTO.setRegionGridIds(Arrays.stream(custGroup.getRegionGridIds().split(",")) - .map(Long::valueOf).collect(Collectors.toList())); - } - newMemberList.addAll(importFromRegionGrid(custGroup, gridImportDTO, headId)); - } else if ("2".equals(gridType)) { - // 绘制网格 - if (StringUtils.isNotEmpty(custGroup.getDrawGridIds())) { - gridImportDTO.setDrawGridIds(Arrays.stream(custGroup.getDrawGridIds().split(",")) - .map(Long::valueOf).collect(Collectors.toList())); - } - newMemberList.addAll(importFromDrawGrid(custGroup, gridImportDTO, headId)); - } - - // 计算差异 - Set newKeySet = newMemberList.stream() - .map(m -> m.getCustId() + "_" + m.getCustType()) - .collect(Collectors.toSet()); - - // 需要删除的客户:存在于旧列表但不存在于新列表 - List toDeleteKeys = new ArrayList<>(existKeySet); - toDeleteKeys.removeAll(newKeySet); - // 需要新增的客户:存在于新列表但不存在于旧列表 - List toAddMembers = newMemberList.stream() - .filter(m -> !existKeySet.contains(m.getCustId() + "_" + m.getCustType())) - .collect(Collectors.toList()); - - // 删除不再存在的客户 - if (!toDeleteKeys.isEmpty()) { - for (String key : toDeleteKeys) { - String[] parts = key.split("_"); - LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); - deleteWrapper.eq(CustGroupMember::getGroupId, custGroup.getId()) - .eq(CustGroupMember::getCustId, parts[0]) - .eq(CustGroupMember::getCustType, parts[1]); - custGroupMemberMapper.delete(deleteWrapper); - } - } - - // 插入新增客户 - int addCount = 0; - for (CustGroupMember member : toAddMembers) { + transactionTemplate.executeWithoutResult(status -> { try { - custGroupMemberMapper.insert(member); - addCount++; - } catch (org.springframework.dao.DuplicateKeyException e) { - log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId()); - } - } + // 从客群部门获取 headId(定时任务中无登录用户上下文) + String headId = ""; + if (custGroup.getDeptId() != null) { + String deptIdStr = String.valueOf(custGroup.getDeptId()); + headId = deptIdStr.length() >= 3 ? deptIdStr.substring(0, 3) : deptIdStr; + } - log.info("客群客户更新完成,客群ID:{},原数量:{},新数量:{},新增:{},删除:{}", - custGroup.getId(), existMembers.size(), newMemberList.size(), addCount, toDeleteKeys.size()); + // 查询现有客户成员(排除手动移除的客户) + LambdaQueryWrapper existWrapper = new LambdaQueryWrapper<>(); + existWrapper.eq(CustGroupMember::getGroupId, custGroup.getId()) + .eq(CustGroupMember::getDelFlag, 0) + .eq(CustGroupMember::getManualRemove, 0); + List existMembers = custGroupMemberMapper.selectList(existWrapper); + + if (existMembers.isEmpty()) { + log.info("客群中没有客户,跳过更新,客群ID:{}", custGroup.getId()); + return; + } + + Map baseMemberMap = new LinkedHashMap<>(); + Map> existMemberMap = new LinkedHashMap<>(); + for (CustGroupMember member : existMembers) { + String key = buildCustKey(member.getCustId(), member.getCustType()); + baseMemberMap.putIfAbsent(key, member); + existMemberMap.computeIfAbsent(key, k -> new ArrayList<>()).add(member); + } + + Map> gridRelMap = queryGridRelationshipsMap(custGroup, new ArrayList<>(baseMemberMap.values()), headId); + + int updateCount = 0; + int insertCount = 0; + int deleteCount = 0; + + for (Map.Entry entry : baseMemberMap.entrySet()) { + String key = entry.getKey(); + CustGroupMember baseMember = entry.getValue(); + List currentList = existMemberMap.getOrDefault(key, Collections.emptyList()); + List desiredList = buildDesiredMembers(baseMember, gridRelMap.get(key)); + + int commonSize = Math.min(currentList.size(), desiredList.size()); + for (int i = 0; i < commonSize; i++) { + CustGroupMember current = currentList.get(i); + CustGroupMember desired = desiredList.get(i); + current.setUserName(desired.getUserName()); + current.setNickName(desired.getNickName()); + current.setOutletId(desired.getOutletId()); + current.setOutletName(desired.getOutletName()); + current.setBranchId(desired.getBranchId()); + current.setBranchName(desired.getBranchName()); + current.setUpdateTime(new Date()); + custGroupMemberMapper.updateById(current); + updateCount++; + } + + for (int i = commonSize; i < currentList.size(); i++) { + custGroupMemberMapper.deleteById(currentList.get(i).getId()); + deleteCount++; + } + + for (int i = commonSize; i < desiredList.size(); i++) { + CustGroupMember newMember = desiredList.get(i); + newMember.setGroupId(custGroup.getId()); + newMember.setCreateTime(new Date()); + custGroupMemberMapper.insert(newMember); + insertCount++; + } + } + + log.info("客群管户关系更新完成,客群ID:{},客户数:{},更新:{},新增:{},删除:{}", + custGroup.getId(), baseMemberMap.size(), updateCount, insertCount, deleteCount); + } catch (Exception e) { + status.setRollbackOnly(); + throw e; + } + }); } /** - * 异步导入客户 + * 从对应网格查询客户的最新管户关系,返回 Map(key = custId_custType) + */ + private Map> queryGridRelationshipsMap(CustGroup custGroup, List members, String headId) { + Map> result = new LinkedHashMap<>(); + String gridType = custGroup.getGridType(); + + if (StringUtils.isEmpty(gridType)) { + log.warn("动态客群未配置网格类型,跳过管户关系更新,客群ID:{}", custGroup.getId()); + return result; + } + + if ("0".equals(gridType)) { + fillCmpmRelationships(result, members, headId); + } else if ("1".equals(gridType)) { + fillRegionRelationships(result, parseGridIds(custGroup.getRegionGridIds()), members, headId); + } else if ("2".equals(gridType)) { + fillDrawRelationships(result, parseGridIds(custGroup.getDrawGridIds()), members, headId); + } else { + log.warn("动态客群网格类型暂不支持,客群ID:{},gridType:{}", custGroup.getId(), gridType); + } + return result; + } + + private void fillCmpmRelationships(Map> result, List members, String headId) { + List> retailCustList = new ArrayList<>(); + List> corporateCustList = new ArrayList<>(); + + for (CustGroupMember member : members) { + Map custInfo = new HashMap<>(); + custInfo.put("custId", member.getCustId()); + custInfo.put("custType", member.getCustType()); + if ("0".equals(member.getCustType()) || "1".equals(member.getCustType())) { + retailCustList.add(custInfo); + } else if ("2".equals(member.getCustType())) { + corporateCustList.add(custInfo); + } + } + + if (!retailCustList.isEmpty()) { + List relationships = gridCmpmService.getRelationshipsByCustList("retail", headId, retailCustList); + for (GridCmpmVO cmpm : relationships) { + CustGroupMember member = new CustGroupMember(); + member.setUserName(cmpm.getUserName()); + member.setNickName(cmpm.getNickName()); + member.setOutletId(cmpm.getOutletId()); + member.setOutletName(cmpm.getOutletName()); + member.setBranchId(cmpm.getBranchId()); + member.setBranchName(cmpm.getBranchName()); + addRelationship(result, buildCustKey(cmpm.getCustId(), cmpm.getCustType()), member); + } + } + + if (!corporateCustList.isEmpty()) { + List relationships = gridCmpmService.getRelationshipsByCustList("corporate", headId, corporateCustList); + for (GridCmpmVO cmpm : relationships) { + CustGroupMember member = new CustGroupMember(); + member.setUserName(cmpm.getUserName()); + member.setNickName(cmpm.getNickName()); + member.setOutletId(cmpm.getOutletId()); + member.setOutletName(cmpm.getOutletName()); + member.setBranchId(cmpm.getBranchId()); + member.setBranchName(cmpm.getBranchName()); + addRelationship(result, buildCustKey(cmpm.getCustId(), cmpm.getCustType()), member); + } + } + } + + private void fillRegionRelationships(Map> result, List regionGridIds, + List members, String headId) { + if (regionGridIds == null || regionGridIds.isEmpty()) { + return; + } + + Set targetCustKeys = members.stream() + .map(member -> buildCustKey(member.getCustId(), member.getCustType())) + .collect(Collectors.toSet()); + + List custUsers = regionGridListService.selectAllCustByGridIds(regionGridIds, headId); + for (RegionCustUser custUser : custUsers) { + String key = buildCustKey(custUser.getCustId(), custUser.getCustType()); + if (!targetCustKeys.contains(key)) { + continue; + } + addRegionCustUserRelationships(result, key, custUser); + } + } + + private void fillDrawRelationships(Map> result, List drawGridIds, + List members, String headId) { + if (drawGridIds == null || drawGridIds.isEmpty()) { + return; + } + + Set targetCustKeys = members.stream() + .map(member -> buildCustKey(member.getCustId(), member.getCustType())) + .collect(Collectors.toSet()); + + for (Long gridId : drawGridIds) { + List custUsers = drawGridShapeRelateMapper.selectCustByDrawGridId(gridId, headId); + if (custUsers == null || custUsers.isEmpty()) { + continue; + } + for (RegionCustUser custUser : custUsers) { + String key = buildCustKey(custUser.getCustId(), custUser.getCustType()); + if (!targetCustKeys.contains(key)) { + continue; + } + addRegionCustUserRelationships(result, key, custUser); + } + } + } + + private void addRegionCustUserRelationships(Map> result, String key, RegionCustUser custUser) { + String[] userNames = custUser.getUserNames() != null ? custUser.getUserNames().split(",") : new String[0]; + String[] nickNames = custUser.getNickNames() != null ? custUser.getNickNames().split(",") : new String[0]; + String[] outletIds = custUser.getOutletIds() != null ? custUser.getOutletIds().split(",") : new String[0]; + String[] outletNames = custUser.getOutletNames() != null ? custUser.getOutletNames().split(",") : new String[0]; + String[] branchIds = custUser.getBranchIds() != null ? custUser.getBranchIds().split(",") : new String[0]; + String[] branchNames = custUser.getBranchNames() != null ? custUser.getBranchNames().split(",") : new String[0]; + + int count = Math.max(userNames.length, 1); + for (int i = 0; i < count; i++) { + CustGroupMember member = new CustGroupMember(); + member.setUserName(i < userNames.length ? userNames[i] : null); + member.setNickName(i < nickNames.length ? nickNames[i] : null); + member.setOutletId(parseLongValue(i < outletIds.length ? outletIds[i] : null)); + member.setOutletName(i < outletNames.length ? outletNames[i] : null); + member.setBranchId(parseLongValue(i < branchIds.length ? branchIds[i] : null)); + member.setBranchName(i < branchNames.length ? branchNames[i] : null); + addRelationship(result, key, member); + } + } + + private void addRelationship(Map> result, String key, CustGroupMember relationship) { + List relationshipList = result.computeIfAbsent(key, k -> new ArrayList<>()); + boolean exists = relationshipList.stream().anyMatch(item -> + Objects.equals(StringUtils.trimToNull(item.getUserName()), StringUtils.trimToNull(relationship.getUserName())) + && Objects.equals(StringUtils.trimToNull(item.getNickName()), StringUtils.trimToNull(relationship.getNickName())) + && Objects.equals(item.getOutletId(), relationship.getOutletId()) + && Objects.equals(StringUtils.trimToNull(item.getOutletName()), StringUtils.trimToNull(relationship.getOutletName())) + && Objects.equals(item.getBranchId(), relationship.getBranchId()) + && Objects.equals(StringUtils.trimToNull(item.getBranchName()), StringUtils.trimToNull(relationship.getBranchName()))); + if (!exists) { + relationshipList.add(relationship); + } + } + + private List buildDesiredMembers(CustGroupMember baseMember, List relationships) { + List result = new ArrayList<>(); + if (relationships == null || relationships.isEmpty()) { + CustGroupMember member = copyBaseMember(baseMember); + clearRelationshipFields(member); + result.add(member); + return result; + } + + for (CustGroupMember relationship : relationships) { + CustGroupMember member = copyBaseMember(baseMember); + member.setUserName(relationship.getUserName()); + member.setNickName(relationship.getNickName()); + member.setOutletId(relationship.getOutletId()); + member.setOutletName(relationship.getOutletName()); + member.setBranchId(relationship.getBranchId()); + member.setBranchName(relationship.getBranchName()); + result.add(member); + } + return result; + } + + private CustGroupMember copyBaseMember(CustGroupMember source) { + CustGroupMember target = new CustGroupMember(); + target.setGroupId(source.getGroupId()); + target.setCustType(source.getCustType()); + target.setCustId(source.getCustId()); + target.setCustName(source.getCustName()); + target.setCustIdc(source.getCustIdc()); + target.setSocialCreditCode(source.getSocialCreditCode()); + target.setCreateBy(source.getCreateBy()); + return target; + } + + private void clearRelationshipFields(CustGroupMember member) { + member.setUserName(null); + member.setNickName(null); + member.setOutletId(null); + member.setOutletName(null); + member.setBranchId(null); + member.setBranchName(null); + } + + private String buildCustKey(String custId, String custType) { + return custId + "_" + custType; + } + + private Long parseLongValue(String value) { + if (StringUtils.isEmpty(value)) { + return null; + } + return Long.valueOf(value.trim()); + } + + /** + * 异步导入客户(模板导入 + 管户关系匹配) */ private void doImportCustGroupByTemplate(CustGroup custGroup, MultipartFile file, String headId) { try { - // 解析Excel + // 1. 解析Excel获取客户信息 List templateList = EasyExcel.read(file.getInputStream()) .head(CustGroupMemberTemplate.class) .sheet() @@ -586,9 +579,9 @@ public class CustGroupServiceImpl implements ICustGroupService { if (templateList.isEmpty()) { throw new ServiceException("导入列表为空"); } - // 校验和去重 - Set uniqueCustIds = new HashSet<>(); - List memberList = new ArrayList<>(); + + // 2. 校验和去重,构建客户信息Map (custId + custType -> CustGroupMember) + Map custInfoMap = new LinkedHashMap<>(); for (CustGroupMemberTemplate template : templateList) { // 校验客户类型 String custType = template.getCustType(); @@ -610,15 +603,29 @@ public class CustGroupServiceImpl implements ICustGroupService { default: throw new ServiceException("客户类型填写错误,只能填写:个人、企业、商户"); } + // 校验客户类型与客群类型是否匹配 + String groupType = custGroup.getGroupType(); + if ("0".equals(groupType)) { + // 零售类客群:只允许个人(0)和商户(1) + if (!"0".equals(custTypeValue) && !"1".equals(custTypeValue)) { + throw new ServiceException("零售类客群只能导入个人和商户客户,客户[" + template.getCustId() + "]类型为" + custType + ",不符合要求"); + } + } else if ("1".equals(groupType)) { + // 公司类客群:只允许企业(2) + if (!"2".equals(custTypeValue)) { + throw new ServiceException("公司类客群只能导入企业客户,客户[" + template.getCustId() + "]类型为" + custType + ",不符合要求"); + } + } // 客户号不能为空 if (StringUtils.isEmpty(template.getCustId())) { throw new ServiceException("客户号不能为空"); } - // 检查重复 - if (!uniqueCustIds.add(template.getCustId())) { + // 构建唯一键 + String key = template.getCustId() + "_" + custTypeValue; + if (custInfoMap.containsKey(key)) { continue; // 跳过重复客户 } - // 创建客户成员 + // 创建客户成员(仅客户信息) CustGroupMember member = new CustGroupMember(); member.setGroupId(custGroup.getId()); member.setCustType(custTypeValue); @@ -636,256 +643,264 @@ public class CustGroupServiceImpl implements ICustGroupService { && StringUtils.isEmpty(member.getSocialCreditCode())) { throw new ServiceException("企业/商户客户[" + template.getCustId() + "]统信码不能为空"); } - memberList.add(member); + custInfoMap.put(key, member); } - // 使用编程式事务:先删除旧客户,再插入新客户 - transactionTemplate.executeWithoutResult(status -> { - // 删除该客群的所有旧客户 - log.info("开始删除客群旧客户(模板导入),客群ID:{}", custGroup.getId()); - LambdaQueryWrapper memberWrapper = new LambdaQueryWrapper<>(); - memberWrapper.eq(CustGroupMember::getGroupId, custGroup.getId()); - custGroupMemberMapper.delete(memberWrapper); - log.info("客群旧客户删除完成(模板导入),客群ID:{}", custGroup.getId()); + final int[] successCount = {0}; + final int[] skippedCount = {0}; - // 批量插入新客户 - log.info("开始批量插入客户(模板导入),客群ID:{},客户总数:{}", custGroup.getId(), memberList.size()); - int batchSize = 1000; - int successCount = 0; - int skippedCount = 0; - for (int i = 0; i < memberList.size(); i += batchSize) { - int endIndex = Math.min(i + batchSize, memberList.size()); - List batchList = memberList.subList(i, endIndex); - log.info("处理批次 [{}/{}],本批大小:{}", i / batchSize + 1, (memberList.size() + batchSize - 1) / batchSize, batchList.size()); - for (CustGroupMember member : batchList) { - try { - custGroupMemberMapper.insert(member); - successCount++; - } catch (DuplicateKeyException e) { - // 客户已存在(包含被手动移除后保留的记录),直接跳过,避免再次加入客群 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId()) - .eq(CustGroupMember::getCustId, member.getCustId()) - .eq(CustGroupMember::getCustType, member.getCustType()); - CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper); - skippedCount++; - if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) { - log.debug("客户已被手动移除,跳过重新导入:groupId={}, custId={}", member.getGroupId(), member.getCustId()); - } else { - log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId()); + transactionTemplate.executeWithoutResult(status -> { + try { + // 3. 从网格中查询管户关系 + List relationshipList = queryCustRelationships(custGroup, headId); + + // 4. 构建管户关系Map (custId + custType -> List) + Map> relationshipMap = new HashMap<>(); + for (CustGroupMember rel : relationshipList) { + String key = rel.getCustId() + "_" + rel.getCustType(); + relationshipMap.computeIfAbsent(key, k -> new ArrayList<>()).add(rel); + } + + // 5. 合并客户信息和管户关系,生成最终成员列表 + List memberList = new ArrayList<>(); + for (Map.Entry entry : custInfoMap.entrySet()) { + String key = entry.getKey(); + CustGroupMember custInfo = entry.getValue(); + List relationships = relationshipMap.get(key); + + if (relationships != null && !relationships.isEmpty()) { + for (CustGroupMember rel : relationships) { + CustGroupMember member = new CustGroupMember(); + member.setGroupId(custGroup.getId()); + member.setCustType(custInfo.getCustType()); + member.setCustId(custInfo.getCustId()); + member.setCustName(custInfo.getCustName()); + member.setCustIdc(custInfo.getCustIdc()); + member.setSocialCreditCode(custInfo.getSocialCreditCode()); + member.setCreateTime(new Date()); + member.setUserName(rel.getUserName()); + member.setNickName(rel.getNickName()); + member.setOutletId(rel.getOutletId()); + member.setOutletName(rel.getOutletName()); + member.setBranchId(rel.getBranchId()); + member.setBranchName(rel.getBranchName()); + memberList.add(member); } + } else { + memberList.add(custInfo); } } + + log.info("开始批量插入客户(模板导入),客群ID:{},客户总数:{}", custGroup.getId(), memberList.size()); + for (CustGroupMember member : memberList) { + try { + custGroupMemberMapper.insert(member); + successCount[0]++; + } catch (DuplicateKeyException e) { + skippedCount[0]++; + log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId()); + } + } + + log.info("客群客户导入完成(模板),客群ID:{},成功:{},跳过重复:{}", + custGroup.getId(), successCount[0], skippedCount[0]); + custGroup.setCreateStatus("1"); + custGroup.setUpdateBy(custGroup.getCreateBy()); + custGroup.setUpdateTime(new Date()); + custGroupMapper.updateById(custGroup); + } catch (Exception e) { + status.setRollbackOnly(); + throw e; } - log.info("客群客户导入完成(模板),客群ID:{},成功:{},跳过重复:{}", - custGroup.getId(), successCount, skippedCount); - // 更新创建状态为成功 - custGroup.setCreateStatus("1"); - custGroup.setUpdateBy(custGroup.getCreateBy()); - custGroup.setUpdateTime(new Date()); - custGroupMapper.updateById(custGroup); }); + // 发送成功通知 + String successMsg = String.format("客群【%s】导入完成,成功导入 %d 条客户数据", custGroup.getGroupName(), successCount[0]); + notificationService.sendNotification(custGroup.getCreateBy(), successMsg); } catch (Exception e) { log.error("客群客户导入失败,客群ID:{},异常:{}", custGroup.getId(), e.getMessage(), e); - // 注意:由于删除和插入在同一事务中,插入失败会自动回滚删除操作,无需手动清理 - - // 更新创建状态为失败 - custGroup.setCreateStatus("2"); - custGroup.setUpdateBy(custGroup.getCreateBy()); - custGroup.setUpdateTime(new Date()); - custGroupMapper.updateById(custGroup); - throw new ServiceException("客群客户导入失败: " + e.getMessage()); - } - } - - /** - * 异步导入客户(网格方式) - */ - private void doImportCustGroupByGrid(GridImportDTO gridImportDTO, String headId) { - CustGroup custGroup = gridImportDTO.getCustGroup(); - try { - List memberList = new ArrayList<>(); - String gridType = gridImportDTO.getGridType(); - // 根据网格类型查询客户 - if ("0".equals(gridType)) { - // 绩效网格 - memberList.addAll(importFromCmpmGrid(custGroup, gridImportDTO, headId)); - } else if ("1".equals(gridType)) { - // 地理网格 - memberList.addAll(importFromRegionGrid(custGroup, gridImportDTO, headId)); - } else if ("2".equals(gridType)) { - // 绘制网格 - memberList.addAll(importFromDrawGrid(custGroup, gridImportDTO, headId)); - } - if (memberList.isEmpty()) { - throw new ServiceException("未查询到任何客户"); - } - - // 使用编程式事务:先删除旧客户,再插入新客户 transactionTemplate.executeWithoutResult(status -> { - // 删除该客群的所有旧客户 - log.info("开始删除客群旧客户,客群ID:{}", custGroup.getId()); - LambdaQueryWrapper memberWrapper = new LambdaQueryWrapper<>(); - memberWrapper.eq(CustGroupMember::getGroupId, custGroup.getId()); - custGroupMemberMapper.delete(memberWrapper); - log.info("客群旧客户删除完成,客群ID:{}", custGroup.getId()); - - // 批量插入新客户 - log.info("开始批量插入客户,客群ID:{},客户总数:{}", custGroup.getId(), memberList.size()); - - // 分批批量插入(每批1000条) - int batchSize = 1000; - int totalInserted = 0; - int totalSkipped = 0; - - for (int i = 0; i < memberList.size(); i += batchSize) { - int endIndex = Math.min(i + batchSize, memberList.size()); - List batchList = memberList.subList(i, endIndex); - log.info("处理批次 [{}/{}],本批大小:{}", i / batchSize + 1, - (memberList.size() + batchSize - 1) / batchSize, batchList.size()); - - // SQL层面的批量插入 - custGroupMemberMapper.batchInsertMembers(batchList); - - int manualRemovedCount = countManualRemovedMembers(custGroup.getId(), batchList); - totalInserted += batchList.size() - manualRemovedCount; - totalSkipped += manualRemovedCount; - } - - log.info("客群客户导入完成(网格),客群ID:{},插入:{},跳过:{}", - custGroup.getId(), totalInserted, totalSkipped); - - // 更新创建状态为成功 - custGroup.setCreateStatus("1"); - custGroup.setUpdateBy(custGroup.getCreateBy()); - custGroup.setUpdateTime(new Date()); - log.info("准备更新客群状态为成功,客群ID:{}", custGroup.getId()); - custGroupMapper.updateById(custGroup); - log.info("客群状态更新成功完成,客群ID:{}", custGroup.getId()); - }); - } catch (Exception e) { - // 先记录原始异常(必须第一时间记录,避免后续异常覆盖) - log.error("==========客群客户导入异常========== 客群ID:{},异常类型:{},异常消息:{}", - custGroup.getId(), e.getClass().getName(), e.getMessage(), e); - - // 注意:由于删除和插入在同一事务中,插入失败会自动回滚删除操作,无需手动清理 - - // 更新创建状态为失败 - try { custGroup.setCreateStatus("2"); custGroup.setUpdateBy(custGroup.getCreateBy()); custGroup.setUpdateTime(new Date()); - log.info("准备更新客群状态为失败,客群ID:{}", custGroup.getId()); custGroupMapper.updateById(custGroup); - log.info("客群状态更新为失败完成,客群ID:{}", custGroup.getId()); - } catch (Exception updateException) { - log.error("==========更新客群状态为失败也异常了========== 客群ID:{},异常类型:{},异常消息:{}", - custGroup.getId(), updateException.getClass().getName(), updateException.getMessage(), updateException); - } - throw new ServiceException("客群客户导入失败: " + e.getMessage()); + }); + // 发送失败通知 + String failMsg = String.format("客群【%s】导入失败:%s", custGroup.getGroupName(), e.getMessage()); + notificationService.sendNotification(custGroup.getCreateBy(), failMsg); } } /** - * 统计本批中被手动移除、因此需要持续排除的客户数量 + * 解析网格ID字符串为List */ - private int countManualRemovedMembers(Long groupId, List batchList) { - Set batchKeys = batchList.stream() - .map(m -> m.getCustId() + "|" + m.getCustType()) - .collect(Collectors.toSet()); - - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(CustGroupMember::getGroupId, groupId) - .eq(CustGroupMember::getManualRemove, 1); - List manualRemovedList = custGroupMemberMapper.selectList(queryWrapper); - - return (int) manualRemovedList.stream() - .filter(m -> batchKeys.contains(m.getCustId() + "|" + m.getCustType())) - .count(); + private List parseGridIds(String gridIdsStr) { + if (StringUtils.isEmpty(gridIdsStr)) { + return null; + } + return Arrays.stream(gridIdsStr.split(",")) + .filter(StringUtils::isNotEmpty) + .map(Long::valueOf) + .collect(Collectors.toList()); } /** - * 从绩效网格导入客户 + * 从网格中查询管户关系 */ - private List importFromCmpmGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) { + private List queryCustRelationships(CustGroup custGroup, String headId) { + List memberList = new ArrayList<>(); + String gridType = custGroup.getGridType(); + List regionGridIds = parseGridIds(custGroup.getRegionGridIds()); + List drawGridIds = parseGridIds(custGroup.getDrawGridIds()); + + if (StringUtils.isEmpty(gridType)) { + return memberList; + } + + if ("0".equals(gridType)) { + memberList.addAll(queryRelationshipsFromCmpmGrid(custGroup, headId)); + } else if ("1".equals(gridType)) { + memberList.addAll(queryRelationshipsFromRegionGrid(custGroup, regionGridIds, headId)); + } else if ("2".equals(gridType)) { + memberList.addAll(queryRelationshipsFromDrawGrid(custGroup, drawGridIds, headId)); + } + + return memberList; + } + + /** + * 从绩效网格查询管户关系(全量匹配) + */ + private List queryRelationshipsFromCmpmGrid(CustGroup custGroup, String headId) { List memberList = new ArrayList<>(); - // 确定要查询的网格类型 List gridTypes = new ArrayList<>(); - String cmpmBizType = gridImportDTO.getCmpmBizType(); - if ("retail".equals(cmpmBizType)) { + String groupType = custGroup.getGroupType(); + + if ("0".equals(groupType)) { gridTypes.add("retail"); - } else if ("corporate".equals(cmpmBizType)) { + } else if ("1".equals(groupType)) { gridTypes.add("corporate"); - gridTypes.add("corporate_account"); } else { - throw new ServiceException("请选择绩效网格业务类型(零售/对公/对公账户)"); + return memberList; } - // 查询客户 - for (String userName : gridImportDTO.getUserNames()) { - for (String gridType : gridTypes) { - List cmpmList = gridCmpmService.selectManageListForImport(gridType, userName, headId); - for (GridCmpmVO cmpm : cmpmList) { - CustGroupMember member = new CustGroupMember(); - member.setGroupId(custGroup.getId()); - member.setCustId(cmpm.getCustId()); - member.setCustName(cmpm.getCustName()); - member.setCustType(cmpm.getCustType()); - member.setCustIdc(cmpm.getCustIdc()); - member.setSocialCreditCode(cmpm.getUsci()); - member.setCreateTime(new Date()); - memberList.add(member); - } + + for (String gridType : gridTypes) { + List cmpmList = gridCmpmService.selectAllForImport(gridType, headId); + for (GridCmpmVO cmpm : cmpmList) { + CustGroupMember member = new CustGroupMember(); + member.setGroupId(custGroup.getId()); + member.setCustId(cmpm.getCustId()); + member.setCustType(cmpm.getCustType()); + member.setUserName(cmpm.getUserName()); + member.setNickName(cmpm.getNickName()); + member.setOutletId(cmpm.getOutletId()); + member.setOutletName(cmpm.getOutletName()); + member.setBranchId(cmpm.getBranchId()); + member.setBranchName(cmpm.getBranchName()); + memberList.add(member); } } return memberList; } /** - * 从地理网格导入客户 + * 从地理网格查询管户关系 */ - private List importFromRegionGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) { + private List queryRelationshipsFromRegionGrid(CustGroup custGroup, List regionGridIds, String headId) { List memberList = new ArrayList<>(); - if (gridImportDTO.getRegionGridIds() == null || gridImportDTO.getRegionGridIds().isEmpty()) { - throw new ServiceException("请选择地理网格"); + if (regionGridIds == null || regionGridIds.isEmpty()) { + return memberList; } - // 直接根据网格ID列表批量查询所有客户(SQL中直接拼接headId,绕过MyBatis-Plus拦截器) - List custUsers = regionGridListService.selectAllCustByGridIds(gridImportDTO.getRegionGridIds(), headId); + + List custUsers = regionGridListService.selectAllCustByGridIds(regionGridIds, headId); for (RegionCustUser custUser : custUsers) { - CustGroupMember member = new CustGroupMember(); - member.setGroupId(custGroup.getId()); - member.setCustId(custUser.getCustId()); - member.setCustName(custUser.getCustName()); - member.setCustType(custUser.getCustType()); - member.setCreateTime(new Date()); - memberList.add(member); + String[] userNames = custUser.getUserNames() != null ? custUser.getUserNames().split(",") : new String[0]; + String[] nickNames = custUser.getNickNames() != null ? custUser.getNickNames().split(",") : new String[0]; + String[] outletIds = custUser.getOutletIds() != null ? custUser.getOutletIds().split(",") : new String[0]; + String[] outletNames = custUser.getOutletNames() != null ? custUser.getOutletNames().split(",") : new String[0]; + String[] branchIds = custUser.getBranchIds() != null ? custUser.getBranchIds().split(",") : new String[0]; + String[] branchNames = custUser.getBranchNames() != null ? custUser.getBranchNames().split(",") : new String[0]; + + int count = Math.max(userNames.length, 1); + for (int i = 0; i < count; i++) { + CustGroupMember member = new CustGroupMember(); + member.setGroupId(custGroup.getId()); + member.setCustId(custUser.getCustId()); + member.setCustType(custUser.getCustType()); + member.setUserName(i < userNames.length ? userNames[i] : null); + member.setNickName(i < nickNames.length ? nickNames[i] : null); + member.setOutletId(i < outletIds.length && StringUtils.isNotEmpty(outletIds[i]) ? Long.valueOf(outletIds[i]) : null); + member.setOutletName(i < outletNames.length ? outletNames[i] : null); + member.setBranchId(i < branchIds.length && StringUtils.isNotEmpty(branchIds[i]) ? Long.valueOf(branchIds[i]) : null); + member.setBranchName(i < branchNames.length ? branchNames[i] : null); + memberList.add(member); + } } return memberList; } /** - * 从绘制网格导入客户 + * 从绘制网格查询管户关系 */ - private List importFromDrawGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) { + private List queryRelationshipsFromDrawGrid(CustGroup custGroup, List drawGridIds, String headId) { List memberList = new ArrayList<>(); - if (gridImportDTO.getDrawGridIds() == null || gridImportDTO.getDrawGridIds().isEmpty()) { - throw new ServiceException("请选择绘制网格"); + if (drawGridIds == null || drawGridIds.isEmpty()) { + return memberList; } - // 使用 selectCustByDrawGridId 方法(直接在SQL中拼接headId,绕过拦截器) - for (Long gridId : gridImportDTO.getDrawGridIds()) { + + for (Long gridId : drawGridIds) { List custUsers = drawGridShapeRelateMapper.selectCustByDrawGridId(gridId, headId); if (custUsers != null && !custUsers.isEmpty()) { for (RegionCustUser custUser : custUsers) { - CustGroupMember member = new CustGroupMember(); - member.setGroupId(custGroup.getId()); - member.setCustId(custUser.getCustId()); - member.setCustName(custUser.getCustName()); - member.setCustType(custUser.getCustType()); - member.setCreateTime(new Date()); - memberList.add(member); + String[] userNames = custUser.getUserNames() != null ? custUser.getUserNames().split(",") : new String[0]; + String[] nickNames = custUser.getNickNames() != null ? custUser.getNickNames().split(",") : new String[0]; + String[] outletIds = custUser.getOutletIds() != null ? custUser.getOutletIds().split(",") : new String[0]; + String[] outletNames = custUser.getOutletNames() != null ? custUser.getOutletNames().split(",") : new String[0]; + String[] branchIds = custUser.getBranchIds() != null ? custUser.getBranchIds().split(",") : new String[0]; + String[] branchNames = custUser.getBranchNames() != null ? custUser.getBranchNames().split(",") : new String[0]; + + int count = Math.max(userNames.length, 1); + for (int i = 0; i < count; i++) { + CustGroupMember member = new CustGroupMember(); + member.setGroupId(custGroup.getId()); + member.setCustId(custUser.getCustId()); + member.setCustType(custUser.getCustType()); + member.setUserName(i < userNames.length ? userNames[i] : null); + member.setNickName(i < nickNames.length ? nickNames[i] : null); + member.setOutletId(i < outletIds.length && StringUtils.isNotEmpty(outletIds[i]) ? Long.valueOf(outletIds[i]) : null); + member.setOutletName(i < outletNames.length ? outletNames[i] : null); + member.setBranchId(i < branchIds.length && StringUtils.isNotEmpty(branchIds[i]) ? Long.valueOf(branchIds[i]) : null); + member.setBranchName(i < branchNames.length ? branchNames[i] : null); + memberList.add(member); + } } } } return memberList; } + + @Override + public List getAllGroupTags() { + // 查询所有已有标签(逗号分隔的字符串) + List tagStrings = custGroupMapper.selectAllGroupTags(); + if (tagStrings == null || tagStrings.isEmpty()) { + return new ArrayList<>(); + } + + // 将逗号分隔的标签拆分并去重 + Set tagSet = new LinkedHashSet<>(); + for (String tagString : tagStrings) { + if (StringUtils.isNotEmpty(tagString)) { + String[] tags = tagString.split(","); + for (String tag : tags) { + String trimmedTag = tag.trim(); + if (StringUtils.isNotEmpty(trimmedTag)) { + tagSet.add(trimmedTag); + } + } + } + } + + return new ArrayList<>(tagSet); + } } diff --git a/ibs-group/src/main/java/com/ruoyi/group/service/impl/GroupPerformanceServiceImpl.java b/ibs-group/src/main/java/com/ruoyi/group/service/impl/GroupPerformanceServiceImpl.java new file mode 100644 index 0000000..7f8dbfa --- /dev/null +++ b/ibs-group/src/main/java/com/ruoyi/group/service/impl/GroupPerformanceServiceImpl.java @@ -0,0 +1,104 @@ +package com.ruoyi.group.service.impl; + +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825; +import com.ruoyi.group.domain.entity.GroupCustCountGongsi825; +import com.ruoyi.group.domain.entity.GroupCustCountLingshou825; +import com.ruoyi.group.mapper.GroupPerformanceMapper; +import com.ruoyi.group.service.IGroupPerformanceService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; + +@Service +public class GroupPerformanceServiceImpl implements IGroupPerformanceService { + + @Resource + private GroupPerformanceMapper groupPerformanceMapper; + + @Override + public List selectLsCountList(String dt, String groupName) { + return groupPerformanceMapper.selectLsCountList(buildLsCountParams(dt, groupName)); + } + + private HashMap buildLsCountParams(String dt, String groupName) { + HashMap paramMap = new HashMap<>(); + if (SecurityUtils.userRole().equals("branch")) { + paramMap.put("isBranch", true); + } + if (SecurityUtils.userRole().equals("outlet")) { + paramMap.put("isOutlet", true); + } + if (SecurityUtils.userRole().equals("manager")) { + paramMap.put("isManager", true); + } + paramMap.put("deptId", SecurityUtils.getDeptId()); + paramMap.put("userName", SecurityUtils.getUsername()); + paramMap.put("dt", dt); + paramMap.put("groupName", groupName); + return paramMap; + } + + @Override + public List selectGsCountList(String dt, String groupName) { + HashMap paramMap = new HashMap<>(); + paramMap.put("userRole", "head"); + if (SecurityUtils.userRole().equals("branch")) { + paramMap.put("userRole", "branch"); + paramMap.put("isBranch", true); + } + if (SecurityUtils.userRole().equals("outlet")) { + paramMap.put("userRole", "outlet"); + paramMap.put("isOutlet", true); + } + if (SecurityUtils.userRole().equals("manager")) { + paramMap.put("userRole", "manager"); + paramMap.put("isManager", true); + } + paramMap.put("deptId", SecurityUtils.getDeptId()); + paramMap.put("userName", SecurityUtils.getUsername()); + paramMap.put("dt", dt); + paramMap.put("groupName", groupName); + return groupPerformanceMapper.selectGsCountList(paramMap); + } + + @Override + public List selectLsCustList(String groupId, String custName, String custIdc, String dt) { + return groupPerformanceMapper.selectLsCustList(buildLsCustParams(groupId, custName, custIdc, dt)); + } + + private HashMap buildLsCustParams(String groupId, String custName, String custIdc, String dt) { + HashMap paramMap = new HashMap<>(); + paramMap.put("deptId", SecurityUtils.getDeptId()); + paramMap.put("userName", SecurityUtils.getUsername()); + paramMap.put("groupId", groupId); + paramMap.put("custName", custName); + paramMap.put("custIdc", custIdc); + paramMap.put("dt", dt); + return paramMap; + } + + @Override + public List selectGsCustList(String groupId, String custName, String socialCreditCode, String dt) { + HashMap paramMap = new HashMap<>(); + if (SecurityUtils.userRole().equals("branch")) { + paramMap.put("isBranch", true); + } + if (SecurityUtils.userRole().equals("outlet")) { + paramMap.put("isOutlet", true); + } + if (SecurityUtils.userRole().equals("manager")) { + paramMap.put("isManager", true); + } + paramMap.put("deptId", SecurityUtils.getDeptId()); + paramMap.put("userName", SecurityUtils.getUsername()); + paramMap.put("groupId", groupId); + paramMap.put("custName", custName); + paramMap.put("socialCreditCode", socialCreditCode); + paramMap.put("dt", dt); + return groupPerformanceMapper.selectGsCustList(paramMap); + } +} diff --git a/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml b/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml index 8b55004..9240226 100644 --- a/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml +++ b/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml @@ -40,13 +40,14 @@ cg.id, cg.group_name, cg.group_mode, - cg.create_mode, + cg.group_type, cg.user_name, cg.nick_name, cg.dept_id, cg.share_enabled, cg.share_dept_ids, cg.group_status, + cg.group_tags, cg.create_by, cg.create_time, cg.update_by, @@ -65,12 +66,12 @@ AND cg.group_mode = #{dto.groupMode} - - AND cg.create_mode = #{dto.createMode} - AND cg.group_status = #{dto.groupStatus} + + AND cg.group_tags LIKE CONCAT('%', #{dto.groupTags}, '%') + ORDER BY cg.create_time DESC @@ -80,13 +81,14 @@ cg.id, cg.group_name, cg.group_mode, - cg.create_mode, + cg.group_type, cg.user_name, cg.nick_name, cg.dept_id, cg.share_enabled, cg.share_dept_ids, cg.group_status, + cg.group_tags, cg.valid_time, cg.create_by, cg.create_time, @@ -95,8 +97,6 @@ cg.remark, cg.create_status, cg.grid_type, - cg.cmpm_biz_type, - cg.grid_user_names, cg.region_grid_ids, cg.draw_grid_ids, (SELECT COUNT(*) FROM ibs_cust_group_member cgm WHERE cgm.group_id = cg.id AND cgm.del_flag = '0') AS cust_count @@ -116,4 +116,14 @@ + + diff --git a/ibs-group/src/main/resources/mapper/group/CustGroupMemberMapper.xml b/ibs-group/src/main/resources/mapper/group/CustGroupMemberMapper.xml index 4832aea..43b059b 100644 --- a/ibs-group/src/main/resources/mapper/group/CustGroupMemberMapper.xml +++ b/ibs-group/src/main/resources/mapper/group/CustGroupMemberMapper.xml @@ -13,6 +13,12 @@ cgm.cust_name, cgm.cust_idc, cgm.social_credit_code, + cgm.user_name, + cgm.nick_name, + cgm.outlet_id, + cgm.outlet_name, + cgm.branch_id, + cgm.branch_name, cgm.create_time FROM ibs_cust_group_member cgm @@ -24,17 +30,66 @@ AND cgm.cust_name LIKE CONCAT('%', #{dto.custName}, '%') + + AND cgm.user_name = #{dto.userName} + + + AND cgm.nick_name LIKE CONCAT('%', #{dto.nickName}, '%') + ORDER BY cgm.create_time ASC + + + + + + + + + INSERT IGNORE INTO ibs_cust_group_member - (group_id, cust_type, cust_id, cust_name, cust_idc, social_credit_code, create_by, create_time, del_flag, manual_remove) + (group_id, cust_type, cust_id, cust_name, cust_idc, social_credit_code, + user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name, + create_by, create_time, del_flag, manual_remove) VALUES - (#{item.groupId}, #{item.custType}, #{item.custId}, #{item.custName}, #{item.custIdc}, #{item.socialCreditCode}, #{item.createBy}, NOW(), '0', '0') + (#{item.groupId}, #{item.custType}, #{item.custId}, #{item.custName}, #{item.custIdc}, #{item.socialCreditCode}, + #{item.userName}, #{item.nickName}, #{item.outletId}, #{item.outletName}, #{item.branchId}, #{item.branchName}, + #{item.createBy}, NOW(), '0', '0') diff --git a/ibs-group/src/main/resources/mapper/group/GroupPerformanceMapper.xml b/ibs-group/src/main/resources/mapper/group/GroupPerformanceMapper.xml new file mode 100644 index 0000000..24520a3 --- /dev/null +++ b/ibs-group/src/main/resources/mapper/group/GroupPerformanceMapper.xml @@ -0,0 +1,245 @@ + + + + + + + + + + + + + diff --git a/ibs/src/main/java/com/ruoyi/ibs/cmpm/controller/GridCmpmController.java b/ibs/src/main/java/com/ruoyi/ibs/cmpm/controller/GridCmpmController.java index f766302..99f2176 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/cmpm/controller/GridCmpmController.java +++ b/ibs/src/main/java/com/ruoyi/ibs/cmpm/controller/GridCmpmController.java @@ -118,13 +118,4 @@ public class GridCmpmController extends BaseController { return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ; } - /** - * 根据网格类型获取客户经理列表 - */ - @GetMapping("/managerList") - @Log(title = "绩效网格-获取客户经理列表") - @ApiOperation("获取客户经理列表") - public AjaxResult getManagerListByGridType(@RequestParam String gridType) { - return AjaxResult.success(gridCmpmService.getManagerListByGridType(gridType)); - } } diff --git a/ibs/src/main/java/com/ruoyi/ibs/cmpm/mapper/GridCmpmMapper.java b/ibs/src/main/java/com/ruoyi/ibs/cmpm/mapper/GridCmpmMapper.java index 67b7253..73eb613 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/cmpm/mapper/GridCmpmMapper.java +++ b/ibs/src/main/java/com/ruoyi/ibs/cmpm/mapper/GridCmpmMapper.java @@ -65,30 +65,21 @@ public interface GridCmpmMapper { List selectManagerList(); /** - * 根据网格类型和总行ID查询客户经理列表 - * @param gridType 网格类型(零售retail、对公corporate、对公账户corporate_account) + * 全量查询绩效网格客户列表(用于客群管户关系匹配,不按客户经理过滤) + * @param gridType 网格类型 * @param headId 总行ID - * @return 客户经理列表(user_name, nick_name) + * @return 客户列表(包含管户关系信息) */ - List> getManagerListByGridType(@Param("gridType") String gridType, @Param("headId") String headId); + List getAllCustomerListForImport(@Param("gridType") String gridType, @Param("headId") String headId); /** - * 根据网格类型、总行ID和客户经理查询客户列表(分表查询,适用于客群导入) - * @param gridType 网格类型 - * @param userName 客户经理柜员号 + * 根据客户ID和类型列表查询管户关系(用于动态客群更新管户关系) + * @param gridType 网格类型(retail/corporate) * @param headId 总行ID - * @return 客户列表 + * @param custInfoList 客户信息列表(包含custId和custType) + * @return 客户管户关系列表 */ - List getCustomerListForImport(@Param("gridType") String gridType, @Param("userName") String userName, @Param("headId") String headId); - - /** - * 根据网格类型、总行ID和客户经理流式查询客户列表(使用Cursor,适用于大数据量场景) - * @param gridType 网格类型 - * @param userName 客户经理柜员号 - * @param headId 总行ID - * @return 客户列表游标 - */ - Cursor getCustomerListForImportCursor(@Param("gridType") String gridType, @Param("userName") String userName, @Param("headId") String headId); + List getRelationshipsByCustList(@Param("gridType") String gridType, @Param("headId") String headId, @Param("custInfoList") List> custInfoList); // List selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo); diff --git a/ibs/src/main/java/com/ruoyi/ibs/cmpm/service/GridCmpmService.java b/ibs/src/main/java/com/ruoyi/ibs/cmpm/service/GridCmpmService.java index 2ed9af2..4236964 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/cmpm/service/GridCmpmService.java +++ b/ibs/src/main/java/com/ruoyi/ibs/cmpm/service/GridCmpmService.java @@ -303,25 +303,27 @@ public class GridCmpmService { } /** - * 根据网格类型和总行ID查询客户经理列表 - * @param gridType 网格类型(零售retail、对公corporate、对公账户corporate_account) - * @return 客户经理列表 + * 全量查询绩效网格客户列表(用于客群管户关系匹配,不依赖SecurityUtils) + * @param gridType 网格类型 + * @param headId 总行ID + * @return 客户列表(包含管户关系信息) */ - public List> getManagerListByGridType(String gridType) { - String headId = SecurityUtils.getHeadId(); - return gridCmpmMapper.getManagerListByGridType(gridType, headId); + public List selectAllForImport(String gridType, String headId) { + return gridCmpmMapper.getAllCustomerListForImport(gridType, headId); } /** - * 根据参数查询绩效网格客户列表(不依赖SecurityUtils,适用于异步线程) - * 所有参数由调用者传入,不在方法内部获取用户上下文 - * @param gridType 网格类型 - * @param userName 客户经理柜员号 + * 根据客户ID列表查询管户关系(用于动态客群更新管户关系) + * @param gridType 网格类型(retail/corporate) * @param headId 总行ID - * @return 客户列表 + * @param custInfoList 客户信息列表(包含custId和custType) + * @return 客户管户关系列表 */ - public List selectManageListForImport(String gridType, String userName, String headId) { - return gridCmpmMapper.getCustomerListForImport(gridType, userName, headId); + public List getRelationshipsByCustList(String gridType, String headId, List> custInfoList) { + if (custInfoList == null || custInfoList.isEmpty()) { + return new ArrayList<>(); + } + return gridCmpmMapper.getRelationshipsByCustList(gridType, headId, custInfoList); } } diff --git a/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCmpmCountLingshouNew825.java b/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCmpmCountLingshouNew825.java index 844762f..68d103b 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCmpmCountLingshouNew825.java +++ b/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCmpmCountLingshouNew825.java @@ -100,7 +100,7 @@ public class GridCmpmCountLingshouNew825 implements Serializable { private Integer yxxykNum; @Excel(name = "有效社保卡户数") private Integer yxsbkNum; - @Excel(name = "二换三社保卡户数") + @Excel(name = "有效社保卡新增") private Integer twoTo3SbkNum; @Excel(name = "养老金迁移至社保卡户数") private Integer yljToSbkNum; @@ -108,6 +108,8 @@ public class GridCmpmCountLingshouNew825 implements Serializable { private Integer fshlNum; @Excel(name = "有效收单商户") private Integer yxsdNum; + @Excel(name = "核心收单户数") + private Integer hxsdNum; private String regionCode; private String opsDept; } diff --git a/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCustCountLingshouNew825.java b/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCustCountLingshouNew825.java index c9a192b..d6f925b 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCustCountLingshouNew825.java +++ b/ibs/src/main/java/com/ruoyi/ibs/grid/domain/entity/GridCustCountLingshouNew825.java @@ -56,10 +56,12 @@ public class GridCustCountLingshouNew825 implements Serializable { private String isCfyx; @Excel(name = "是否有效社保卡客户") private String isYxsbk; - @Excel(name = "是否二换三社保卡") + @Excel(name = "是否有效社保卡新增") private String is2to3Sbk; @Excel(name = "是否养老金迁移至社保卡") private String isYljToSbk; + @Excel(name = "是否核心收单") + private String isHxsd; private String regionCode; private String opsDept; private String custType; diff --git a/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml b/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml index bef24dc..b492b34 100644 --- a/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml +++ b/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml @@ -505,26 +505,20 @@ where manager_id is not null - - + select cust_id, cust_type, user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name from grid_cmpm_${gridType}_${headId} - where user_name is not null - order by user_name - - + select cust_id, cust_type, user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name from grid_cmpm_${gridType}_${headId} - where user_name = #{userName} - - - - \ No newline at end of file diff --git a/ibs/src/main/resources/mapper/grid/GridCountMapper.xml b/ibs/src/main/resources/mapper/grid/GridCountMapper.xml index 3c529fc..eebea87 100644 --- a/ibs/src/main/resources/mapper/grid/GridCountMapper.xml +++ b/ibs/src/main/resources/mapper/grid/GridCountMapper.xml @@ -27,7 +27,7 @@ zf_30rt, cur_bal_d, sx_rat, yx_rat, sx_num, yx_num, sx_bal, bal_loan, loan_ave, yxht_rat, dian_rat, shui_rat, tax_rat, open_rat, yxht_num, dian_num, shui_num, tax_num, open_num, dep_bal, fin_bal, grhx_num, cfyx_num, yxxyk_num, yxsbk_num, `2to3_sbk_num` as twoTo3SbkNum, ylj_to_sbk_num, - fshl_num, yxsd_num, region_code, ops_dept + fshl_num, yxsd_num, hxsd_num, region_code, ops_dept FROM grid_cmpm_count_lingshou_new_825 and dt = #{dt} @@ -102,6 +102,7 @@ is_yxsbk, is_2to3_sbk, is_ylj_to_sbk, + is_hxsd, region_code, ops_dept, cust_type, diff --git a/ruoyi-ui/src/api/group/custGroup.js b/ruoyi-ui/src/api/group/custGroup.js index 909708c..4464e86 100644 --- a/ruoyi-ui/src/api/group/custGroup.js +++ b/ruoyi-ui/src/api/group/custGroup.js @@ -17,15 +17,6 @@ export function getCustGroup(id) { }) } -// 异步创建客群(网格导入) -export function createCustGroupByGrid(data) { - return request({ - url: '/group/cust/createByGrid', - method: 'post', - data: data - }) -} - // 异步创建客群(模板导入) export function createCustGroupByTemplate(data) { return request({ @@ -36,24 +27,6 @@ export function createCustGroupByTemplate(data) { }) } -// 更新客群 -export function updateCustGroup(data) { - return request({ - url: '/group/cust/update', - method: 'post', - data: data - }) -} - -// 更新客群(网格导入) -export function updateCustGroupByGrid(data) { - return request({ - url: '/group/cust/updateByGrid', - method: 'post', - data: data - }) -} - // 更新客群(模板导入) export function updateCustGroupByTemplate(data) { return request({ @@ -100,24 +73,6 @@ export function removeMembers(groupId, memberIds) { }) } -// 检查客群名称是否存在 -export function checkGroupNameExist(groupName) { - return request({ - url: '/group/cust/checkName', - method: 'get', - params: { groupName: groupName } - }) -} - -// 根据网格类型获取客户经理列表 -export function getManagerList(gridType) { - return request({ - url: '/grid/cmpm/managerList', - method: 'get', - params: { gridType: gridType } - }) -} - // 分页查询客群成员列表 export function listCustGroupMembers(groupId, query) { return request({ @@ -135,3 +90,11 @@ export function getRegionGridListForGroup(query) { params: query }) } + +// 获取所有客群标签列表 +export function getAllGroupTags() { + return request({ + url: '/group/cust/tags', + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/group/performance.js b/ruoyi-ui/src/api/group/performance.js new file mode 100644 index 0000000..5ef9e6f --- /dev/null +++ b/ruoyi-ui/src/api/group/performance.js @@ -0,0 +1,69 @@ +import request from '@/utils/request'; + +export function getGroupPerformanceLsList(params) { + return request({ + url: '/group/performance/ls/list', + method: 'get', + params + }); +} + +export function getGroupPerformanceGsList(params) { + return request({ + url: '/group/performance/gs/list', + method: 'get', + params + }); +} + +export function getGroupPerformanceLsCustList(params) { + return request({ + url: '/group/performance/ls/custList', + method: 'get', + params + }); +} + +export function getGroupPerformanceGsCustList(params) { + return request({ + url: '/group/performance/gs/custList', + method: 'get', + params + }); +} + +export function exportGroupPerformanceLs(params) { + return request({ + url: '/group/performance/exportLs', + method: 'get', + params, + responseType: 'blob' + }); +} + +export function exportGroupPerformanceGs(params) { + return request({ + url: '/group/performance/exportGs', + method: 'get', + params, + responseType: 'blob' + }); +} + +export function exportGroupPerformanceLsCust(params) { + return request({ + url: '/group/performance/exportLsCust', + method: 'get', + params, + responseType: 'blob' + }); +} + +export function exportGroupPerformanceGsCust(params) { + return request({ + url: '/group/performance/exportGsCust', + method: 'get', + params, + responseType: 'blob' + }); +} diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js index b8afced..1b47c40 100644 --- a/ruoyi-ui/src/router/index.js +++ b/ruoyi-ui/src/router/index.js @@ -238,6 +238,28 @@ export const constantRoutes = [ component: () => import('@/views/group/custGroup/detail') }] }, + { + path: '/center/groupPerformance/list', + component: Layout, + hidden: true, + children: [{ + path: '/center/groupPerformance/list', + name: 'GroupPerformanceList', + meta: { title: '客群业绩统计', activeMenu: '/center/groupPerformance/list' }, + component: () => import('@/views/group/performance/list') + }] + }, + { + path: '/center/groupPerformance/custom', + component: Layout, + hidden: true, + children: [{ + path: '/center/groupPerformance/custom', + name: 'GroupPerformanceCustom', + meta: { title: '客群客户明细', activeMenu: '/center/groupPerformance/list' }, + component: () => import('@/views/group/performance/custom') + }] + }, { path: '/checklist/customerlist', component: Layout, @@ -353,8 +375,8 @@ export const constantRoutes = [ meta: { title: '个人中心', icon: 'user' } } ] - }, - ]; + } +]; // 动态路由,基于用户权限动态去加载 export const dynamicRoutes = [ diff --git a/ruoyi-ui/src/views/customer/charts/360charts/commercial/index.vue b/ruoyi-ui/src/views/customer/charts/360charts/commercial/index.vue index 125ad6c..be507a1 100644 --- a/ruoyi-ui/src/views/customer/charts/360charts/commercial/index.vue +++ b/ruoyi-ui/src/views/customer/charts/360charts/commercial/index.vue @@ -432,7 +432,7 @@ v-if="item.cmpmType == '贷款客户经理'" :label="`${item.cmpmType}:`" > - {{ item.cmpmUserList }} + {{ is932Dept ? `${baseForm.belongUserName}-${baseForm.belongUserId}` : item.cmpmUserList }} @@ -1242,6 +1242,7 @@ export default { businessScope: '', gridUserName: '', belongUserName: '', + belongUserId: '', updateTime: '' }, registerLocationNum: '', @@ -1374,7 +1375,7 @@ export default { Custom }, computed: { - ...mapGetters(['roles', 'userName']), + ...mapGetters(['roles', 'userName', 'deptId']), isHeadAdmin() { return this.roles.includes('headAdmin') }, @@ -1386,6 +1387,10 @@ export default { isPrivate() { return this.roles.includes('headPrivate') }, + // 是否932开头部门 + is932Dept() { + return String(this.deptId || '').substring(0, 3) === '932' + }, // 运管部 isHeadOps() { return this.roles.includes('headOps') diff --git a/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue b/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue index 9ff7cbf..b56cfdb 100644 --- a/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue +++ b/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue @@ -437,7 +437,7 @@ v-if="item.cmpmType == '贷款客户经理'" :label="`${item.cmpmType}:`" > - {{ item.cmpmUserList }} + {{ is932Dept ? `${baseForm.belongUserName}-${baseForm.belongUserId}` : item.cmpmUserList }} @@ -1460,6 +1460,7 @@ export default { tel: '', gridUserName: '', belongUserName: '', + belongUserId: '', updateTime: '' }, registerLocationNum: '', @@ -1663,6 +1664,9 @@ export default { }, is825() { return String(this.deptId || '').substring(0, 3) === '825' + }, + is932Dept() { + return String(this.deptId || '').substring(0, 3) === '932' } }, created() { diff --git a/ruoyi-ui/src/views/customer/charts/360charts/indexcharts/index.vue b/ruoyi-ui/src/views/customer/charts/360charts/indexcharts/index.vue index 025bb83..0190211 100644 --- a/ruoyi-ui/src/views/customer/charts/360charts/indexcharts/index.vue +++ b/ruoyi-ui/src/views/customer/charts/360charts/indexcharts/index.vue @@ -364,7 +364,7 @@ v-if="item.cmpmType == '贷款客户经理'" :label="`${item.cmpmType}:`" > - {{ item.cmpmUserList }} + {{ is932Dept ? `${profile.belongUserName}-${profile.belongUserId}` : item.cmpmUserList }} @@ -1663,7 +1663,7 @@ export default { CustContact }, computed: { - ...mapGetters(['roles', 'userName']), + ...mapGetters(['roles', 'userName', 'deptId']), isHeadAdmin() { return this.roles.includes('headAdmin') }, @@ -1675,6 +1675,10 @@ export default { isPrivate() { return this.roles.includes('headPrivate') }, + // 是否932开头部门 + is932Dept() { + return String(this.deptId || '').substring(0, 3) === '932' + }, // 运管部 isHeadOps() { return this.roles.includes('headOps') diff --git a/ruoyi-ui/src/views/grid/performance/list/list.js b/ruoyi-ui/src/views/grid/performance/list/list.js index 2694d65..1c97749 100644 --- a/ruoyi-ui/src/views/grid/performance/list/list.js +++ b/ruoyi-ui/src/views/grid/performance/list/list.js @@ -235,10 +235,11 @@ export const privateColumnsNew825 = [ { prop: 'cfyxNum', label: '财富有效客户数', width: 150 }, { prop: 'yxxykNum', label: '有效信用卡数', width: 140 }, { prop: 'yxsbkNum', label: '有效社保卡户数', width: 150 }, - { prop: 'twoTo3SbkNum', label: '二换三社保卡户数', width: 160 }, + { prop: 'twoTo3SbkNum', label: '有效社保卡新增', width: 160 }, { prop: 'yljToSbkNum', label: '养老金迁移至社保卡户数', width: 180 }, { prop: 'fshlNum', label: '丰收互联客户数', width: 150 }, { prop: 'yxsdNum', label: '有效收单商户', width: 140 }, + { prop: 'hxsdNum', label: '核心收单户数', width: 140 }, { prop: 'zf365rt', label: '近365天走访率', width: 140 }, { prop: 'zf180rt', label: '近180天走访率', width: 140 }, { prop: 'zf90rt', label: '近90天走访率', width: 140 }, @@ -638,8 +639,9 @@ export const privateTotalColumnsNew825 = [ { prop: 'isGrhx', label: '是否个人核心客户', width: 140 }, { prop: 'isCfyx', label: '是否财富有效客户', width: 140 }, { prop: 'isYxsbk', label: '是否有效社保卡客户', width: 150 }, - { prop: 'is2to3Sbk', label: '是否二换三社保卡', width: 150 }, + { prop: 'is2to3Sbk', label: '是否有效社保卡新增', width: 150 }, { prop: 'isYljToSbk', label: '是否养老金迁移至社保卡', width: 180 }, + { prop: 'isHxsd', label: '是否核心收单', width: 120 }, { prop: 'is365zf', label: '近365天有无走访', width: 140 }, { prop: 'is180zf', label: '近180天有无走访', width: 140 }, { prop: 'is90zf', label: '近90天有无走访', width: 140 }, diff --git a/ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue b/ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue index 71f0427..15463a6 100644 --- a/ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue +++ b/ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue @@ -22,11 +22,22 @@ 静态客群 动态客群 -
- 静态客群:创建后客户列表固定,除非手动更新 + + + + + 零售类 + 公司类 + + + +
+ + +
+
+ 已有标签: + + {{ tag }} + +
+
+ 已选标签: + + {{ tag }} + +
+
+ - - - 模板导入 - 绩效网格 - 地理网格 - 绘制网格 - + + + + 选择文件 + + 下载模板 + +
+ 仅支持Excel文件,文件大小不超过10MB +
+
- - + + + 绩效网格 + + +
+ 系统将根据客户信息从所选网格匹配管户关系,匹配不上则保存空的管户关系 +
+
- - - - -