From dd4c2fc1df70eee55ed845ae35874cb069951fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=B9=90=E8=A8=80?= Date: Fri, 10 Apr 2026 17:40:38 +0800 Subject: [PATCH] =?UTF-8?q?0410-=E6=B5=B7=E5=AE=81=E9=A2=84=E8=AD=A6?= =?UTF-8?q?=E8=BD=AC=E5=8F=91+=E5=8C=97=E4=BB=91=E5=AE=A2=E7=BE=A4?= =?UTF-8?q?=E4=BC=98=E5=8C=96+=E6=B5=B7=E5=AE=81aum=E6=8A=A5=E8=A1=A8?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/dto/CustGroupMemberQueryDTO.java | 18 + .../group/domain/dto/CustGroupQueryDTO.java | 6 + .../ruoyi/group/mapper/CustGroupMapper.java | 20 +- .../impl/CustGroupMemberServiceImpl.java | 12 +- .../service/impl/CustGroupServiceImpl.java | 52 ++- .../mapper/group/CustGroupMapper.xml | 50 +- .../mapper/group/CustGroupMemberMapper.xml | 11 + .../cmpm/controller/GridCmpmController.java | 12 + .../vo/DwbRetailCustLevelManagerDetailVO.java | 21 +- .../ruoyi/ibs/cmpm/mapper/GridCmpmMapper.java | 5 + .../ibs/cmpm/service/GridCmpmService.java | 125 ++++- .../task/controller/WorkRecordController.java | 32 +- .../task/domain/dto/AlterForwardRequest.java | 23 + .../ibs/task/domain/dto/ForwardTarget.java | 19 + .../domain/entity/AlterAssignHistory.java | 53 +++ .../ibs/task/domain/entity/WorkRecord.java | 4 + .../task/domain/vo/AlterAssignTargetVO.java | 14 + .../task/domain/vo/AlterForwardMetaVO.java | 32 ++ .../task/domain/vo/AlterForwardResultVO.java | 14 + .../com/ruoyi/ibs/task/domain/vo/AlterVO.java | 16 + .../task/mapper/AlterAssignHistoryMapper.java | 9 + .../ibs/task/mapper/WorkRecordMapper.java | 20 +- .../ibs/task/service/WorkRecordService.java | 19 +- .../service/impl/WorkRecordServiceImpl.java | 437 +++++++++++++++++- .../resources/mapper/WorkRecordMapper.xml | 147 +++++- .../resources/mapper/cmpm/GridCmpmMapper.xml | 32 +- .../src/main/resources/application-dev.yml | 10 + .../com/ruoyi/system/enums/OssFileEnum.java | 4 +- .../gridSearch/accountManageReport/index.js | 10 +- ruoyi-ui/src/api/system/home.js | 20 +- ruoyi-ui/src/layout/components/Navbar.vue | 138 +++++- .../gridSearch/accountManageReport/index.vue | 18 +- .../custGroup/components/create-dialog.vue | 9 +- ruoyi-ui/src/views/group/custGroup/detail.vue | 53 ++- ruoyi-ui/src/views/group/custGroup/index.vue | 50 +- .../src/views/workmng/warningtask/index.vue | 367 +++++++++------ 36 files changed, 1632 insertions(+), 250 deletions(-) create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/domain/dto/AlterForwardRequest.java create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/domain/dto/ForwardTarget.java create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/domain/entity/AlterAssignHistory.java create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/domain/vo/AlterAssignTargetVO.java create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/domain/vo/AlterForwardMetaVO.java create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/domain/vo/AlterForwardResultVO.java create mode 100644 ibs/src/main/java/com/ruoyi/ibs/task/mapper/AlterAssignHistoryMapper.java 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 d45ecff..d8bbefb 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 @@ -48,4 +48,22 @@ public class CustGroupMemberQueryDTO { */ @ApiModelProperty(value = "客户经理姓名") private String nickName; + + /** + * 当前用户角色 + */ + @ApiModelProperty(value = "当前用户角色", hidden = true) + private String userRole; + + /** + * 当前用户名 + */ + @ApiModelProperty(value = "当前用户名", hidden = true) + private String currentUserName; + + /** + * 当前机构ID + */ + @ApiModelProperty(value = "当前机构ID", hidden = true) + private Long currentDeptId; } 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 af398f1..75665c7 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 @@ -51,6 +51,12 @@ public class CustGroupQueryDTO implements Serializable { @ApiModelProperty(value = "视图类型", name = "viewType") private String viewType; + /** + * 当前用户是否属于总行管理员口径 + */ + @ApiModelProperty(value = "当前用户是否总行管理员", hidden = true) + private Boolean headRole; + /** * 页码 */ 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 d1b9495..a27c1f8 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 @@ -34,7 +34,8 @@ public interface CustGroupMapper extends BaseMapper { */ List selectCustGroupList(@Param("dto") CustGroupQueryDTO dto, @Param("userName") String userName, - @Param("deptId") String deptId); + @Param("deptId") String deptId, + @Param("headId") String headId); /** * 根据ID查询客群详情 @@ -44,7 +45,9 @@ public interface CustGroupMapper extends BaseMapper { */ CustGroupVO selectCustGroupById(@Param("id") Long id, @Param("userName") String userName, - @Param("deptId") String deptId); + @Param("deptId") String deptId, + @Param("headRole") Boolean headRole, + @Param("headId") String headId); /** * 校验当前用户是否有客群查看权限 @@ -56,7 +59,18 @@ public interface CustGroupMapper extends BaseMapper { */ Long countVisibleCustGroup(@Param("id") Long id, @Param("userName") String userName, - @Param("deptId") String deptId); + @Param("deptId") String deptId, + @Param("headRole") Boolean headRole, + @Param("headId") String headId); + + /** + * 校验客群是否属于总行管理员共享操作范围 + * + * @param id 客群ID + * @return 数量 + */ + Long countHeadOperableCustGroup(@Param("id") Long id, + @Param("headId") String headId); /** * 查询所有已有的客群标签 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 8385da3..861d7d2 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 @@ -41,6 +41,9 @@ public class CustGroupMemberServiceImpl implements ICustGroupMemberService { @Override public List listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto) { custGroupService.checkCustGroupViewPermission(groupId); + dto.setUserRole(SecurityUtils.userRole()); + dto.setCurrentUserName(SecurityUtils.getUsername()); + dto.setCurrentDeptId(SecurityUtils.getDeptId()); // 在权限检查之后启动分页,避免权限检查SQL消耗分页设置 int pageNum = dto.getPageNum() != null ? dto.getPageNum() : 1; int pageSize = dto.getPageSize() != null ? dto.getPageSize() : 10; @@ -109,7 +112,14 @@ public class CustGroupMemberServiceImpl implements ICustGroupMemberService { if (custGroup == null) { throw new ServiceException("客群不存在"); } - if (!SecurityUtils.getUsername().equals(custGroup.getUserName())) { + if (!SecurityUtils.hasRole("headAdmin") + && !SecurityUtils.hasRole("headPublic") + && !SecurityUtils.hasRole("headPrivate") + && !SecurityUtils.hasRole("headOps")) { + throw new ServiceException("无权限操作该客群"); + } + Long count = custGroupMapper.countHeadOperableCustGroup(groupId, SecurityUtils.getHeadId()); + if (count == null || count <= 0) { throw new ServiceException("无权限操作该客群"); } 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 fbcd070..bd1625f 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 @@ -67,12 +67,24 @@ public class CustGroupServiceImpl implements ICustGroupService { @Override public List listCustGroup(CustGroupQueryDTO dto) { - return custGroupMapper.selectCustGroupList(dto, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId())); + dto.setHeadRole(isHeadCustGroupAdmin()); + return custGroupMapper.selectCustGroupList( + dto, + SecurityUtils.getUsername(), + String.valueOf(SecurityUtils.getDeptId()), + SecurityUtils.getHeadId() + ); } @Override public CustGroupVO getCustGroup(Long id) { - CustGroupVO custGroup = custGroupMapper.selectCustGroupById(id, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId())); + CustGroupVO custGroup = custGroupMapper.selectCustGroupById( + id, + SecurityUtils.getUsername(), + String.valueOf(SecurityUtils.getDeptId()), + isHeadCustGroupAdmin(), + SecurityUtils.getHeadId() + ); if (custGroup == null) { throw new ServiceException("客群不存在"); } @@ -82,6 +94,7 @@ public class CustGroupServiceImpl implements ICustGroupService { @Override @Transactional(rollbackFor = Exception.class) public String createCustGroupByTemplate(CustGroup custGroup, MultipartFile file) { + assertHeadCustGroupAdmin(); // 检查客群名称是否存在 if (checkGroupNameExist(custGroup.getGroupName())) { throw new ServiceException("客群名称已存在"); @@ -117,6 +130,7 @@ public class CustGroupServiceImpl implements ICustGroupService { if (existGroup == null) { throw new ServiceException("客群不存在"); } + assertOperatePermission(existGroup); // 检查客群是否正在创建或更新 if ("0".equals(existGroup.getCreateStatus())) { throw new ServiceException("客群正在处理中,请稍后再试"); @@ -167,6 +181,11 @@ public class CustGroupServiceImpl implements ICustGroupService { throw new ServiceException("请选择要删除的客群"); } for (Long id : idList) { + CustGroup custGroup = custGroupMapper.selectById(id); + if (custGroup == null) { + throw new ServiceException("客群不存在"); + } + assertOperatePermission(custGroup); // 删除客群客户关联 LambdaQueryWrapper memberWrapper = new LambdaQueryWrapper<>(); memberWrapper.eq(CustGroupMember::getGroupId, id); @@ -196,12 +215,39 @@ public class CustGroupServiceImpl implements ICustGroupService { @Override public void checkCustGroupViewPermission(Long id) { - Long count = custGroupMapper.countVisibleCustGroup(id, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId())); + Long count = custGroupMapper.countVisibleCustGroup( + id, + SecurityUtils.getUsername(), + String.valueOf(SecurityUtils.getDeptId()), + isHeadCustGroupAdmin(), + SecurityUtils.getHeadId() + ); if (count == null || count <= 0) { throw new ServiceException("客群不存在或无查看权限"); } } + public boolean isHeadCustGroupAdmin() { + return SecurityUtils.hasRole("headAdmin") + || SecurityUtils.hasRole("headPublic") + || SecurityUtils.hasRole("headPrivate") + || SecurityUtils.hasRole("headOps"); + } + + public void assertHeadCustGroupAdmin() { + if (!isHeadCustGroupAdmin()) { + throw new ServiceException("当前用户无权限操作该功能"); + } + } + + private void assertOperatePermission(CustGroup custGroup) { + assertHeadCustGroupAdmin(); + Long count = custGroupMapper.countHeadOperableCustGroup(custGroup.getId(), SecurityUtils.getHeadId()); + if (count == null || count <= 0) { + throw new ServiceException("无权限操作该客群"); + } + } + @Override public void updateDynamicCustGroups() { log.info("开始更新动态客群..."); diff --git a/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml b/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml index 9240226..922a032 100644 --- a/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml +++ b/ibs-group/src/main/resources/mapper/group/CustGroupMapper.xml @@ -6,7 +6,22 @@ AND ( - cg.user_name = #{userName} + + + EXISTS ( + SELECT 1 + FROM sys_user su + LEFT JOIN sys_user_role sur ON su.user_id = sur.user_id + LEFT JOIN sys_role sr ON sur.role_id = sr.role_id + WHERE su.user_name = cg.user_name + AND LEFT(CAST(cg.dept_id AS CHAR), 3) = #{headId} + AND sr.role_key IN ('headAdmin', 'headPublic', 'headPrivate', 'headOps') + ) + + + 1 = 2 + + OR ( cg.share_enabled = 1 AND cg.group_status = '0' @@ -20,7 +35,22 @@ - AND cg.user_name = #{userName} + + + AND EXISTS ( + SELECT 1 + FROM sys_user su + LEFT JOIN sys_user_role sur ON su.user_id = sur.user_id + LEFT JOIN sys_role sr ON sur.role_id = sr.role_id + WHERE su.user_name = cg.user_name + AND LEFT(CAST(cg.dept_id AS CHAR), 3) = #{headId} + AND sr.role_key IN ('headAdmin', 'headPublic', 'headPrivate', 'headOps') + ) + + + AND 1 = 2 + + AND cg.share_enabled = 1 @@ -116,6 +146,22 @@ + + - select id, user_name, nick_name, alter_type, alter_detail, cust_id, cust_name, read_time, status, remark, is_feedback, cust_isn - from work_record + select wr.id, wr.user_name, wr.nick_name, wr.alter_type, wr.alter_detail, wr.cust_id, wr.cust_name, + wr.read_time, wr.status, wr.remark, wr.is_feedback, wr.cust_isn, wr.dept_id, sd.dept_name + from work_record wr + left join sys_dept sd on wr.dept_id = sd.dept_id - is_alter = 1 - and user_name= #{username} + wr.is_alter = 1 + and wr.user_name= #{username} - and status = #{status} + and wr.status = #{status} - and alter_type = #{alterType} + and wr.alter_type = #{alterType} - order by create_time desc, status asc + order by wr.create_time desc, wr.status asc + + + + + + update work_record + set user_name = null, + nick_name = null, + dept_id = #{targetDeptId}, + read_time = null, + update_time = sysdate(), + update_by = #{updateBy} + where id = #{id} + and user_name = #{sourceUserName} + and dept_id is null + and is_alter = 1 + + + + update work_record + set user_name = #{targetUserName}, + nick_name = #{targetNickName}, + alter_detail = case + when alter_detail like '[FORWARD=1] %' then concat('[FORWARD=0] ', substring(alter_detail, 13)) + when alter_detail like '[FORWARD=1]%' then concat('[FORWARD=0]', substring(alter_detail, 12)) + else alter_detail + end, + dept_id = null, + read_time = null, + update_time = sysdate(), + update_by = #{updateBy} + where id = #{id} + and user_name = #{sourceUserName} + and dept_id is null + and is_alter = 1 + + + + update work_record + set user_name = #{targetUserName}, + nick_name = #{targetNickName}, + alter_detail = case + when alter_detail like '[FORWARD=1] %' then concat('[FORWARD=0] ', substring(alter_detail, 13)) + when alter_detail like '[FORWARD=1]%' then concat('[FORWARD=0]', substring(alter_detail, 12)) + else alter_detail + end, + dept_id = null, + read_time = null, + update_time = sysdate(), + update_by = #{updateBy} + where id = #{id} + and dept_id = #{sourceDeptId} + and user_name is null + and is_alter = 1 + + - \ No newline at end of file + diff --git a/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml b/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml index b492b34..cc74cc0 100644 --- a/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml +++ b/ibs/src/main/resources/mapper/cmpm/GridCmpmMapper.xml @@ -167,6 +167,36 @@ + + - \ No newline at end of file + diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 1d98dc5..9ad790b 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -28,6 +28,10 @@ spring: username: root password: Kfcx@1234 driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://rm-bp17634n45cj631s0ao.mysql.rds.aliyuncs.com/ibs?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true +# username: znsj +# password: Znsj@123456 +# driverClassName: com.mysql.cj.jdbc.Driver # 从库数据源 slave: # 从数据源开关/默认关闭 @@ -78,12 +82,14 @@ spring: redis: # 地址 host: 116.62.17.81 +# host: r-bp1mmtcknvsscsrjrypd.redis.rds.aliyuncs.com # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # 密码 password: Kfcx@1234 +# password: N0f3d12c4a927eee1+ # 连接超时时间 timeout: 10s lettuce: @@ -121,5 +127,9 @@ oss: accessKeyId: LTAI5tMsUgorcgnpTxZDV1wS accessKeySecret: c7qIjXIPx8Cz2CriJpYGyCFwFjRxeB bucketName: oss-wkc +# endpoint: oss-cn-hangzhou.aliyuncs.comBucket:znjgoss.oss-cn-hangzhou.aliyuncs.com +# accessKeyId: LTAI5tCRocKhQaCtFnYKp46w +# accessKeySecret: 0ovFbMQWas1wOZTG91mpKbV70JgR32 +# bucketName: oss-wkc diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/enums/OssFileEnum.java b/ruoyi-system/src/main/java/com/ruoyi/system/enums/OssFileEnum.java index dae9843..9947174 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/enums/OssFileEnum.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/enums/OssFileEnum.java @@ -20,7 +20,9 @@ public enum OssFileEnum { VISIT_RECORD("VISIT_RECORD/"), - CUST_MAP_EXPORT("CUST_MAP_EXPORT/"); + CUST_MAP_EXPORT("CUST_MAP_EXPORT/"), + + CUST_MANAGER_REPORT("CUST_MANAGER_REPORT/"); private String PREFIX; diff --git a/ruoyi-ui/src/api/gridSearch/accountManageReport/index.js b/ruoyi-ui/src/api/gridSearch/accountManageReport/index.js index b7b7d19..4eaf8c8 100644 --- a/ruoyi-ui/src/api/gridSearch/accountManageReport/index.js +++ b/ruoyi-ui/src/api/gridSearch/accountManageReport/index.js @@ -22,4 +22,12 @@ export function getCustLevelList() { url: '/grid/cmpm/custManager/custLevel/list', method: 'get' }) -} \ No newline at end of file +} + +export function exportCustManagerReport(data) { + return request({ + url: '/grid/cmpm/custManager/export', + method: 'post', + data + }) +} diff --git a/ruoyi-ui/src/api/system/home.js b/ruoyi-ui/src/api/system/home.js index 3a13abd..cb4575d 100644 --- a/ruoyi-ui/src/api/system/home.js +++ b/ruoyi-ui/src/api/system/home.js @@ -204,12 +204,22 @@ export function warningworkRecordList(query) { params: query }) } -//更新预警工作清单 -export function warningworkRecordSubmit(data) { + +// 查询预警转发元数据 +export function getAlterForwardMeta(query) { return request({ - url: `/work/record/alter/edit`, + url: `/work/record/alter/forward/meta`, + method: 'get', + params: query + }) +} + +// 预警转发 +export function forwardAlter(data) { + return request({ + url: `/work/record/alter/forward`, method: 'post', - data: data, + data }) } @@ -265,4 +275,4 @@ export function getAlterTypes() { url: '/work/record/alter/types', method: 'get' }) - } \ No newline at end of file + } diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue index 6ed3caa..53ee647 100644 --- a/ruoyi-ui/src/layout/components/Navbar.vue +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -15,7 +15,14 @@
@@ -509,4 +607,11 @@ export default { } } } - \ No newline at end of file + +.recommend-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} +