Compare commits

14 Commits

136 changed files with 10509 additions and 1464 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"kiroAgent.configureMCP": "Disabled"
}

313
CLAUDE.md
View File

@@ -2,215 +2,186 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述 ## Project Overview
**数字支行辅助管理系统(IBS)** - 基于 若依框架 v3.8.8 的前后端分离全栈项目,专注于银行支行的网格化营销、客户管理和走访业务。 This is a full-stack banking management system (数字支行辅助管理系统) built on the RuoYi framework v3.8.8. It provides grid-based customer relationship management, customer grouping, visit tracking, and performance statistics for banking institutions.
## 常用命令 - **Backend**: Spring Boot 2.5.14 + MyBatis + MySQL + Redis + JWT
- **Frontend**: Vue 2.6.12 + Element UI 2.15.14
- **Java Version**: 1.8
### 后端开发 ## Architecture
```bash ### Backend Module Structure
# Maven 打包(跳过测试)
mvn clean package -Dmaven.test.skip=true
# 运行已打包的 JAR The backend is a multi-module Maven project with the following modules:
cd ruoyi-admin/target
java -jar -Xms256m -Xmx1024m ruoyi-admin.jar
# 后端服务地址 ```
http://localhost:8080 ruoyi/
├── ruoyi-admin/ # Web entry point, main application (RuoYiApplication)
# Swagger API 文档 ├── ruoyi-framework/ # Core framework (security, config, interceptors)
http://localhost:8080/swagger-ui/index.html ├── ruoyi-system/ # System management (users, roles, menus, depts)
├── ruoyi-common/ # Common utilities, annotations, domain classes
# 测试登录接口获取 token ├── ruoyi-quartz/ # Scheduled task management
POST /login/test?username=admin&password=admin123 ├── ruoyi-generator/ # Code generation tools
├── ibs/ # Main business module: grid customer management (网格营销)
└── ibs-group/ # Customer group management module (客户分组)
``` ```
### 前端开发 ### Business Domain Structure
Each business module (e.g., `ibs`, `ibs-group`) follows this package structure:
```
com.ruoyi.<module>/
├── controller/ # REST controllers (handle HTTP requests)
├── service/ # Business logic layer
├── mapper/ # MyBatis mappers (database access)
├── domain/
│ ├── entity/ # Database entities
│ ├── dto/ # Data Transfer Objects (request)
│ └── vo/ # View Objects (response)
└── handler/ # Custom MyBatis type handlers
```
### Frontend Structure
```
ruoyi-ui/
├── src/
│ ├── api/ # API request modules (organized by feature)
│ ├── views/ # Page components
│ ├── components/ # Reusable components
│ ├── store/ # Vuex state management
│ ├── router/ # Vue Router configuration
│ ├── utils/ # Utility functions
│ └── directive/ # Custom Vue directives
```
## Build and Run Commands
### Backend (Maven)
```bash
# Clean build artifacts
cd bin && clean.bat
# Package the project (creates JAR in ruoyi-admin/target/)
cd bin && package.bat
# Run the backend server (requires packaged JAR)
cd bin && run.bat
# Or run directly with Maven from ruoyi-admin/
mvn spring-boot:run
# The main class is: com.ruoyi.RuoYiApplication
# Default port: 8080
```
### Frontend (npm/Vue CLI)
```bash ```bash
cd ruoyi-ui cd ruoyi-ui
# 安装依赖 # Development server (runs on port 80)
npm install
# 开发环境运行(端口 80,代理到 localhost:8080)
npm run dev npm run dev
# 生产环境构建 # For older Node.js versions with OpenSSL issues
npm run dev_t
# Build for production
npm run build:prod npm run build:prod
# 代码检查 # Build for staging
npm run build:stage
# Build for pre-production
npm run build:pre
# Lint code
npm run lint npm run lint
``` ```
### 数据库连接 ## Development Configuration
```bash ### Application Profiles
# 通过 MCP MySQL 工具连接
# 地址: 116.62.17.81:3306
# 数据库: ibs
# 用户名: root
```
## 核心架构 Backend uses Spring profiles located in `ruoyi-admin/src/main/resources/`:
- `application.yml` - Base configuration
- `application-dev.yml` - Development environment
- `application-uat.yml` - UAT environment
- `application-pre.yml` - Pre-production
- `application-pro.yml` - Production
### 后端模块结构 Active profile is set in `application.yml` (default: `dev`).
``` ### Frontend API Proxy
ruoyi-admin/ # 主入口模块,包含启动类和配置文件
ruoyi-framework/ # 框架核心:安全配置、缓存、数据源等
ruoyi-system/ # 系统管理:用户、角色、菜单、字典等
ruoyi-common/ # 通用工具:工具类、注解、常量等
ruoyi-quartz/ # 定时任务模块
ruoyi-generator/ # 代码生成器
ibs/ # ★ 业务模块:数字支行核心业务 ★
```
### IBS 业务模块 (核心业务) The Vue dev server proxies API requests to `http://localhost:8080`:
- Frontend dev server: `http://localhost:80`
- API requests: `/dev-api/*``http://localhost:8080/*`
位置: `ibs/src/main/java/com/ruoyi/ibs/` ## Key Patterns and Conventions
**主要业务包:** ### Backend Code Patterns
| 包名 | 功能 | 说明 | 1. **Controller-Service-Mapper Layering**
|------|------|------| - Controllers handle HTTP requests/responses, use `@RestController`
| `grid` | 网格管理 | 支行网格划分、分配、统计 | - Services contain business logic, use `@Service`
| `cmpm` | 客户经理管理 | 客户经理信息维护 | - Mappers use MyBatis annotations or XML files in `resources/mapper/`
| `list` | 客户列表管理 | 零售/商户/企业客户管理 |
| `visit` | 走访管理 | 走访任务、记录、轨迹 |
| `task` | 任务管理 | 营销任务分配和跟踪 |
| `draw` | 绘图/网格绘制 | 基于百度地图的网格绘制 |
| `custmap` | 客户地图 | 客户地理分布可视化 |
| `dashboard` | 仪表盘 | 数据统计和展示 |
| `datavisual` | 数据可视化 | 报表和图表 |
| `rules` | 规则配置 | 业务规则配置 |
| `qxhy` | 青县惠银接口 | 外部系统对接 |
| `websocket` | WebSocket通信 | 实时通信支持 |
**业务模块命名规范:** 2. **DTO/VO Pattern**
- 新建模块命名: `ibs` + 主要功能(如 `ibs-grid`, `ibs-customer`) - DTOs (Data Transfer Objects) for incoming requests
- Controller 放在新建模块中,不与若依框架混合 - VOs (View Objects) for outgoing responses
- Entity 使用 `@Data` 注解 - Entities map directly to database tables
- Service 使用 `@Resource` 注解,不继承 `ServiceImpl`
- DAO 使用 MyBatis Plus,复杂操作在 XML 中编写 SQL
### 前端结构 3. **Security & Authentication**
- JWT-based authentication via `TokenService`
- Role-based access: `head`, `branch`, `outlet`, `manager`
- Use `SecurityUtils` for current user context: `SecurityUtils.getUsername()`, `SecurityUtils.getDeptId()`, `SecurityUtils.userRole()`
``` 4. **Pagination**
ruoyi-ui/src/ - Uses `PageHelper` for database pagination
├── api/ # API 接口定义 - Controllers return `TableDataInfo` with `total` and `rows`
├── views/ # 页面视图
│ ├── grid/ # 网格管理相关页面
│ ├── customer/ # 客户管理
│ ├── taskManage/ # 任务管理
│ ├── dashboard/ # 仪表盘
│ └── ...
├── components/ # 公共组件
├── map/ # 地图相关(百度地图集成)
├── store/ # Vuex 状态管理
└── router/ # 路由配置
```
### 数据库表命名规范 5. **Caching**
- Uses `RedisCache` for Redis operations
- Cache keys often follow pattern: `{module}:{feature}:{key}`
- 新建表需加项目前缀: `ibs_` + 表名 ### Frontend Code Patterns
- 示例: `ibs_grid`, `ibs_customer`, `ibs_visit_record`
## 关键技术点 1. **API Modules**
- Each feature has a dedicated API file in `src/api/`
- Uses `request()` wrapper around axios
- API base URL is configured in `src/utils/request.js`
### 1. 地图集成 2. **Vuex Store**
- Modules in `src/store/modules/`: user, app, permission, settings, tagsView
- State persists via `vuex-persistedstate`
项目深度集成百度地图 API,用于: 3. **Permission Directives**
- 网格绘制和编辑 - `v-hasPermi` for button-level permissions
- 客户地理位置标注 - `v-hasRole` for role-based display
- 走访轨迹记录
- 客户分布热力图
**相关配置:** ## Common Business Concepts
- 百度地图 AK 在前端配置
- 使用 JTS 库进行地理空间计算
### 2. 批量导入优化 - **网格**: Grid-based territory management for customer assignment
- **客户经理**: Relationship managers assigned to customers
- **客户星级**: Customer rating levels (5星, 4星, 3星, 2星, 1星, 基础, 长尾)
- **AUM (Asset Under Management)**: Customer assets, tracked with monthly averages
- **PAD走访**: Mobile visit records for customer interactions
设计批量导入功能时: ## MyBatis Configuration
- 使用批量操作提高响应速度
- 导入结果只展示失败数据,不展示成功数据
- 使用 EasyExcel 处理 Excel
### 3. 多端支持 - Mapper XML files: `classpath*:mapper/**/*Mapper.xml`
- Type aliases: `com.ruoyi.**.domain`
- Custom type handlers: `com.ruoyi.ibs.handler`
- Pagination: `pagehelper` with MySQL dialect
- PC 端: 主要管理和配置功能 ## Notes
- PAD 端: 走访记录功能(移动端)
### 4. 外部系统对接 - The project uses Chinese comments and variable names in many places
- Scheduled tasks use Quartz via `ruoyi-quartz` module
| 系统 | 地址 | 用途 | - File upload path configured in `application.yml`: `ruoyi.profile`
|------|------|------| - Swagger/Knife4j API docs available when running
| 青县惠银 | http://158.234.96.76:5002 | 业务数据对接 |
| BI 系统 | http://158.220.52.42:9388/bi | 数据分析和报表 |
| 阿里云 OSS | - | 文件存储 |
## 开发规范
### 代码分层
- **Entity**: 实体类,不继承 BaseEntity,单独添加审计字段
- **DTO**: 接口传参专用类
- **VO**: 返回数据专用类
- **Service**: 业务逻辑,使用 `@Resource` 注入
- **Mapper**: MyBatis Plus + XML 混合使用
### 审计字段
通过注解实现自动填充:
```java
@TableField(fill = FieldFill.INSERT)
private String createBy;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
```
### 分页
使用 MyBatis Plus 分页插件自动处理,传入 `Page` 对象即可。
### 前端菜单配置
添加新页面后,需要在数据库 `sys_menu` 表中配置菜单权限。
## 重要配置文件
| 文件 | 位置 | 说明 |
|------|------|------|
| 后端配置 | `ruoyi-admin/src/main/resources/application-dev.yml` | 数据库、Redis、第三方服务配置 |
| 前端配置 | `ruoyi-ui/vue.config.js` | 开发服务器、代理配置 |
| 菜单数据 | 数据库 `sys_menu` 表 | 菜单权限配置 |
## 测试账号
- 用户名: `admin`
- 密码: `admin123`
## Swagger 接口文档
启动后端后访问: `http://localhost:8080/swagger-ui/index.html`
## 开发流程建议
1. **数据库设计**: 新建表需加 `ibs_` 前缀
2. **后端实体类**: 使用 `@Data` 注解,添加审计字段注解
3. **后端业务层**: 在 `ibs` 模块下开发,简单 CRUD 用 MyBatis Plus,复杂操作用 XML
4. **后端测试**: 使用 `/login/test` 获取 token 后测试接口
5. **前端开发**: 在 `ruoyi-ui/views/` 下对应业务模块开发
6. **菜单配置**: 在 `sys_menu` 表添加菜单项
7. **API 文档**: 生成后保存在 `doc/api/` 目录

View File

@@ -9,7 +9,6 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.group.domain.dto.CustGroupMemberTemplate; import com.ruoyi.group.domain.dto.CustGroupMemberTemplate;
import com.ruoyi.group.domain.dto.CustGroupQueryDTO; 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.CustGroup;
import com.ruoyi.group.domain.vo.CustGroupVO; import com.ruoyi.group.domain.vo.CustGroupVO;
import com.ruoyi.group.service.ICustGroupService; import com.ruoyi.group.service.ICustGroupService;
@@ -50,69 +49,41 @@ public class CustGroupController extends BaseController {
} }
/** /**
* 获取客群详情 * 根据ID查询客群详情
*/ */
@ApiOperation("获取客群详情") @ApiOperation("根据ID查询客群详情")
@Log(title = "客群管理-获取客群详情") @Log(title = "客群管理-查询客群详情")
@GetMapping("/{id}") @GetMapping("/{id}")
public AjaxResult getCustGroup(@PathVariable Long id) { public AjaxResult getCustGroup(@PathVariable Long id) {
CustGroupVO vo = custGroupService.getCustGroup(id); CustGroupVO custGroup = custGroupService.getCustGroup(id);
return AjaxResult.success(vo); 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("异步创建客群(模板导入)") @ApiOperation("异步创建客群(模板导入)")
@Log(title = "客群管理-异步创建客群", businessType = BusinessType.INSERT) @Log(title = "客群管理-异步创建客群", businessType = BusinessType.INSERT)
@PostMapping(value = "/createByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/createByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AjaxResult createCustGroupByTemplate(@RequestPart("dto") @Valid String dtoJson, public AjaxResult createCustGroupByTemplate(
@RequestPart("file") MultipartFile file) { @RequestPart("dto") @Valid String dtoJson,
@RequestPart("file") MultipartFile file) {
CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class); CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class);
return AjaxResult.success(custGroupService.createCustGroupByTemplate(custGroup, file)); return AjaxResult.success("操作成功", custGroupService.createCustGroupByTemplate(custGroup, file));
}
/**
* 更新客群
*/
@ApiOperation("更新客群")
@Log(title = "客群管理-更新客群", businessType = BusinessType.UPDATE)
@PostMapping("/update")
public AjaxResult updateCustGroup(@RequestBody @Valid CustGroup custGroup) {
String result = custGroupService.updateCustGroup(custGroup);
return AjaxResult.success(result);
}
/**
* 更新客群(网格导入)
*/
@ApiOperation("更新客群(网格导入)")
@Log(title = "客群管理-更新客群(网格导入)", businessType = BusinessType.UPDATE)
@PostMapping("/updateByGrid")
public AjaxResult updateCustGroupByGrid(@RequestBody @Valid GridImportDTO gridImportDTO) {
String result = custGroupService.updateCustGroupByGrid(gridImportDTO);
return AjaxResult.success(result);
} }
/** /**
* 更新客群(模板导入) * 更新客群(模板导入)
* gridType、regionGridIds、drawGridIds 包含在 dto 中
* file 参数可选:不传文件则只更新客群信息,传文件则追加客户
*/ */
@ApiOperation("更新客群(模板导入)") @ApiOperation("更新客群(模板导入)")
@Log(title = "客群管理-更新客群(模板导入)", businessType = BusinessType.UPDATE) @Log(title = "客群管理-更新客群(模板导入)", businessType = BusinessType.UPDATE)
@PostMapping(value = "/updateByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/updateByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AjaxResult updateCustGroupByTemplate(@RequestPart("dto") @Valid String dtoJson, public AjaxResult updateCustGroupByTemplate(
@RequestPart("file") MultipartFile file) { @RequestPart("dto") @Valid String dtoJson,
@RequestPart(value = "file", required = false) MultipartFile file) {
CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class); CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class);
return AjaxResult.success(custGroupService.updateCustGroupByTemplate(custGroup, file)); return AjaxResult.success(custGroupService.updateCustGroupByTemplate(custGroup, file));
} }
@@ -125,7 +96,7 @@ public class CustGroupController extends BaseController {
@GetMapping("/createStatus/{id}") @GetMapping("/createStatus/{id}")
public AjaxResult getCreateStatus(@PathVariable Long id) { public AjaxResult getCreateStatus(@PathVariable Long id) {
String status = custGroupService.getCreateStatus(id); String status = custGroupService.getCreateStatus(id);
return AjaxResult.success(status); return AjaxResult.success("操作成功", status);
} }
/** /**
@@ -151,14 +122,14 @@ public class CustGroupController extends BaseController {
} }
/** /**
* 手动移除客群客户 * 获取所有已有的客群标签列表
*/ */
@ApiOperation("手动移除客群客户") @ApiOperation("获取所有客群标签")
@Log(title = "客群管理-手动移除客户", businessType = BusinessType.DELETE) @Log(title = "客群管理-获取客群标签")
@PostMapping("/removeMembers") @GetMapping("/tags")
public AjaxResult removeMembers(@RequestParam Long groupId, @RequestBody List<Long> memberIds) { public AjaxResult getAllGroupTags() {
String result = custGroupService.removeMembers(groupId, memberIds); List<String> tags = custGroupService.getAllGroupTags();
return AjaxResult.success(result); return AjaxResult.success(tags);
} }
} }

View File

@@ -0,0 +1,54 @@
package com.ruoyi.group.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
import com.ruoyi.group.service.ICustGroupMemberService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* 客群客户Controller
*
* @author ruoyi
*/
@Api(tags = "客群客户接口")
@RestController
@RequestMapping("/group/member")
public class CustGroupMemberController extends BaseController {
@Resource
private ICustGroupMemberService custGroupMemberService;
/**
* 分页查询客群客户列表
*/
@ApiOperation("分页查询客群客户列表")
@Log(title = "客群客户-查询客户列表")
@GetMapping("/list/{groupId}")
public TableDataInfo listCustGroupMembers(@PathVariable Long groupId,
CustGroupMemberQueryDTO dto) {
// 注意startPage()在Service内部调用因为权限检查会先执行SQL导致分页失效
List<CustGroupMemberVO> list = custGroupMemberService.listCustGroupMembers(groupId, dto);
return getDataTable(list);
}
/**
* 手动移除客群客户
*/
@ApiOperation("手动移除客群客户")
@Log(title = "客群客户-手动移除客户", businessType = BusinessType.DELETE)
@PostMapping("/remove")
public AjaxResult removeMembers(@RequestParam Long groupId, @RequestBody List<Long> memberIds) {
String result = custGroupMemberService.removeMembers(groupId, memberIds);
return AjaxResult.success(result);
}
}

View File

@@ -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<GroupCmpmCountLingshou825> selectLsCountList(String dt, String groupName) {
Page<Object> page = startPage();
List<GroupCmpmCountLingshou825> list = groupPerformanceService.selectLsCountList(dt, groupName);
return getDataTable(list, page);
}
@ApiOperation("查询公司客群业绩汇总列表")
@GetMapping("/gs/list")
public TableDataPageInfo<GroupCmpmCountGongsi825> selectGsCountList(String dt, String groupName) {
Page<Object> page = startPage();
List<GroupCmpmCountGongsi825> list = groupPerformanceService.selectGsCountList(dt, groupName);
return getDataTable(list, page);
}
@ApiOperation("查询零售客群客户明细")
@GetMapping("/ls/custList")
public TableDataPageInfo<GroupCustCountLingshou825> selectLsCustList(@RequestParam String groupId,
String custName,
String custIdc,
String dt) {
Page<Object> page = startPage();
List<GroupCustCountLingshou825> list = groupPerformanceService.selectLsCustList(groupId, custName, custIdc, dt);
return getDataTable(list, page);
}
@ApiOperation("查询公司客群客户明细")
@GetMapping("/gs/custList")
public TableDataPageInfo<GroupCustCountGongsi825> selectGsCustList(@RequestParam String groupId,
String custName,
String socialCreditCode,
String dt) {
Page<Object> page = startPage();
List<GroupCustCountGongsi825> 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<GroupCmpmCountLingshou825> 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<GroupCmpmCountGongsi825> 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<GroupCustCountLingshou825> 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<GroupCustCountGongsi825> util = new ExcelUtil<>(GroupCustCountGongsi825.class);
util.exportExcel(response, groupPerformanceService.selectGsCustList(groupId, custName, socialCreditCode, dt), "公司客群客户明细");
} catch (Exception e) {
logger.error("导出公司客群客户明细失败", e);
}
}
}

View File

@@ -0,0 +1,69 @@
package com.ruoyi.group.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 客群客户查询DTO
*
* @author ruoyi
*/
@Data
@ApiModel(description = "客群客户查询条件")
public class CustGroupMemberQueryDTO {
/**
* 页码
*/
@ApiModelProperty(value = "页码", hidden = true)
private Integer pageNum;
/**
* 每页数量
*/
@ApiModelProperty(value = "每页数量", hidden = true)
private Integer pageSize;
/**
* 客户类型0=个人, 1=商户, 2=企业
*/
@ApiModelProperty(value = "客户类型")
private String custType;
/**
* 客户姓名
*/
@ApiModelProperty(value = "客户姓名")
private String custName;
/**
* 客户经理柜员号
*/
@ApiModelProperty(value = "客户经理柜员号")
private String userName;
/**
* 客户经理姓名
*/
@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;
}

View File

@@ -39,6 +39,24 @@ public class CustGroupQueryDTO implements Serializable {
@ApiModelProperty(value = "客群状态", name = "groupStatus") @ApiModelProperty(value = "客群状态", name = "groupStatus")
private String groupStatus; private String groupStatus;
/**
* 客群标签(模糊匹配)
*/
@ApiModelProperty(value = "客群标签", name = "groupTags")
private String groupTags;
/**
* 视图类型mine=我创建的sharedToMe=下发给我的
*/
@ApiModelProperty(value = "视图类型", name = "viewType")
private String viewType;
/**
* 当前用户是否属于总行管理员口径
*/
@ApiModelProperty(value = "当前用户是否总行管理员", hidden = true)
private Boolean headRole;
/** /**
* 页码 * 页码
*/ */
@@ -50,4 +68,4 @@ public class CustGroupQueryDTO implements Serializable {
*/ */
@ApiModelProperty(value = "每页大小", name = "pageSize") @ApiModelProperty(value = "每页大小", name = "pageSize")
private Integer pageSize = 10; private Integer pageSize = 10;
} }

View File

@@ -38,10 +38,10 @@ public class CustGroup {
private String groupMode; private String groupMode;
/** /**
* 创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格 * 客群类型0=零售类(个人+商户), 1=公司类(企业)
*/ */
@ApiModelProperty(value = "创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格", name = "createMode") @ApiModelProperty(value = "客群类型0=零售类(个人+商户), 1=公司类(企业)", name = "groupType")
private String createMode; private String groupType;
/** /**
* 柜员号 * 柜员号
@@ -59,7 +59,6 @@ public class CustGroup {
* 所属机构ID * 所属机构ID
*/ */
@ApiModelProperty(value = "所属机构ID", name = "deptId") @ApiModelProperty(value = "所属机构ID", name = "deptId")
@TableField(fill = FieldFill.INSERT)
private Long deptId; private Long deptId;
/** /**
@@ -74,13 +73,6 @@ public class CustGroup {
@ApiModelProperty(value = "可见部门ID列表逗号分隔", name = "shareDeptIds") @ApiModelProperty(value = "可见部门ID列表逗号分隔", name = "shareDeptIds")
private String shareDeptIds; private String shareDeptIds;
/**
* 共享部门ID列表非表字段用于接收前端传参
*/
@ApiModelProperty(value = "共享部门ID列表", name = "shareDeptIdList")
@TableField(exist = false)
private List<Long> shareDeptIdList;
/** /**
* 客群状态0=正常, 1=已禁用 * 客群状态0=正常, 1=已禁用
*/ */
@@ -109,14 +101,12 @@ public class CustGroup {
/** /**
* 更新者 * 更新者
*/ */
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy; private String updateBy;
/** /**
* 更新时间 * 更新时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime; private Date updateTime;
/** /**
@@ -133,23 +123,18 @@ public class CustGroup {
private String remark; 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; private String gridType;
/**
* 绩效业务类型retail=零售, corporate=公司(动态客群使用)
*/
@ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType")
private String cmpmBizType;
/**
* 客户经理列表(逗号分隔,动态客群使用)
*/
@ApiModelProperty(value = "客户经理列表", name = "gridUserNames")
private String gridUserNames;
/** /**
* 地理网格ID列表逗号分隔动态客群使用 * 地理网格ID列表逗号分隔动态客群使用
*/ */

View File

@@ -60,19 +60,65 @@ public class CustGroupMember {
@ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode") @ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode")
private String 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;
/** /**
* 创建者 * 创建者
*/ */
@TableField(fill = FieldFill.INSERT)
private String createBy; private String createBy;
/** /**
* 创建时间 * 创建时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
private Date createTime; private Date createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** /**
* 删除标识0=正常, 1=删除 * 删除标识0=正常, 1=删除
*/ */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -57,10 +57,52 @@ public class CustGroupMemberVO {
@ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode") @ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode")
private String 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") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间", name = "createTime") @ApiModelProperty(value = "创建时间", name = "createTime")
private Date createTime; private Date createTime;
/**
* 存量状态true=存量客户系统存在false=非存量客户(系统不存在)
*/
@ApiModelProperty(value = "存量状态true=存量客户false=非存量客户", name = "isExisting")
private Boolean isExisting;
} }

View File

@@ -35,10 +35,10 @@ public class CustGroupVO {
private String groupMode; private String groupMode;
/** /**
* 创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格 * 客群类型0=零售类(个人+商户), 1=公司类(企业)
*/ */
@ApiModelProperty(value = "创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格", name = "createMode") @ApiModelProperty(value = "客群类型0=零售类(个人+商户), 1=公司类(企业)", name = "groupType")
private String createMode; private String groupType;
/** /**
* 柜员号 * 柜员号
@@ -65,10 +65,10 @@ public class CustGroupVO {
private Integer shareEnabled; private Integer shareEnabled;
/** /**
* 可见部门ID列表 * 可见部门ID列表(逗号分隔)
*/ */
@ApiModelProperty(value = "可见部门ID列表", name = "shareDeptIds") @ApiModelProperty(value = "可见部门ID列表(逗号分隔)", name = "shareDeptIds")
private List<Long> shareDeptIds; private String shareDeptIds;
/** /**
* 客群状态0=正常, 1=已禁用 * 客群状态0=正常, 1=已禁用
@@ -114,6 +114,43 @@ public class CustGroupVO {
@ApiModelProperty(value = "备注", name = "remark") @ApiModelProperty(value = "备注", name = "remark")
private String remark; private String remark;
/**
* 客群标签(多个标签用逗号分隔)
*/
@ApiModelProperty(value = "客群标签(多个标签用逗号分隔)", name = "groupTags")
private String groupTags;
/**
* 有效期截止时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "有效期截止时间", name = "validTime")
private Date validTime;
/**
* 创建状态0=创建中, 1=创建成功, 2=创建失败
*/
@ApiModelProperty(value = "创建状态0=创建中, 1=创建成功, 2=创建失败", name = "createStatus")
private String createStatus;
/**
* 网格类型管户关系来源0=绩效网格, 1=地理网格, 2=绘制网格
*/
@ApiModelProperty(value = "网格类型管户关系来源0=绩效网格, 1=地理网格, 2=绘制网格", name = "gridType")
private String gridType;
/**
* 地理网格ID列表逗号分隔
*/
@ApiModelProperty(value = "地理网格ID列表", name = "regionGridIds")
private String regionGridIds;
/**
* 绘制网格ID列表逗号分隔
*/
@ApiModelProperty(value = "绘制网格ID列表", name = "drawGridIds")
private String drawGridIds;
/** /**
* 客户列表 * 客户列表
*/ */

View File

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.group.domain.dto.CustGroupQueryDTO; import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
import com.ruoyi.group.domain.entity.CustGroup; import com.ruoyi.group.domain.entity.CustGroup;
import com.ruoyi.group.domain.vo.CustGroupVO; import com.ruoyi.group.domain.vo.CustGroupVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@@ -13,6 +14,7 @@ import java.util.List;
* *
* @author ruoyi * @author ruoyi
*/ */
@Mapper
public interface CustGroupMapper extends BaseMapper<CustGroup> { public interface CustGroupMapper extends BaseMapper<CustGroup> {
/** /**
@@ -30,5 +32,50 @@ public interface CustGroupMapper extends BaseMapper<CustGroup> {
* @param dto 查询条件 * @param dto 查询条件
* @return 客群VO列表 * @return 客群VO列表
*/ */
List<CustGroupVO> selectCustGroupList(@Param("dto") CustGroupQueryDTO dto); List<CustGroupVO> selectCustGroupList(@Param("dto") CustGroupQueryDTO dto,
} @Param("userName") String userName,
@Param("deptId") String deptId,
@Param("headId") String headId);
/**
* 根据ID查询客群详情
*
* @param id 客群ID
* @return 客群VO
*/
CustGroupVO selectCustGroupById(@Param("id") Long id,
@Param("userName") String userName,
@Param("deptId") String deptId,
@Param("headRole") Boolean headRole,
@Param("headId") String headId);
/**
* 校验当前用户是否有客群查看权限
*
* @param id 客群ID
* @param userName 当前用户名
* @param deptId 当前部门ID
* @return 可查看数量
*/
Long countVisibleCustGroup(@Param("id") Long id,
@Param("userName") String userName,
@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);
/**
* 查询所有已有的客群标签
*
* @return 标签列表
*/
List<String> selectAllGroupTags();
}

View File

@@ -1,21 +1,61 @@
package com.ruoyi.group.mapper; package com.ruoyi.group.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
import com.ruoyi.group.domain.entity.CustGroupMember; import com.ruoyi.group.domain.entity.CustGroupMember;
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* 客群客户关联Mapper接口 * 客群客户关联Mapper接口
* *
* @author ruoyi * @author ruoyi
*/ */
@Mapper
public interface CustGroupMemberMapper extends BaseMapper<CustGroupMember> { public interface CustGroupMemberMapper extends BaseMapper<CustGroupMember> {
/** /**
* 查询客群客户数量 * 分页查询客群客户列表
* *
* @param groupId 客群ID * @param groupId 客群ID
* @return 数量 * @param dto 查询条件
* @return 客户列表
*/ */
Long countByGroupId(@Param("groupId") Long groupId); List<CustGroupMemberVO> selectCustGroupMemberList(@Param("groupId") Long groupId,
@Param("dto") CustGroupMemberQueryDTO dto);
/**
* 批量插入客群客户INSERT IGNORE遇到重复键自动跳过
*
* @param memberList 客户列表
*/
void batchInsertMembers(@Param("list") List<CustGroupMember> memberList);
/**
* 批量查询个人客户是否存在(根据客户号+身份证号)
*
* @param custIds 客户号列表
* @return 存在的客户号列表
*/
List<String> selectExistingRetailCustIds(@Param("list") List<String> custIds);
/**
* 批量查询商户客户是否存在(根据客户号+统信码)
*
* @param custIds 客户号列表
* @return 存在的客户号列表
*/
List<String> selectExistingMerchantCustIds(@Param("list") List<String> custIds);
/**
* 批量查询企业客户是否存在(根据客户号+统信码)
*
* @param custIds 客户号列表
* @return 存在的客户号列表
*/
List<String> selectExistingBusinessCustIds(@Param("list") List<String> custIds);
} }

View File

@@ -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<GroupCmpmCountLingshou825> selectLsCountList(HashMap<String, Object> paramMap);
List<GroupCmpmCountGongsi825> selectGsCountList(HashMap<String, Object> paramMap);
List<GroupCustCountLingshou825> selectLsCustList(HashMap<String, Object> paramMap);
List<GroupCustCountGongsi825> selectGsCustList(HashMap<String, Object> paramMap);
}

View File

@@ -0,0 +1,32 @@
package com.ruoyi.group.service;
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
import java.util.List;
/**
* 客群客户服务接口
*
* @author ruoyi
*/
public interface ICustGroupMemberService {
/**
* 分页查询客群客户列表
*
* @param groupId 客群ID
* @param dto 查询条件
* @return 客户列表
*/
List<CustGroupMemberVO> listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto);
/**
* 手动移除客群客户
*
* @param groupId 客群ID
* @param memberIds 客户ID列表
* @return 结果
*/
String removeMembers(Long groupId, List<Long> memberIds);
}

View File

@@ -1,7 +1,6 @@
package com.ruoyi.group.service; package com.ruoyi.group.service;
import com.ruoyi.group.domain.dto.CustGroupQueryDTO; 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.CustGroup;
import com.ruoyi.group.domain.vo.CustGroupVO; import com.ruoyi.group.domain.vo.CustGroupVO;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -17,98 +16,56 @@ public interface ICustGroupService {
/** /**
* 查询客群列表 * 查询客群列表
*
* @param dto 查询条件
* @return 客群VO列表
*/ */
List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto); List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto);
/**
* 根据ID查询客群详情
*/
CustGroupVO getCustGroup(Long id);
/**
* 校验当前用户是否有客群查看权限
*/
void checkCustGroupViewPermission(Long id);
/** /**
* 异步创建客群(模板导入) * 异步创建客群(模板导入)
* gridType、regionGridIds、drawGridIds 从 custGroup 中获取
* *
* @param custGroup 客群实体 * @param custGroup 客群实体(包含 gridType、regionGridIds、drawGridIds
* @param file Excel文件 * @param file Excel文件
* @return 客群ID * @return 客群ID
*/ */
String createCustGroupByTemplate(CustGroup custGroup, MultipartFile file); 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 custGroup 客群实体(包含 gridType、regionGridIds、drawGridIds
* @param file Excel文件 * @param file Excel文件
* @return 结果消息 * @return 结果消息
*/ */
String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file); String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file);
/**
* 更新客群
*
* @param custGroup 客群实体
* @return 结果消息
*/
String updateCustGroup(CustGroup custGroup);
/** /**
* 删除客群 * 删除客群
*
* @param idList 客群ID列表
* @return 结果消息
*/ */
String deleteCustGroup(List<Long> idList); String deleteCustGroup(List<Long> idList);
/**
* 获取客群详情
*
* @param id 客群ID
* @return 客群VO
*/
CustGroupVO getCustGroup(Long id);
/** /**
* 检查客群名称是否存在 * 检查客群名称是否存在
*
* @param groupName 客群名称
* @return true=存在, false=不存在
*/ */
boolean checkGroupNameExist(String groupName); boolean checkGroupNameExist(String groupName);
/** /**
* 查询客群创建状态 * 查询客群创建状态
*
* @param id 客群ID
* @return 创建状态0=创建中, 1=创建成功, 2=创建失败
*/ */
String getCreateStatus(Long id); String getCreateStatus(Long id);
/**
* 手动移除客群客户
*
* @param groupId 客群ID
* @param memberIds 客群成员ID列表
* @return 结果消息
*/
String removeMembers(Long groupId, List<Long> memberIds);
/** /**
* 更新动态客群(定时任务调用) * 更新动态客群(定时任务调用)
* 根据原始导入条件重新查询并更新客户列表
*/ */
void updateDynamicCustGroups(); void updateDynamicCustGroups();
@@ -117,4 +74,11 @@ public interface ICustGroupService {
*/ */
void checkAndDisableExpiredGroups(); void checkAndDisableExpiredGroups();
} /**
* 获取所有已有的客群标签列表
*
* @return 标签列表
*/
List<String> getAllGroupTags();
}

View File

@@ -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<GroupCmpmCountLingshou825> selectLsCountList(String dt, String groupName);
List<GroupCmpmCountGongsi825> selectGsCountList(String dt, String groupName);
List<GroupCustCountLingshou825> selectLsCustList(String groupId, String custName, String custIdc, String dt);
List<GroupCustCountGongsi825> selectGsCustList(String groupId, String custName, String socialCreditCode, String dt);
}

View File

@@ -0,0 +1,140 @@
package com.ruoyi.group.service.impl;
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
import com.ruoyi.group.domain.entity.CustGroup;
import com.ruoyi.group.domain.entity.CustGroupMember;
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
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;
/**
* 客群客户服务实现类
*
* @author ruoyi
*/
@Service
public class CustGroupMemberServiceImpl implements ICustGroupMemberService {
@Resource
private CustGroupMemberMapper custGroupMemberMapper;
@Resource
private CustGroupMapper custGroupMapper;
@Resource
private ICustGroupService custGroupService;
@Override
public List<CustGroupMemberVO> 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;
PageHelper.startPage(pageNum, pageSize);
// 1. 查询客群成员列表(分页)
List<CustGroupMemberVO> memberList = custGroupMemberMapper.selectCustGroupMemberList(groupId, dto);
if (memberList == null || memberList.isEmpty()) {
return memberList;
}
// 2. 按客户类型分组,批量查询存量状态
// 个人客户
List<String> retailCustIds = memberList.stream()
.filter(m -> "0".equals(m.getCustType()))
.map(CustGroupMemberVO::getCustId)
.collect(Collectors.toList());
// 商户客户
List<String> merchantCustIds = memberList.stream()
.filter(m -> "1".equals(m.getCustType()))
.map(CustGroupMemberVO::getCustId)
.collect(Collectors.toList());
// 企业客户
List<String> businessCustIds = memberList.stream()
.filter(m -> "2".equals(m.getCustType()))
.map(CustGroupMemberVO::getCustId)
.collect(Collectors.toList());
// 3. 批量查询各类型存量客户
Set<String> existingRetailCustIds = new HashSet<>();
Set<String> existingMerchantCustIds = new HashSet<>();
Set<String> 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
@Transactional(rollbackFor = Exception.class)
public String removeMembers(Long groupId, List<Long> memberIds) {
// 检查客群是否存在
CustGroup custGroup = custGroupMapper.selectById(groupId);
if (custGroup == null) {
throw new ServiceException("客群不存在");
}
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("无权限操作该客群");
}
// 删除客户关联
memberIds.forEach(memberId -> {
CustGroupMember member = custGroupMemberMapper.selectById(memberId);
if (member != null && member.getGroupId().equals(groupId)) {
// 设置手动移除标识
member.setManualRemove(1);
custGroupMemberMapper.updateById(member);
// 逻辑删除
custGroupMemberMapper.deleteById(memberId);
}
});
return "移除成功";
}
}

View File

@@ -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<GroupCmpmCountLingshou825> selectLsCountList(String dt, String groupName) {
return groupPerformanceMapper.selectLsCountList(buildLsCountParams(dt, groupName));
}
private HashMap<String, Object> buildLsCountParams(String dt, String groupName) {
HashMap<String, Object> 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<GroupCmpmCountGongsi825> selectGsCountList(String dt, String groupName) {
HashMap<String, Object> 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<GroupCustCountLingshou825> selectLsCustList(String groupId, String custName, String custIdc, String dt) {
return groupPerformanceMapper.selectLsCustList(buildLsCustParams(groupId, custName, custIdc, dt));
}
private HashMap<String, Object> buildLsCustParams(String groupId, String custName, String custIdc, String dt) {
HashMap<String, Object> 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<GroupCustCountGongsi825> selectGsCustList(String groupId, String custName, String socialCreditCode, String dt) {
HashMap<String, Object> 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);
}
}

View File

@@ -4,18 +4,80 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.group.mapper.CustGroupMapper"> <mapper namespace="com.ruoyi.group.mapper.CustGroupMapper">
<sql id="custGroupVisibleBaseCondition">
AND (
<choose>
<when test="headRole != null and headRole">
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')
)
</when>
<otherwise>
1 = 2
</otherwise>
</choose>
OR (
cg.share_enabled = 1
AND cg.group_status = '0'
AND cg.share_dept_ids IS NOT NULL
AND cg.share_dept_ids != ''
AND find_in_set(#{deptId}, cg.share_dept_ids)
)
)
</sql>
<sql id="custGroupVisibleCondition">
<choose>
<when test="dto != null and dto.viewType == 'mine'">
<choose>
<when test="dto.headRole">
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')
)
</when>
<otherwise>
AND 1 = 2
</otherwise>
</choose>
</when>
<when test="dto != null and dto.viewType == 'sharedToMe'">
AND cg.share_enabled = 1
AND cg.group_status = '0'
AND cg.share_dept_ids IS NOT NULL
AND cg.share_dept_ids != ''
AND find_in_set(#{deptId}, cg.share_dept_ids)
</when>
<otherwise>
<include refid="custGroupVisibleBaseCondition"/>
</otherwise>
</choose>
</sql>
<select id="selectCustGroupList" resultType="CustGroupVO"> <select id="selectCustGroupList" resultType="CustGroupVO">
SELECT SELECT
cg.id, cg.id,
cg.group_name, cg.group_name,
cg.group_mode, cg.group_mode,
cg.create_mode, cg.group_type,
cg.user_name, cg.user_name,
cg.nick_name, cg.nick_name,
cg.dept_id, cg.dept_id,
cg.share_enabled, cg.share_enabled,
cg.share_dept_ids, cg.share_dept_ids,
cg.group_status, cg.group_status,
cg.group_tags,
cg.create_by, cg.create_by,
cg.create_time, cg.create_time,
cg.update_by, cg.update_by,
@@ -26,21 +88,88 @@
FROM ibs_cust_group cg FROM ibs_cust_group cg
<where> <where>
cg.del_flag = '0' cg.del_flag = '0'
and create_status = '1' AND cg.create_status = '1'
<include refid="custGroupVisibleCondition"/>
<if test="dto.groupName != null and dto.groupName != ''"> <if test="dto.groupName != null and dto.groupName != ''">
AND cg.group_name LIKE CONCAT('%', #{dto.groupName}, '%') AND cg.group_name LIKE CONCAT('%', #{dto.groupName}, '%')
</if> </if>
<if test="dto.groupMode != null and dto.groupMode != ''"> <if test="dto.groupMode != null and dto.groupMode != ''">
AND cg.group_mode = #{dto.groupMode} AND cg.group_mode = #{dto.groupMode}
</if> </if>
<if test="dto.createMode != null and dto.createMode != ''">
AND cg.create_mode = #{dto.createMode}
</if>
<if test="dto.groupStatus != null and dto.groupStatus != ''"> <if test="dto.groupStatus != null and dto.groupStatus != ''">
AND cg.group_status = #{dto.groupStatus} AND cg.group_status = #{dto.groupStatus}
</if> </if>
<if test="dto.groupTags != null and dto.groupTags != ''">
AND cg.group_tags LIKE CONCAT('%', #{dto.groupTags}, '%')
</if>
</where> </where>
ORDER BY cg.create_time DESC ORDER BY cg.create_time DESC
</select> </select>
<select id="selectCustGroupById" resultType="CustGroupVO">
SELECT
cg.id,
cg.group_name,
cg.group_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,
cg.update_by,
cg.update_time,
cg.remark,
cg.create_status,
cg.grid_type,
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
FROM ibs_cust_group cg
WHERE cg.id = #{id}
AND cg.del_flag = '0'
AND cg.create_status = '1'
<include refid="custGroupVisibleBaseCondition"/>
</select>
<select id="countVisibleCustGroup" resultType="java.lang.Long">
SELECT COUNT(1)
FROM ibs_cust_group cg
WHERE cg.id = #{id}
AND cg.del_flag = '0'
AND cg.create_status = '1'
<include refid="custGroupVisibleBaseCondition"/>
</select>
<select id="countHeadOperableCustGroup" resultType="java.lang.Long">
SELECT COUNT(1)
FROM ibs_cust_group cg
WHERE cg.id = #{id}
AND cg.del_flag = '0'
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')
)
</select>
<select id="selectAllGroupTags" resultType="java.lang.String">
SELECT DISTINCT group_tags
FROM ibs_cust_group
WHERE del_flag = '0'
AND create_status = '1'
AND group_tags IS NOT NULL
AND group_tags != ''
ORDER BY group_tags
</select>
</mapper> </mapper>

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.group.mapper.CustGroupMemberMapper">
<select id="selectCustGroupMemberList" resultType="CustGroupMemberVO">
SELECT
cgm.id,
cgm.group_id,
cgm.cust_type,
cgm.cust_id,
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
<where>
cgm.group_id = #{groupId}
AND cgm.del_flag = '0'
<choose>
<when test="dto != null and dto.userRole == 'branch'">
AND cgm.branch_id = #{dto.currentDeptId}
</when>
<when test="dto != null and dto.userRole == 'outlet'">
AND cgm.outlet_id = #{dto.currentDeptId}
</when>
<when test="dto != null and dto.userRole == 'manager'">
AND cgm.user_name = #{dto.currentUserName}
</when>
</choose>
<if test="dto != null and dto.custType != null and dto.custType != ''">
AND cgm.cust_type = #{dto.custType}
</if>
<if test="dto != null and dto.custName != null and dto.custName != ''">
AND cgm.cust_name LIKE CONCAT('%', #{dto.custName}, '%')
</if>
<if test="dto != null and dto.userName != null and dto.userName != ''">
AND cgm.user_name = #{dto.userName}
</if>
<if test="dto != null and dto.nickName != null and dto.nickName != ''">
AND cgm.nick_name LIKE CONCAT('%', #{dto.nickName}, '%')
</if>
</where>
ORDER BY cgm.create_time ASC
</select>
<!-- 批量查询个人客户是否存在 -->
<select id="selectExistingRetailCustIds" resultType="String">
SELECT DISTINCT t.cust_id
FROM ibs_cust_group_member t
INNER JOIN cust_info_retail c ON t.cust_id = c.cust_id AND t.cust_idc = c.cust_idc
WHERE t.del_flag = '0'
AND t.cust_type = '0'
AND t.cust_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 批量查询商户客户是否存在 -->
<select id="selectExistingMerchantCustIds" resultType="String">
SELECT DISTINCT t.cust_id
FROM ibs_cust_group_member t
INNER JOIN cust_info_merchant c ON t.cust_id = c.cust_id AND t.social_credit_code = c.social_credit_code
WHERE t.del_flag = '0'
AND t.cust_type = '1'
AND t.cust_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 批量查询企业客户是否存在 -->
<select id="selectExistingBusinessCustIds" resultType="String">
SELECT DISTINCT t.cust_id
FROM ibs_cust_group_member t
INNER JOIN cust_info_business c ON t.cust_id = c.cust_id AND t.social_credit_code = c.social_credit_code
WHERE t.del_flag = '0'
AND t.cust_type = '2'
AND t.cust_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 批量插入客群客户INSERT IGNORE遇到重复键自动跳过 -->
<insert id="batchInsertMembers">
INSERT IGNORE INTO ibs_cust_group_member
(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
<foreach collection="list" item="item" index="index" separator=",">
(#{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')
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.group.mapper.GroupPerformanceMapper">
<select id="selectLsCountList" resultType="com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825">
SELECT
dt,
group_id,
group_name,
group_mode,
dept_id,
dept_name,
outlets_id,
outlets_name,
user_name,
cust_num,
zf_365cnt,
zf_180cnt,
zf_90cnt,
zf_30cnt,
zf_365rt,
zf_180rt,
zf_90rt,
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,
hxsd_num,
region_code,
ops_dept
FROM group_cmpm_count_lingshou_825
<where>
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="groupName != null and groupName != ''">and group_name like concat('%', #{groupName}, '%')</if>
<if test="isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
</where>
</select>
<select id="selectGsCountList" resultType="com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825">
SELECT
dt,
group_id,
group_name,
group_mode,
dept_id,
dept_name,
outlets_id,
outlets_name,
user_name,
cust_num,
hq_cur_balance,
bz_cur_balance,
loan_balance_cny,
finance_prod_711_balance,
finance_prod_716_balance,
loan_year_dailyaverage,
qfcd_rat,
tx_rat,
bh_rat,
yxdfgz_rat,
dkdf_rat,
dksf_rat,
dkshf_rat,
pjb_rat,
czb_rat,
sfb_rat,
mrb_rat,
szst_rat,
kh_rat,
gjjsyw_rat,
yqjsh_rat,
qfcd_num,
tx_num,
bh_num,
yxdfgz_num,
ustr_count_per_m,
ustr_bal_m,
dkdf_num,
dksf_num,
dkshf_num,
pjb_num,
czb_num,
sfb_num,
mrb_num,
szst_num,
kh_num,
gjjsyw_num,
yqjsh_num,
region_code,
ops_dept,
htqy_rat,
htqy_num,
dept_type,
zf_365cnt,
zf_180cnt,
zf_90cnt,
zf_30cnt,
zf_365rt,
zf_180rt,
zf_90rt,
zf_30rt,
ph_rat,
ph_num
FROM group_cmpm_count_gongsi_825
<where>
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="groupName != null and groupName != ''">and group_name like concat('%', #{groupName}, '%')</if>
<if test=" isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test=" isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test=" isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
</where>
</select>
<select id="selectLsCustList" resultType="com.ruoyi.group.domain.entity.GroupCustCountLingshou825">
SELECT
group_id,
cust_name,
cust_idc,
cust_isn,
cur_bal_d,
bal_loan,
loan_ave,
is_sx,
is_yx,
sx_bal,
is_yxht,
is_xyk,
fshl,
is_sd,
dian,
shui,
tax,
open_num,
dep_bal,
fin_bal,
is_grhx,
is_cfyx,
is_yxsbk,
is_2to3_sbk,
is_ylj_to_sbk,
region_code,
ops_dept,
cust_type,
is_365zf,
is_180zf,
is_90zf,
is_30zf,
dt,
is_hxsd
FROM group_cust_count_lingshou_825
<where>
group_id = #{groupId}
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="custName != null and custName != ''">and cust_name like concat('%', #{custName}, '%')</if>
<if test="custIdc != null and custIdc != ''">and cust_idc like concat('%', #{custIdc}, '%')</if>
</where>
</select>
<select id="selectGsCustList" resultType="com.ruoyi.group.domain.entity.GroupCustCountGongsi825">
SELECT
group_id,
cust_name,
social_credit_code,
cust_isn,
hq_cur_balance,
bz_cur_balance,
is_credit,
loan_balance_cny,
loan_year_dailyaverage,
finance_prod_716_open_flag,
finance_prod_716_balance,
finance_prod_711_open_flag,
finance_prod_711_balance,
intl_bussiness_jcbh_open_flag,
is_ustr,
ustr_count_per_m,
ustr_bal_m,
eleccharge_sign_flag,
watercharge_sign_flag,
taxdeduction_sign_flag,
pjb,
czb,
sfb,
mrb,
szst,
is_open_sts,
intl_bussiness_open_flag,
intl_bussiness_325_open_flag,
region_code,
ops_dept,
cust_type,
is_htqy,
dept_name,
outlets_id,
outlets_name,
user_name,
dept_id,
is_365zf,
is_180zf,
is_90zf,
is_30zf,
dt,
is_ph
FROM group_cust_count_gongsi_825
<where>
group_id = #{groupId}
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
<if test="custName != null and custName != ''">and cust_name like concat('%', #{custName}, '%')</if>
<if test="socialCreditCode != null and socialCreditCode != ''">and social_credit_code like concat('%', #{socialCreditCode}, '%')</if>
</where>
</select>
</mapper>

View File

@@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataPageInfo; import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.ibs.cmpm.domain.dto.CustLevelDTO; import com.ruoyi.ibs.cmpm.domain.dto.CustLevelDTO;
import com.ruoyi.ibs.cmpm.domain.dto.CustManagerDTO; import com.ruoyi.ibs.cmpm.domain.dto.CustManagerDTO;
@@ -104,6 +105,17 @@ public class GridCmpmController extends BaseController {
return success(gridCmpmService.getCustLevelListForManager()); return success(gridCmpmService.getCustLevelListForManager());
} }
@PostMapping("/custManager/export")
@Log(title = "绩效网格-管户报表异步导出", businessType = BusinessType.EXPORT)
@ApiOperation("管户报表异步导出")
public AjaxResult exportCustManager(@RequestBody(required = false) CustManagerDTO custManagerDTO) {
if (custManagerDTO == null) {
custManagerDTO = new CustManagerDTO();
}
String taskId = gridCmpmService.exportCustManagerAsync(custManagerDTO);
return AjaxResult.success("导出任务创建成功,请稍后前往下载中心下载", taskId);
}
@GetMapping("/custLevel/count") @GetMapping("/custLevel/count")
@Log(title = "绩效网格-查询客户分层等级") @Log(title = "绩效网格-查询客户分层等级")
@ApiOperation("查询客户分层等级") @ApiOperation("查询客户分层等级")
@@ -117,4 +129,5 @@ public class GridCmpmController extends BaseController {
public AjaxResult selectCustBaseInfoList(@RequestBody CustBaseInfo custBaseInfo) { public AjaxResult selectCustBaseInfoList(@RequestBody CustBaseInfo custBaseInfo) {
return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ; return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ;
} }
} }

View File

@@ -1,7 +1,6 @@
package com.ruoyi.ibs.cmpm.domain.vo; package com.ruoyi.ibs.cmpm.domain.vo;
import com.baomidou.mybatisplus.annotation.TableId; import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -24,74 +23,92 @@ public class DwbRetailCustLevelManagerDetailVO {
/** 网点号 */ /** 网点号 */
@ApiModelProperty(value = "网点号") @ApiModelProperty(value = "网点号")
@ExcelProperty("网点号")
private String outletId; private String outletId;
/** 网点名 */ /** 网点名 */
@ApiModelProperty(value = "网点名") @ApiModelProperty(value = "网点名")
@ExcelProperty("网点名")
private String outletName; private String outletName;
/** 支行号 */ /** 支行号 */
@ApiModelProperty(value = "支行号") @ApiModelProperty(value = "支行号")
@ExcelProperty("支行号")
private String branchId; private String branchId;
/** 支行名 */ /** 支行名 */
@ApiModelProperty(value = "支行名") @ApiModelProperty(value = "支行名")
@ExcelProperty("支行名")
private String branchName; private String branchName;
/** 客户名称 */ /** 客户名称 */
@ApiModelProperty(value = "客户名称") @ApiModelProperty(value = "客户名称")
@ExcelProperty("客户名称")
private String custName; private String custName;
/** 客户证件号 */ /** 客户证件号 */
@ApiModelProperty(value = "客户证件号") @ApiModelProperty(value = "客户证件号")
@ExcelProperty("客户证件号")
private String custIdc; private String custIdc;
/** 客户内码 */ /** 客户内码 */
@ApiModelProperty(value = "客户内码") @ApiModelProperty(value = "客户内码")
@ExcelProperty("客户内码")
private String custIsn; private String custIsn;
/** 年龄 */ /** 年龄 */
@ApiModelProperty(value = "年龄") @ApiModelProperty(value = "年龄")
@ExcelProperty("年龄")
private String custAge; private String custAge;
/** 性别 */ /** 性别 */
@ApiModelProperty(value = "性别") @ApiModelProperty(value = "性别")
@ExcelProperty("性别")
private String custSex; private String custSex;
/** 联系电话 */ /** 联系电话 */
@ApiModelProperty(value = "联系电话") @ApiModelProperty(value = "联系电话")
@ExcelProperty("联系电话")
private String custPhone; private String custPhone;
/** 联系地址 */ /** 联系地址 */
@ApiModelProperty(value = "联系地址") @ApiModelProperty(value = "联系地址")
@ExcelProperty("联系地址")
private String custAddress; private String custAddress;
/** 总资产余额 */ /** 总资产余额 */
@ApiModelProperty(value = "总资产余额") @ApiModelProperty(value = "总资产余额")
@ExcelProperty("总资产余额")
private BigDecimal custAumBal; private BigDecimal custAumBal;
/** 总资产余额较上月变动 */ /** 总资产余额较上月变动 */
@ApiModelProperty(value = "总资产余额较上月变动") @ApiModelProperty(value = "总资产余额较上月变动")
@ExcelProperty("总资产余额较上月变动")
private BigDecimal aumBalCompLm; private BigDecimal aumBalCompLm;
/** 总资产月日均 */ /** 总资产月日均 */
@ApiModelProperty(value = "总资产月日均") @ApiModelProperty(value = "总资产月日均")
@ExcelProperty("总资产余额月日均")
private BigDecimal custAumMonthAvg; private BigDecimal custAumMonthAvg;
/** 客户星级 */ /** 客户星级 */
@ApiModelProperty(value = "客户星级") @ApiModelProperty(value = "客户星级")
@ExcelProperty("客户星级")
private String custLevel; private String custLevel;
/** 星级较上月变动 */ /** 星级较上月变动 */
@ApiModelProperty(value = "星级较上月变动") @ApiModelProperty(value = "星级较上月变动")
@ExcelProperty("星级较上月变动")
private String custLevelCompLm; private String custLevelCompLm;
/** 责任人 */ /** 责任人 */
@ApiModelProperty(value = "责任人") @ApiModelProperty(value = "责任人")
@ExcelProperty("责任人")
private String managerName; private String managerName;
/** 责任人柜员号 */ /** 责任人柜员号 */
@ApiModelProperty(value = "责任人柜员号") @ApiModelProperty(value = "责任人柜员号")
@ExcelProperty("责任人柜员号")
private String managerId; private String managerId;
} }

View File

@@ -8,11 +8,15 @@ import com.ruoyi.ibs.cmpm.domain.entity.GridCmpm;
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailCustLevelManagerDetailVO; import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailCustLevelManagerDetailVO;
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO; import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO;
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmClaimVO; import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmClaimVO;
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO;
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo; import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* @Author 吴凯程 * @Author 吴凯程
@@ -43,6 +47,11 @@ public interface GridCmpmMapper {
List<DwbRetailCustLevelManagerDetailVO> getCustManagerList(CustManagerDTO custManagerDTO); List<DwbRetailCustLevelManagerDetailVO> getCustManagerList(CustManagerDTO custManagerDTO);
List<DwbRetailCustLevelManagerDetailVO> getCustManagerListByPage(@Param("dto") CustManagerDTO custManagerDTO,
@Param("headId") String headId,
@Param("offset") Integer offset,
@Param("pageSize") Integer pageSize);
int getCustLevelCount(CustManagerDTO custManagerDTO); int getCustLevelCount(CustManagerDTO custManagerDTO);
List<CustBaseInfo> selectCustInfoFromGridCmpm(CustBaseInfo custBaseInfo); List<CustBaseInfo> selectCustInfoFromGridCmpm(CustBaseInfo custBaseInfo);
@@ -60,6 +69,23 @@ public interface GridCmpmMapper {
List<String> selectManagerList(); List<String> selectManagerList();
/**
* 全量查询绩效网格客户列表(用于客群管户关系匹配,不按客户经理过滤)
* @param gridType 网格类型
* @param headId 总行ID
* @return 客户列表(包含管户关系信息)
*/
List<GridCmpmVO> getAllCustomerListForImport(@Param("gridType") String gridType, @Param("headId") String headId);
/**
* 根据客户ID和类型列表查询管户关系用于动态客群更新管户关系
* @param gridType 网格类型retail/corporate
* @param headId 总行ID
* @param custInfoList 客户信息列表包含custId和custType
* @return 客户管户关系列表
*/
List<GridCmpmVO> getRelationshipsByCustList(@Param("gridType") String gridType, @Param("headId") String headId, @Param("custInfoList") List<Map<String, String>> custInfoList);
// List<CustBaseInfo> selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo); // List<CustBaseInfo> selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo);
// //

View File

@@ -1,11 +1,15 @@
package com.ruoyi.ibs.cmpm.service; package com.ruoyi.ibs.cmpm.service;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.ibs.cmpm.domain.dto.CustLevelDTO; import com.ruoyi.ibs.cmpm.domain.dto.CustLevelDTO;
import com.ruoyi.ibs.cmpm.domain.dto.CustManagerDTO; import com.ruoyi.ibs.cmpm.domain.dto.CustManagerDTO;
import com.ruoyi.ibs.cmpm.domain.dto.GridCmpmListDTO; import com.ruoyi.ibs.cmpm.domain.dto.GridCmpmListDTO;
@@ -14,11 +18,19 @@ import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailCustLevelManagerDetailVO;
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO; import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO;
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO; import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO;
import com.ruoyi.ibs.cmpm.mapper.GridCmpmMapper; import com.ruoyi.ibs.cmpm.mapper.GridCmpmMapper;
import com.ruoyi.ibs.task.domain.entity.ImportExportTask;
import com.ruoyi.ibs.task.mapper.ImportExportTaskMapper;
import com.ruoyi.system.enums.OssFileEnum;
import com.ruoyi.system.service.OssFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -27,6 +39,7 @@ import java.util.stream.Collectors;
* @Date 2025/10/15 * @Date 2025/10/15
**/ **/
@Service @Service
@Slf4j
public class GridCmpmService { public class GridCmpmService {
@@ -39,6 +52,15 @@ public class GridCmpmService {
@Resource @Resource
private RedisCache redisCache; private RedisCache redisCache;
@Resource
private ImportExportTaskMapper importExportTaskMapper;
@Resource
private OssFileService ossFileService;
@Resource(name = "excelExportExecutor")
private ExecutorService executorService;
private final String CUST_LEVEL_COUNT_KEY = "GRID_CMPM_CUST_LEVEL_COUNT_"; private final String CUST_LEVEL_COUNT_KEY = "GRID_CMPM_CUST_LEVEL_COUNT_";
public List<GridCmpmVO> selectManageList(GridCmpmListDTO gridCmpmRetailListDTO){ public List<GridCmpmVO> selectManageList(GridCmpmListDTO gridCmpmRetailListDTO){
@@ -94,33 +116,32 @@ public class GridCmpmService {
} }
public List<DwbRetailCustLevelManagerDetailVO> selectCustManagerList(CustManagerDTO custManagerDTO) { public List<DwbRetailCustLevelManagerDetailVO> selectCustManagerList(CustManagerDTO custManagerDTO) {
String userRole = SecurityUtils.userRole(); applyCustManagerScope(custManagerDTO);
if (userRole.equals("manager")){
//客户经理查自己
custManagerDTO.setManagerId(SecurityUtils.getUsername());
}else if (userRole.equals("outlet")){
//网点管理员查网点
custManagerDTO.setOutletId(String.valueOf(SecurityUtils.getDeptId()));
}else if (userRole.equals("branch")){
//支行管理员查支行
custManagerDTO.setBranchId(String.valueOf(SecurityUtils.getDeptId()));
}
//其他角色查全部
return gridCmpmMapper.getCustManagerList(custManagerDTO); return gridCmpmMapper.getCustManagerList(custManagerDTO);
} }
public String exportCustManagerAsync(CustManagerDTO custManagerDTO) {
applyCustManagerScope(custManagerDTO);
String headId = SecurityUtils.getHeadId();
ImportExportTask task = new ImportExportTask();
String taskId = IdUtils.randomUUID();
task.setId(taskId);
task.setStatus("0");
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
task.setFileName(sdf.format(now) + "管户报表导出");
task.setUserName(SecurityUtils.getUsername());
task.setCreateTime(now);
importExportTaskMapper.insert(task);
String userName = SecurityUtils.getUsername();
executorService.submit(() -> doExportCustManager(taskId, custManagerDTO, userName, headId));
return taskId;
}
public int custLevelCount(CustManagerDTO custManagerDTO) { public int custLevelCount(CustManagerDTO custManagerDTO) {
String userRole = SecurityUtils.userRole(); applyCustManagerScope(custManagerDTO);
if (userRole.equals("manager")){
//客户经理查自己
custManagerDTO.setManagerId(SecurityUtils.getUsername());
}else if (userRole.equals("outlet")){
//网点管理员查网点
custManagerDTO.setOutletId(String.valueOf(SecurityUtils.getDeptId()));
}else if (userRole.equals("branch")){
//支行管理员查支行
custManagerDTO.setBranchId(String.valueOf(SecurityUtils.getDeptId()));
}
return gridCmpmMapper.getCustLevelCount(custManagerDTO); return gridCmpmMapper.getCustLevelCount(custManagerDTO);
} }
@@ -274,6 +295,64 @@ public class GridCmpmService {
return custLevelCountMap; return custLevelCountMap;
} }
private void applyCustManagerScope(CustManagerDTO custManagerDTO) {
String userRole = SecurityUtils.userRole();
if (userRole.equals("manager")) {
custManagerDTO.setManagerId(SecurityUtils.getUsername());
} else if (userRole.equals("outlet")) {
custManagerDTO.setOutletId(String.valueOf(SecurityUtils.getDeptId()));
} else if (userRole.equals("branch")) {
custManagerDTO.setBranchId(String.valueOf(SecurityUtils.getDeptId()));
}
}
private void doExportCustManager(String taskId, CustManagerDTO custManagerDTO, String userName, String headId) {
ImportExportTask task = importExportTaskMapper.selectById(taskId);
File tempFile = null;
try {
tempFile = File.createTempFile("管户报表导出_" + taskId + "-", ".xlsx");
ExcelWriter excelWriter = EasyExcel.write(tempFile, DwbRetailCustLevelManagerDetailVO.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("管户报表").build();
int pageSize = 1000;
int pageNum = 0;
List<DwbRetailCustLevelManagerDetailVO> pageData;
do {
pageData = gridCmpmMapper.getCustManagerListByPage(custManagerDTO, headId, pageNum * pageSize, pageSize);
if (!pageData.isEmpty()) {
excelWriter.write(pageData, writeSheet);
}
pageNum++;
} while (pageData.size() == pageSize);
excelWriter.finish();
String ossUUId = ossFileService.uploadFileToOss(
OssFileEnum.CUST_MANAGER_REPORT,
tempFile,
"管户报表导出_" + taskId + ".xlsx",
userName
);
task.setStatus("1");
task.setFileUrl(ossUUId);
task.setFinishTime(new Date());
importExportTaskMapper.updateById(task);
} catch (Exception e) {
task.setStatus("2");
task.setFinishTime(new Date());
task.setErrorMsg(e.getMessage());
importExportTaskMapper.updateById(task);
} finally {
if (Objects.nonNull(tempFile)) {
boolean deleted = tempFile.delete();
if (!deleted) {
log.warn("临时文件删除失败: {}", tempFile.getAbsolutePath());
}
}
}
}
// 计算客户等级变化情况 // 计算客户等级变化情况
private Map<String, Integer> calculateLevelChanges(Map<String, Integer> historyMap, Map<String, Integer> currentMap) { private Map<String, Integer> calculateLevelChanges(Map<String, Integer> historyMap, Map<String, Integer> currentMap) {
Map<String, Integer> changesMap = new HashMap<>(); Map<String, Integer> changesMap = new HashMap<>();
@@ -302,4 +381,28 @@ public class GridCmpmService {
return changesMap; return changesMap;
} }
/**
* 全量查询绩效网格客户列表用于客群管户关系匹配不依赖SecurityUtils
* @param gridType 网格类型
* @param headId 总行ID
* @return 客户列表(包含管户关系信息)
*/
public List<GridCmpmVO> selectAllForImport(String gridType, String headId) {
return gridCmpmMapper.getAllCustomerListForImport(gridType, headId);
}
/**
* 根据客户ID列表查询管户关系用于动态客群更新管户关系
* @param gridType 网格类型retail/corporate
* @param headId 总行ID
* @param custInfoList 客户信息列表包含custId和custType
* @return 客户管户关系列表
*/
public List<GridCmpmVO> getRelationshipsByCustList(String gridType, String headId, List<Map<String, String>> custInfoList) {
if (custInfoList == null || custInfoList.isEmpty()) {
return new ArrayList<>();
}
return gridCmpmMapper.getRelationshipsByCustList(gridType, headId, custInfoList);
}
} }

View File

@@ -18,8 +18,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.rmi.ServerException; import java.rmi.ServerException;
@@ -220,4 +222,18 @@ public class MyCustomerController extends BaseController {
} }
return AjaxResult.success(iSysCampaignGroupCustomerService.appointCustCamp( custId, custName, custIdc, custPhone, custIsn,socialCreditCode,lpName, campaignId, custType)); return AjaxResult.success(iSysCampaignGroupCustomerService.appointCustCamp( custId, custName, custIdc, custPhone, custIsn,socialCreditCode,lpName, campaignId, custType));
} }
@Log(title = "我的客户-异步导入企业客户价值分层", businessType = com.ruoyi.common.enums.BusinessType.IMPORT)
@PostMapping(value = "/importBusinessCustLevelAsync", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ApiOperation("异步导入企业客户价值分层")
public AjaxResult importBusinessCustLevelAsync(@RequestPart("file") MultipartFile file) {
return AjaxResult.success("导入任务已提交,后台正在处理", myCustomerService.importBusinessCustLevelAsync(file));
}
@Log(title = "我的客户-查询企业客户价值分层导入状态")
@GetMapping("/importBusinessCustLevelStatus/{taskId}")
@ApiOperation("查询企业客户价值分层导入状态")
public AjaxResult importBusinessCustLevelStatus(@PathVariable String taskId) {
return AjaxResult.success(myCustomerService.getBusinessCustLevelImportStatus(taskId));
}
} }

View File

@@ -0,0 +1,14 @@
package com.ruoyi.ibs.customerselect.domain;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class BusinessCustLevelImportExcelDTO {
@ExcelProperty("统信码")
private String socialCreditCode;
@ExcelProperty("价值分层")
private String custLevel;
}

View File

@@ -0,0 +1,30 @@
package com.ruoyi.ibs.customerselect.domain;
import lombok.Data;
import java.io.Serializable;
@Data
public class BusinessCustLevelImportTaskVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 0-处理中 1-成功 2-失败
*/
private String status;
private String message;
private Integer totalCount;
private Integer successCount;
private Integer ignoredCount;
private String userName;
private String createTime;
private String finishTime;
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.ibs.customerselect.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.core.domain.entity.CustInfoBusiness; import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportExcelDTO;
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo; import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
import com.ruoyi.ibs.customerselect.domain.CustInfoDeleteFromAnchor; import com.ruoyi.ibs.customerselect.domain.CustInfoDeleteFromAnchor;
import com.ruoyi.ibs.customerselect.domain.CustInfoUpdateFromAnchor; import com.ruoyi.ibs.customerselect.domain.CustInfoUpdateFromAnchor;
@@ -242,4 +243,10 @@ public interface CustInfoBusinessMapper extends BaseMapper<CustInfoBusiness>
public int insertCustomersToBusinessByScCode(List<SysGroupCustomer> sysGroupCustomers); public int insertCustomersToBusinessByScCode(List<SysGroupCustomer> sysGroupCustomers);
List<CustInfoBusiness> selectRecord(String socialCreditCode); List<CustInfoBusiness> selectRecord(String socialCreditCode);
List<String> selectExistingSocialCreditCodes(@Param("socialCreditCodes") List<String> socialCreditCodes,
@Param("deptCode") String deptCode);
int batchUpdateCustLevelBySocialCreditCode(@Param("list") List<BusinessCustLevelImportExcelDTO> list,
@Param("deptCode") String deptCode);
} }

View File

@@ -1,6 +1,7 @@
package com.ruoyi.ibs.customerselect.service; package com.ruoyi.ibs.customerselect.service;
import com.ruoyi.ibs.customerselect.domain.*; import com.ruoyi.ibs.customerselect.domain.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
@@ -41,4 +42,8 @@ public interface IMyCustomerService {
public CustListSearchVo selectCustomListSearchVo(CustBaseInfo sysCustomerBasedata); public CustListSearchVo selectCustomListSearchVo(CustBaseInfo sysCustomerBasedata);
MerchantMcspInfo selectmerchantMessage(String custId); MerchantMcspInfo selectmerchantMessage(String custId);
String importBusinessCustLevelAsync(MultipartFile file);
BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId);
} }

View File

@@ -1,13 +1,19 @@
package com.ruoyi.ibs.customerselect.service.Impl; package com.ruoyi.ibs.customerselect.service.Impl;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.annotation.DataScope; import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.CustInfoBusiness; import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.ibs.dashboard.service.NotificationService;
import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportExcelDTO;
import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportTaskVO;
import com.ruoyi.ibs.customerselect.domain.*; import com.ruoyi.ibs.customerselect.domain.*;
import com.ruoyi.ibs.customerselect.domain.vo.GridRelateVO; import com.ruoyi.ibs.customerselect.domain.vo.GridRelateVO;
import com.ruoyi.ibs.customerselect.mapper.CustInfoBusinessMapper; import com.ruoyi.ibs.customerselect.mapper.CustInfoBusinessMapper;
@@ -32,9 +38,16 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -45,6 +58,10 @@ import java.util.stream.Collectors;
@Service @Service
public class MyCustomerServiceImpl implements IMyCustomerService { public class MyCustomerServiceImpl implements IMyCustomerService {
private static final String BUSINESS_CUST_LEVEL_IMPORT_TASK_KEY = "BUSINESS_CUST_LEVEL_IMPORT_TASK_";
private static final int BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE = 1000;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Autowired @Autowired
private CustInfoBusinessMapper custInfoBusinessMapper; private CustInfoBusinessMapper custInfoBusinessMapper;
@@ -80,6 +97,12 @@ public class MyCustomerServiceImpl implements IMyCustomerService {
@Resource @Resource
private RedisCache redisCache; private RedisCache redisCache;
@Resource
private NotificationService notificationService;
@Resource(name = "excelImportExecutor")
private ExecutorService excelImportExecutor;
private static Logger logger = LoggerFactory.getLogger(MyCustomerServiceImpl.class); private static Logger logger = LoggerFactory.getLogger(MyCustomerServiceImpl.class);
/** /**
@@ -467,5 +490,154 @@ public class MyCustomerServiceImpl implements IMyCustomerService {
return merchantMcspInfoMapper.selectOne(new LambdaQueryWrapper<MerchantMcspInfo>().eq(MerchantMcspInfo::getCustId,custId)); return merchantMcspInfoMapper.selectOne(new LambdaQueryWrapper<MerchantMcspInfo>().eq(MerchantMcspInfo::getCustId,custId));
} }
@Override
public String importBusinessCustLevelAsync(MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new ServiceException("导入文件不能为空");
}
byte[] fileBytes;
try {
fileBytes = file.getBytes();
} catch (IOException e) {
throw new ServiceException("读取导入文件失败");
}
String taskId = IdUtils.fastSimpleUUID();
String userName = SecurityUtils.getUsername();
String deptCode = SecurityUtils.getHeadId();
BusinessCustLevelImportTaskVO taskVO = new BusinessCustLevelImportTaskVO();
taskVO.setStatus("0");
taskVO.setMessage("导入任务已提交,后台正在处理");
taskVO.setTotalCount(0);
taskVO.setSuccessCount(0);
taskVO.setIgnoredCount(0);
taskVO.setUserName(userName);
taskVO.setCreateTime(formatNow());
cacheBusinessCustLevelImportTask(taskId, taskVO);
excelImportExecutor.submit(() -> doImportBusinessCustLevel(taskId, userName, deptCode, fileBytes));
return taskId;
}
@Override
public BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId) {
BusinessCustLevelImportTaskVO taskVO = redisCache.getCacheObject(getBusinessCustLevelImportTaskKey(taskId));
if (taskVO == null) {
throw new ServiceException("导入任务不存在或已过期");
}
return taskVO;
}
private void doImportBusinessCustLevel(String taskId, String userName, String deptCode, byte[] fileBytes) {
BusinessCustLevelImportTaskVO taskVO = redisCache.getCacheObject(getBusinessCustLevelImportTaskKey(taskId));
if (taskVO == null) {
taskVO = new BusinessCustLevelImportTaskVO();
taskVO.setUserName(userName);
taskVO.setCreateTime(formatNow());
}
try {
List<BusinessCustLevelImportExcelDTO> importRows = EasyExcel.read(new ByteArrayInputStream(fileBytes))
.head(BusinessCustLevelImportExcelDTO.class)
.sheet()
.doReadSync();
Map<String, String> levelMap = new LinkedHashMap<>();
if (importRows != null) {
for (BusinessCustLevelImportExcelDTO row : importRows) {
String socialCreditCode = normalizeImportCell(row.getSocialCreditCode());
String custLevel = normalizeImportCell(row.getCustLevel());
if (StringUtils.isEmpty(socialCreditCode)) {
continue;
}
levelMap.put(socialCreditCode, custLevel);
}
}
if (levelMap.isEmpty()) {
throw new ServiceException("Excel中未识别到有效的统信码和价值分层数据");
}
List<String> existingSocialCreditCodes = getExistingBusinessSocialCreditCodes(new ArrayList<>(levelMap.keySet()), deptCode);
Set<String> existingCodeSet = new HashSet<>(existingSocialCreditCodes);
List<BusinessCustLevelImportExcelDTO> updateList = new ArrayList<>();
for (Map.Entry<String, String> entry : levelMap.entrySet()) {
if (!existingCodeSet.contains(entry.getKey())) {
continue;
}
BusinessCustLevelImportExcelDTO dto = new BusinessCustLevelImportExcelDTO();
dto.setSocialCreditCode(entry.getKey());
dto.setCustLevel(entry.getValue());
updateList.add(dto);
}
batchUpdateBusinessCustLevel(updateList, deptCode);
int totalCount = levelMap.size();
int successCount = updateList.size();
int ignoredCount = totalCount - successCount;
String message = String.format("公司客户视图分层分类数据导入完成,成功更新%d条忽略%d条", successCount, ignoredCount);
taskVO.setStatus("1");
taskVO.setMessage(message);
taskVO.setTotalCount(totalCount);
taskVO.setSuccessCount(successCount);
taskVO.setIgnoredCount(ignoredCount);
taskVO.setFinishTime(formatNow());
cacheBusinessCustLevelImportTask(taskId, taskVO);
notificationService.sendNotification(userName, message);
} catch (Exception e) {
String errorMsg = StringUtils.isNotEmpty(e.getMessage()) ? e.getMessage() : "导入失败,请检查文件内容";
taskVO.setStatus("2");
taskVO.setMessage(errorMsg);
taskVO.setFinishTime(formatNow());
cacheBusinessCustLevelImportTask(taskId, taskVO);
notificationService.sendNotification(userName, "公司客户视图分层分类数据导入失败:" + errorMsg);
logger.error("公司客户视图分层分类数据导入失败taskId={}", taskId, e);
}
}
private void batchUpdateBusinessCustLevel(List<BusinessCustLevelImportExcelDTO> updateList, String deptCode) {
if (updateList == null || updateList.isEmpty()) {
return;
}
for (int i = 0; i < updateList.size(); i += BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE) {
int endIndex = Math.min(i + BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE, updateList.size());
custInfoBusinessMapper.batchUpdateCustLevelBySocialCreditCode(updateList.subList(i, endIndex), deptCode);
}
}
private List<String> getExistingBusinessSocialCreditCodes(List<String> socialCreditCodes, String deptCode) {
if (socialCreditCodes == null || socialCreditCodes.isEmpty()) {
return Collections.emptyList();
}
List<String> existingSocialCreditCodes = new ArrayList<>();
for (int i = 0; i < socialCreditCodes.size(); i += BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE) {
int endIndex = Math.min(i + BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE, socialCreditCodes.size());
List<String> batchCodes = socialCreditCodes.subList(i, endIndex);
List<String> batchResult = custInfoBusinessMapper.selectExistingSocialCreditCodes(batchCodes, deptCode);
if (batchResult != null && !batchResult.isEmpty()) {
existingSocialCreditCodes.addAll(batchResult);
}
}
return existingSocialCreditCodes;
}
private void cacheBusinessCustLevelImportTask(String taskId, BusinessCustLevelImportTaskVO taskVO) {
redisCache.setCacheObject(getBusinessCustLevelImportTaskKey(taskId), taskVO, 24, TimeUnit.HOURS);
}
private String getBusinessCustLevelImportTaskKey(String taskId) {
return BUSINESS_CUST_LEVEL_IMPORT_TASK_KEY + taskId;
}
private String normalizeImportCell(String value) {
if (value == null) {
return null;
}
String trimmedValue = value.trim();
return trimmedValue.isEmpty() ? null : trimmedValue;
}
private String formatNow() {
return LocalDateTime.now().format(DATE_TIME_FORMATTER);
}
} }

View File

@@ -173,6 +173,7 @@ public class DashboardController extends BaseController {
public TableDataInfo list(SysNotice notice) public TableDataInfo list(SysNotice notice)
{ {
startPage(); startPage();
notice.setCurrentHeadDeptId(SecurityUtils.getHeadId() + "000");
List<SysNotice> list = noticeService.selectNoticeList(notice); List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list); return getDataTable(list);
} }

View File

@@ -80,6 +80,16 @@ public class DrawGridController extends BaseController {
return getDataTable(gridList, page); return getDataTable(gridList, page);
} }
@ApiOperation("获取网格列表(用于客群创建,简化查询)")
@Log(title = "自定义绘制网格--获取网格列表(客群创建用)")
@GetMapping("/simpleList")
public R<List<DrawGridListVO>> getSimpleGridList(DrawGridListDTO drawGridListDTO) {
drawGridListDTO.setUserName(SecurityUtils.getUsername());
drawGridListDTO.setDeptId(SecurityUtils.getDeptId());
List<DrawGridListVO> gridList = drawGridService.getSimpleGridList(drawGridListDTO);
return R.ok(gridList);
}
@ApiOperation("分页获取网格内客户列表") @ApiOperation("分页获取网格内客户列表")
@Log(title = "自定义绘制网格--分页获取网格内客户列表") @Log(title = "自定义绘制网格--分页获取网格内客户列表")
@GetMapping("/cust/list") @GetMapping("/cust/list")

View File

@@ -17,6 +17,4 @@ public interface DrawGridCustUserUnbindMapper extends BaseMapper<DrawGridCustUse
List<DrawGridCustVO> getCustList(DrawGridCustListDTO drawGridCustListDTO); List<DrawGridCustVO> getCustList(DrawGridCustListDTO drawGridCustListDTO);
List<DrawGridCustVO> getCustListByManager(DrawGridCustListDTO drawGridCustListDTO); List<DrawGridCustVO> getCustListByManager(DrawGridCustListDTO drawGridCustListDTO);
} }

View File

@@ -2,7 +2,11 @@ package com.ruoyi.ibs.draw.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate; import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate;
import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* @Author 吴凯程 * @Author 吴凯程
@@ -10,4 +14,16 @@ import org.apache.ibatis.annotations.Mapper;
**/ **/
@Mapper @Mapper
public interface DrawGridShapeRelateMapper extends BaseMapper<DrawGridShapeRelate> { public interface DrawGridShapeRelateMapper extends BaseMapper<DrawGridShapeRelate> {
/**
* 根据网格ID查询关联的图形用于异步导入客群原生SQL绕过拦截器
* @param gridId 网格ID
* @return 图形关联列表
*/
List<DrawGridShapeRelate> selectByGridId(@Param("gridId") Long gridId);
/**
* 根据绘制网格ID查询所有客户用于客群导入直接拼接headId绕过拦截器
*/
List<RegionCustUser> selectCustByDrawGridId(@Param("gridId") Long gridId, @Param("headId") String headId);
} }

View File

@@ -24,6 +24,13 @@ public interface DrawGridService {
List<DrawGridListVO> getGridList(DrawGridListDTO drawGridListDTO); List<DrawGridListVO> getGridList(DrawGridListDTO drawGridListDTO);
/**
* 获取网格列表(用于客群创建,简化查询不统计客户数量)
* @param drawGridListDTO 查询条件
* @return 网格列表(不含客户数量)
*/
List<DrawGridListVO> getSimpleGridList(DrawGridListDTO drawGridListDTO);

View File

@@ -334,6 +334,15 @@ public class DrawGridServiceImpl implements DrawGridService {
return gridList; return gridList;
} }
@Override
public List<DrawGridListVO> getSimpleGridList(DrawGridListDTO drawGridListDTO) {
if (SecurityUtils.userRole().equals("manager")) {
return drawGridMapper.getGridListByManager(drawGridListDTO);
} else {
return drawGridMapper.getGridList(drawGridListDTO);
}
}
private CustCountDTO getCustCountByGrid(Long gridId) { private CustCountDTO getCustCountByGrid(Long gridId) {
CustCountDTO custCountDTO = new CustCountDTO(); CustCountDTO custCountDTO = new CustCountDTO();
LambdaQueryWrapper<DrawGridShapeRelate> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<DrawGridShapeRelate> queryWrapper = new LambdaQueryWrapper<>();

View File

@@ -1,56 +1,68 @@
package com.ruoyi.ibs.grid.controller; package com.ruoyi.ibs.grid.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.page.TableDataPageInfo; import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshouNew825;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshouNew825;
import com.ruoyi.ibs.grid.service.GridCountService; import com.ruoyi.ibs.grid.service.GridCountService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; 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.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.List; import java.util.List;
@Api(tags = "网格汇总") @Api(tags = "网格汇总")
@RestController @RestController
@RequestMapping("/ibs/GridCount/") @RequestMapping("/ibs/GridCount/")
public class GridCountController extends BaseController { public class GridCountController extends BaseController {
@Autowired @Autowired
GridCountService service; private GridCountService service;
@ApiOperation(value = "查询零售汇总网格列表") @ApiOperation(value = "查询零售汇总网格列表")
@GetMapping("LsList") @GetMapping("LsList")
public TableDataPageInfo<GridCmpmCountLingshou> selectLsCountList(String town, String village,String dt) { public TableDataPageInfo<?> selectLsCountList(String town, String village, String dt) {
Page<Object> page = startPage(); Page<Object> page = startPage();
List<GridCmpmCountLingshou> list = service.selectLsCountList(town, village,dt); if (isNewRetailTenant()) {
List<GridCmpmCountLingshouNew825> list = service.selectLsCountListNew825(town, village, dt);
return getDataTable(list, page);
}
List<GridCmpmCountLingshou> list = service.selectLsCountList(town, village, dt);
return getDataTable(list, page); return getDataTable(list, page);
} }
@ApiOperation(value = "查询公司汇总网格列表") @ApiOperation(value = "查询公司汇总网格列表")
@GetMapping("GsList") @GetMapping("GsList")
public TableDataPageInfo<GridCmpmCountGongsi> selectGsCountList(String town, String village,String dt) { public TableDataPageInfo<GridCmpmCountGongsi> selectGsCountList(String town, String village, String dt) {
Page<Object> page = startPage(); Page<Object> page = startPage();
List<GridCmpmCountGongsi> list = service.selectGsCountList(town, village,dt); List<GridCmpmCountGongsi> list = service.selectGsCountList(town, village, dt);
return getDataTable(list, page); return getDataTable(list, page);
} }
@ApiOperation(value = "导出零售汇总网格", produces = "application/octet-stream") @ApiOperation(value = "导出零售汇总网格", produces = "application/octet-stream")
@GetMapping("exportLs") @GetMapping("exportLs")
public void exportLs(HttpServletResponse response, String town, String village,String dt) { public void exportLs(HttpServletResponse response, String town, String village, String dt) {
try { try {
if (isNewRetailTenant()) {
ExcelUtil<GridCmpmCountLingshouNew825> util = new ExcelUtil<>(GridCmpmCountLingshouNew825.class);
util.exportExcel(response, service.selectLsCountListNew825(town, village, dt), "零售网格汇总");
return;
}
ExcelUtil<GridCmpmCountLingshou> util = new ExcelUtil<>(GridCmpmCountLingshou.class); ExcelUtil<GridCmpmCountLingshou> util = new ExcelUtil<>(GridCmpmCountLingshou.class);
util.exportExcel(response, service.selectLsCountList(town, village,dt), "零售网格汇总"); util.exportExcel(response, service.selectLsCountList(town, village, dt), "零售网格汇总");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -59,10 +71,10 @@ public class GridCountController extends BaseController {
@Log(title = "导出公司汇总网格") @Log(title = "导出公司汇总网格")
@ApiOperation(value = "导出公司汇总网格", produces = "application/octet-stream") @ApiOperation(value = "导出公司汇总网格", produces = "application/octet-stream")
@GetMapping("exportGs") @GetMapping("exportGs")
public void exportGs(HttpServletResponse response, String town, String village,String dt) { public void exportGs(HttpServletResponse response, String town, String village, String dt) {
try { try {
ExcelUtil<GridCmpmCountGongsi> util = new ExcelUtil<>(GridCmpmCountGongsi.class); ExcelUtil<GridCmpmCountGongsi> util = new ExcelUtil<>(GridCmpmCountGongsi.class);
util.exportExcel(response, service.selectGsCountList(town, village,dt), "公司网格汇总"); util.exportExcel(response, service.selectGsCountList(town, village, dt), "公司网格汇总");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -71,45 +83,55 @@ public class GridCountController extends BaseController {
@Log(title = "查询零售客户明细") @Log(title = "查询零售客户明细")
@ApiOperation(value = "查询零售客户明细") @ApiOperation(value = "查询零售客户明细")
@GetMapping("LsCustList") @GetMapping("LsCustList")
public TableDataPageInfo<GridCustCountLingshou> selectLsCustList(@RequestParam String regionCode, String custName, String custIdc,String dt) { public TableDataPageInfo<?> selectLsCustList(@RequestParam String regionCode, String custName, String custIdc, String dt) {
Page<Object> page = startPage(); Page<Object> page = startPage();
List<GridCustCountLingshou> list = service.selectLsCustList(regionCode, custName, custIdc,dt); if (isNewRetailTenant()) {
List<GridCustCountLingshouNew825> list = service.selectLsCustListNew825(regionCode, custName, custIdc, dt);
return getDataTable(list, page);
}
List<GridCustCountLingshou> list = service.selectLsCustList(regionCode, custName, custIdc, dt);
return getDataTable(list, page); return getDataTable(list, page);
} }
@Log(title = "查询公司客户明细") @Log(title = "查询公司客户明细")
@ApiOperation(value = "查询公司客户明细") @ApiOperation(value = "查询公司客户明细")
@GetMapping("GsCustList") @GetMapping("GsCustList")
public TableDataPageInfo<GridCustCountGongsi> selectGsCustList(@RequestParam String regionCode, String custName, String socialCreditCode,String dt) { public TableDataPageInfo<GridCustCountGongsi> selectGsCustList(@RequestParam String regionCode, String custName, String socialCreditCode, String dt) {
Page<Object> page = startPage(); Page<Object> page = startPage();
List<GridCustCountGongsi> list = service.selectGsCustList(regionCode, custName, socialCreditCode,dt); List<GridCustCountGongsi> list = service.selectGsCustList(regionCode, custName, socialCreditCode, dt);
return getDataTable(list, page); return getDataTable(list, page);
} }
@Log(title = "导出零售客户明细") @Log(title = "导出零售客户明细")
@ApiOperation(value = "导出零售客户明细", produces = "application/octet-stream") @ApiOperation(value = "导出零售客户明细", produces = "application/octet-stream")
@GetMapping("exportLsCust") @GetMapping("exportLsCust")
public void exportLsCust(HttpServletResponse response, @RequestParam String regionCode, String custName, String custIdc,String dt) { public void exportLsCust(HttpServletResponse response, @RequestParam String regionCode, String custName, String custIdc, String dt) {
try { try {
if (isNewRetailTenant()) {
ExcelUtil<GridCustCountLingshouNew825> util = new ExcelUtil<>(GridCustCountLingshouNew825.class);
util.exportExcel(response, service.selectLsCustListNew825(regionCode, custName, custIdc, dt), "网格客户明细");
return;
}
ExcelUtil<GridCustCountLingshou> util = new ExcelUtil<>(GridCustCountLingshou.class); ExcelUtil<GridCustCountLingshou> util = new ExcelUtil<>(GridCustCountLingshou.class);
util.exportExcel(response, service.selectLsCustList(regionCode, custName, custIdc,dt), "网格客户明细"); util.exportExcel(response, service.selectLsCustList(regionCode, custName, custIdc, dt), "网格客户明细");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Log(title = "导出公司客户明细") @Log(title = "导出公司客户明细")
@ApiOperation(value = "导出公司客户明细", produces = "application/octet-stream") @ApiOperation(value = "导出公司客户明细", produces = "application/octet-stream")
@GetMapping("exportGsCust") @GetMapping("exportGsCust")
public void exportGsCust(HttpServletResponse response, @RequestParam String regionCode, String custName, String socialCreditCode,String dt) { public void exportGsCust(HttpServletResponse response, @RequestParam String regionCode, String custName, String socialCreditCode, String dt) {
try { try {
ExcelUtil<GridCustCountGongsi> util = new ExcelUtil<>(GridCustCountGongsi.class); ExcelUtil<GridCustCountGongsi> util = new ExcelUtil<>(GridCustCountGongsi.class);
util.exportExcel(response, service.selectGsCustList(regionCode, custName, socialCreditCode,dt), "网格客户明细"); util.exportExcel(response, service.selectGsCustList(regionCode, custName, socialCreditCode, dt), "网格客户明细");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private boolean isNewRetailTenant() {
return SecurityUtils.getDeptId().toString().startsWith("825");
}
} }

View File

@@ -12,6 +12,7 @@ import com.ruoyi.ibs.grid.domain.dto.GridCustListDTO;
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO; import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
import com.ruoyi.ibs.grid.domain.entity.RegionGrid; import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO; import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
import com.ruoyi.ibs.grid.mapper.RegionGridMapper; import com.ruoyi.ibs.grid.mapper.RegionGridMapper;
import com.ruoyi.ibs.grid.service.RegionGridListService; import com.ruoyi.ibs.grid.service.RegionGridListService;
@@ -108,6 +109,14 @@ public class RegionGridListController extends BaseController {
return AjaxResult.success("开始更新行社所有网格"); return AjaxResult.success("开始更新行社所有网格");
} }
/**
* 查询地理网格列表(专用于客群创建)
*/
@ApiOperation("查询地理网格列表(专用于客群创建)")
@Log(title = "地理网格-查询地理网格列表(客群创建用)")
@GetMapping("/groupList")
public AjaxResult getRegionGridListForGroup(RegionGridListDTO dto) {
return AjaxResult.success(regionGridListService.getRegionGridListForGroup(dto));
}
} }

View File

@@ -0,0 +1,115 @@
package com.ruoyi.ibs.grid.domain.entity;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
/**
* 网格汇总统计_零售825专用对象 grid_cmpm_count_lingshou_new_825
*/
@Data
public class GridCmpmCountLingshouNew825 implements Serializable {
private static final long serialVersionUID = 1L;
@Excel(name = "统计日期")
private String dt;
@Excel(name = "一级网格名称")
private String gridName;
@Excel(name = "二级网格名称")
private String gridName2;
@Excel(name = "县/区")
private String county;
@Excel(name = "镇/街道")
private String town;
@Excel(name = "村/社区")
private String village;
@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 Integer hxsdNum;
private String regionCode;
private String opsDept;
}

View File

@@ -0,0 +1,77 @@
package com.ruoyi.ibs.grid.domain.entity;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
/**
* 客户明细统计_零售825专用对象 grid_cust_count_lingshou_new_825
*/
@Data
public class GridCustCountLingshouNew825 implements Serializable {
private static final long serialVersionUID = 1L;
@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;
@Excel(name = "是否核心收单")
private String isHxsd;
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;
}

View File

@@ -0,0 +1,28 @@
package com.ruoyi.ibs.grid.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 地理网格选择VO - 专用于客群创建时的网格选择
* 只包含必要字段,提升查询效率
*
* @author ruoyi
*/
@Data
public class RegionGridGroupVO implements Serializable {
/**
* 网格主键
*/
@ApiModelProperty(value = "网格主键")
private Long gridId;
/**
* 网格名称
*/
@ApiModelProperty(value = "网格名称")
private String gridName;
}

View File

@@ -2,8 +2,10 @@ package com.ruoyi.ibs.grid.mapper;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshouNew825;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshouNew825;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.HashMap; import java.util.HashMap;
@@ -12,10 +14,12 @@ import java.util.List;
@Mapper @Mapper
public interface GridCountMapper { public interface GridCountMapper {
List<GridCmpmCountLingshou> selectLsCountList(HashMap<String, Object> paramMap); List<GridCmpmCountLingshou> selectLsCountList(HashMap<String, Object> paramMap);
List<GridCmpmCountLingshouNew825> selectLsCountListNew825(HashMap<String, Object> paramMap);
List<GridCmpmCountGongsi> selectGsCountList(HashMap<String, Object> paramMap); List<GridCmpmCountGongsi> selectGsCountList(HashMap<String, Object> paramMap);
List<GridCustCountLingshou> selectLsCustList(HashMap<String, Object> paramMap); List<GridCustCountLingshou> selectLsCustList(HashMap<String, Object> paramMap);
List<GridCustCountLingshouNew825> selectLsCustListNew825(HashMap<String, Object> paramMap);
List<GridCustCountGongsi> selectGsCustList(HashMap<String, Object> paramMap); List<GridCustCountGongsi> selectGsCustList(HashMap<String, Object> paramMap);
} }

View File

@@ -40,6 +40,13 @@ public interface RegionCustUserMapper extends BaseMapper<RegionCustUser> {
Long countCustInSecGrid(@Param("gridId") Long gridId, @Param("custType") String custType); Long countCustInSecGrid(@Param("gridId") Long gridId, @Param("custType") String custType);
/**
* 根据网格ID列表查询所有客户用于异步导入客群直接根据gridIds查询无需区分等级
* @param gridIds 网格ID列表
* @param headId 总行机构号前三位(用于拼接动态表名)
*/
List<RegionCustUser> selectAllCustByGridIds(@Param("gridIds") List<Long> gridIds, @Param("headId") String headId);

View File

@@ -5,6 +5,7 @@ import com.ruoyi.ibs.grid.domain.dto.GridCustListDTO;
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO; import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
import com.ruoyi.ibs.grid.domain.entity.RegionGrid; import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo; import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -69,4 +70,10 @@ public interface RegionGridMapper extends BaseMapper<RegionGrid> {
List<Long> getSecGridIdByTopGridId(@Param("gridId") Long gridId); List<Long> getSecGridIdByTopGridId(@Param("gridId") Long gridId);
/**
* 查询地理网格列表(简化版)- 专用于客群创建
* 只返回gridId和gridName避免N+1查询问题
*/
List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO);
} }

View File

@@ -2,18 +2,22 @@ package com.ruoyi.ibs.grid.service;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshouNew825;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshouNew825;
import java.util.List; import java.util.List;
public interface GridCountService { public interface GridCountService {
List<GridCmpmCountLingshou> selectLsCountList(String town,String village,String dt); List<GridCmpmCountLingshou> selectLsCountList(String town,String village,String dt);
List<GridCmpmCountLingshouNew825> selectLsCountListNew825(String town, String village, String dt);
List<GridCmpmCountGongsi> selectGsCountList(String town, String village,String dt); List<GridCmpmCountGongsi> selectGsCountList(String town, String village,String dt);
List<GridCustCountLingshou> selectLsCustList(String regionCode, String custName, String custIdc,String dt); List<GridCustCountLingshou> selectLsCustList(String regionCode, String custName, String custIdc,String dt);
List<GridCustCountLingshouNew825> selectLsCustListNew825(String regionCode, String custName, String custIdc, String dt);
List<GridCustCountGongsi> selectGsCustList(String regionCode, String custName, String socialCreditCode,String dt); List<GridCustCountGongsi> selectGsCustList(String regionCode, String custName, String socialCreditCode,String dt);
} }

View File

@@ -8,6 +8,7 @@ import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
import com.ruoyi.ibs.grid.domain.entity.RegionGrid; import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO; import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo; import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
@@ -36,11 +37,20 @@ public interface RegionGridListService {
List<RegionGridListVO> getSecGridListByManager(RegionGridListDTO regionGridListDTO); List<RegionGridListVO> getSecGridListByManager(RegionGridListDTO regionGridListDTO);
/** /**
* 查询网格内所有客户(不限制客户类型,用于导入客群) * 根据网格ID列表批量查询所有客户用于导入客群,优化版
* @param regionGrid 地理网格对象 * @param gridIds 网格ID列表
* @return 网格内所有客户列表 * @param headId 总行机构号前三位(用于拼接动态表名)
* @return 客户列表(去重)
*/ */
List<RegionCustUser> selectAllCustFromGrid(RegionGrid regionGrid); List<RegionCustUser> selectAllCustByGridIds(List<Long> gridIds, String headId);
/**
* 查询地理网格列表(简化版)- 专用于客群创建
* 只返回gridId和gridName避免N+1查询问题
* @param regionGridListDTO 查询条件
* @return 网格列表仅包含ID和名称
*/
List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO);
} }

View File

@@ -3,8 +3,10 @@ package com.ruoyi.ibs.grid.service.impl;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshouNew825;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi; import com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou; import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshou;
import com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshouNew825;
import com.ruoyi.ibs.grid.mapper.GridCountMapper; import com.ruoyi.ibs.grid.mapper.GridCountMapper;
import com.ruoyi.ibs.grid.service.GridCountService; import com.ruoyi.ibs.grid.service.GridCountService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -21,6 +23,15 @@ public class GridCountServiceimpl implements GridCountService {
@Override @Override
public List<GridCmpmCountLingshou> selectLsCountList(String town,String village,String dt) { public List<GridCmpmCountLingshou> selectLsCountList(String town,String village,String dt) {
return mapper.selectLsCountList(buildLsCountParams(town, village, dt));
}
@Override
public List<GridCmpmCountLingshouNew825> selectLsCountListNew825(String town, String village, String dt) {
return mapper.selectLsCountListNew825(buildLsCountParams(town, village, dt));
}
private HashMap<String, Object> buildLsCountParams(String town, String village, String dt) {
HashMap<String, Object> paramMap = new HashMap<>(); HashMap<String, Object> paramMap = new HashMap<>();
if(SecurityUtils.userRole().equals("branch")){ if(SecurityUtils.userRole().equals("branch")){
paramMap.put("isBranch",true); paramMap.put("isBranch",true);
@@ -36,7 +47,7 @@ public class GridCountServiceimpl implements GridCountService {
paramMap.put("town",town); paramMap.put("town",town);
paramMap.put("village",village); paramMap.put("village",village);
paramMap.put("dt",dt); paramMap.put("dt",dt);
return mapper.selectLsCountList(paramMap); return paramMap;
} }
@Override @Override
@@ -65,6 +76,15 @@ public class GridCountServiceimpl implements GridCountService {
@Override @Override
public List<GridCustCountLingshou> selectLsCustList(String regionCode, String custName, String custIdc,String dt) { public List<GridCustCountLingshou> selectLsCustList(String regionCode, String custName, String custIdc,String dt) {
return mapper.selectLsCustList(buildLsCustParams(regionCode, custName, custIdc, dt));
}
@Override
public List<GridCustCountLingshouNew825> selectLsCustListNew825(String regionCode, String custName, String custIdc, String dt) {
return mapper.selectLsCustListNew825(buildLsCustParams(regionCode, custName, custIdc, dt));
}
private HashMap<String, Object> buildLsCustParams(String regionCode, String custName, String custIdc, String dt) {
HashMap<String, Object> paramMap = new HashMap<>(); HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("deptId",SecurityUtils.getDeptId()); paramMap.put("deptId",SecurityUtils.getDeptId());
paramMap.put("userName",SecurityUtils.getUsername()); paramMap.put("userName",SecurityUtils.getUsername());
@@ -72,7 +92,7 @@ public class GridCountServiceimpl implements GridCountService {
paramMap.put("custName",custName); paramMap.put("custName",custName);
paramMap.put("custIdc",custIdc); paramMap.put("custIdc",custIdc);
paramMap.put("dt",dt); paramMap.put("dt",dt);
return mapper.selectLsCustList(paramMap); return paramMap;
} }
@Override @Override

View File

@@ -12,6 +12,7 @@ import com.ruoyi.ibs.grid.domain.dto.UnbindDTO;
import com.ruoyi.ibs.grid.domain.entity.*; import com.ruoyi.ibs.grid.domain.entity.*;
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO; import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO; import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo; import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
import com.ruoyi.ibs.grid.mapper.*; import com.ruoyi.ibs.grid.mapper.*;
@@ -432,23 +433,33 @@ public class RegionGridListServiceImpl implements RegionGridListService {
} }
/** /**
* 查询网格内所有客户(不限制客户类型,用于导入客群) * 根据网格ID列表批量查询所有客户用于导入客群,优化版
* @param regionGrid 地理网格对象 * @param gridIds 网格ID列表
* @return 网格内所有客户列表 * @return 客户列表(去重)
*/ */
@Override @Override
public List<RegionCustUser> selectAllCustFromGrid(RegionGrid regionGrid) { public List<RegionCustUser> selectAllCustByGridIds(List<Long> gridIds, String headId) {
LambdaQueryWrapper<RegionCustUser> queryWrapper = new LambdaQueryWrapper<>(); return regionCustUserMapper.selectAllCustByGridIds(gridIds, headId);
// 根据网格等级判断使用一级还是二级网格ID查询 }
if (regionGrid.getGridLevel().equals("1")) {
queryWrapper.eq(RegionCustUser::getTopGridId, regionGrid.getGridId()); /**
} else if (regionGrid.getGridLevel().equals("2")) { * 查询地理网格列表(简化版)- 专用于客群创建
queryWrapper.eq(RegionCustUser::getSecGridId, regionGrid.getGridId()); * 只返回gridId和gridName避免N+1查询问题
} else { * @param regionGridListDTO 查询条件
throw new ServiceException("无效的网格等级: " + regionGrid.getGridLevel()); * @return 网格列表仅包含ID和名称
*/
@Override
public List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO) {
// 设置当前用户部门ID用于权限过滤
regionGridListDTO.setDeptId(SecurityUtils.getDeptId());
// 如果是总行用户,设置归属部室
if (SecurityUtils.isHead()) {
regionGridListDTO.setOpsDept(SecurityUtils.getOpsDept());
} }
// 不限制客户类型,查询所有类型的客户
return regionCustUserMapper.selectList(queryWrapper); // 直接查询一次SQL完成无N+1问题
return regionGridMapper.getRegionGridListForGroup(regionGridListDTO);
} }
} }

View File

@@ -312,6 +312,13 @@ public class SysCampaignController extends BaseController {
.stream().collect(HashMap::new, (m, v) -> m.put(v.getUuid(),v.getModelName()), HashMap::putAll))); .stream().collect(HashMap::new, (m, v) -> m.put(v.getUuid(),v.getModelName()), HashMap::putAll)));
} }
@PostMapping("/updateVisitInfoFeedback")
@ApiOperation("更新PAD走访反馈")
@Log(title ="pad走访记录-更新走访反馈", businessType = BusinessType.UPDATE)
public AjaxResult updateVisitInfoFeedback(@RequestBody VisitInfoFeedbackUpdateDTO updateDTO) {
return toAjax(sysCampaignService.updateVisitInfoFeedback(updateDTO));
}
@PostMapping("/delete") @PostMapping("/delete")
@ApiOperation("根据campaignId删除任务") @ApiOperation("根据campaignId删除任务")
@Log(title = "走访-根据campaignId删除任务") @Log(title = "走访-根据campaignId删除任务")

View File

@@ -41,6 +41,14 @@ public class CustInfoBusinessVo {
@ApiModelProperty(value = "手动打标") @ApiModelProperty(value = "手动打标")
private List<TreeNode> tagManual; private List<TreeNode> tagManual;
/** 九维画像分数 */
@ApiModelProperty(value = "九维画像分数")
private Ent9vPortraitOrc ent9vPortrait;
/** 九维画像详细信息 */
@ApiModelProperty(value = "九维画像详细信息")
private NineVFinalInfoOrc nineVFinalInfo;
public List<TreeNode> getTagManual() { public List<TreeNode> getTagManual() {
return tagManual; return tagManual;
} }
@@ -187,4 +195,20 @@ public class CustInfoBusinessVo {
public void setTabEnmuVos(List<TreeNode> tabEnmuVos) { public void setTabEnmuVos(List<TreeNode> tabEnmuVos) {
this.tabEnmuVos = tabEnmuVos; this.tabEnmuVos = tabEnmuVos;
} }
public Ent9vPortraitOrc getEnt9vPortrait() {
return ent9vPortrait;
}
public void setEnt9vPortrait(Ent9vPortraitOrc ent9vPortrait) {
this.ent9vPortrait = ent9vPortrait;
}
public NineVFinalInfoOrc getNineVFinalInfo() {
return nineVFinalInfo;
}
public void setNineVFinalInfo(NineVFinalInfoOrc nineVFinalInfo) {
this.nineVFinalInfo = nineVFinalInfo;
}
} }

View File

@@ -0,0 +1,80 @@
package com.ruoyi.ibs.list.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 企业九维画像分数对象 ent_9v_portrait_orc_825
*
* @author ruoyi
* @date 2026-03-18
*/
@Data
@TableName("ent_9v_portrait_orc_825")
public class Ent9vPortraitOrc implements Serializable {
private static final long serialVersionUID = 1L;
/** 统一社会信用代码 */
private String uniscid;
/** 客户内码 */
@TableField("cst_id")
private String cstId;
/** 机构号 */
@TableField("org_no")
private String orgNo;
/** score_1合规经营 */
@TableField("score_1")
private BigDecimal score1;
/** score_2风险准入 */
@TableField("score_2")
private BigDecimal score2;
/** score_3高管信用评价 */
@TableField("score_3")
private String score3;
/** score_4股东信用评价 */
@TableField("score_4")
private BigDecimal score4;
/** score_5社会贡献度 */
@TableField("score_5")
private BigDecimal score5;
/** score_6稳定经营 */
@TableField("score_6")
private BigDecimal score6;
/** score_7经营能力 */
@TableField("score_7")
private BigDecimal score7;
/** score_8偿债能力 */
@TableField("score_8")
private BigDecimal score8;
/** score_9潜在代偿资源 */
@TableField("score_9")
private BigDecimal score9;
/** 九维总分 */
@TableField("score_all")
private BigDecimal scoreAll;
/** 九维总分排名 */
@TableField("score_all_rank")
private BigDecimal scoreAllRank;
/** 会计日期 */
@TableField("dat_dt")
private String datDt;
}

View File

@@ -0,0 +1,315 @@
package com.ruoyi.ibs.list.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* 企业九维画像详细信息对象 9v_final_info_orc_825
*
* @author ruoyi
* @date 2026-03-18
*/
@Data
@TableName("9v_final_info_orc_825")
public class NineVFinalInfoOrc implements Serializable {
private static final long serialVersionUID = 1L;
/** 统一社会信用代码 */
private String uniscid;
/** 机构号 */
@TableField("org_no")
private String orgNo;
/** 一、企业合规经营模块 */
/** 是否存在经营异常名录信息 */
@TableField("score_1_1")
private String score11;
/** 是否存在严重违法失信企业名单信息 */
@TableField("score_1_2")
private String score12;
/** 企业环境行为信仰登记是否为"E"或D */
@TableField("score_1_3")
private String score13;
/** 是否存在税务重大税收违法黑名单信息 */
@TableField("score_1_4")
private String score14;
/** 是否存在拖欠工资黑名单 */
@TableField("score_1_5")
private String score15;
/** 是否存在工商吊销企业信息 */
@TableField("score_1_6")
private String score16;
/** 二、企业风险准入模块 */
/** 是否存在注销企业信息 */
@TableField("score_2_1")
private String score21;
/** 是否存在执行案件信息 */
@TableField("score_2_2")
private String score22;
/** 是否存在查封信息 */
@TableField("score_2_3")
private String score23;
/** 是否存在单位未履行生效裁判信息 */
@TableField("score_2_4")
private String score24;
/** 是否存在企业破产清算信息 */
@TableField("score_2_5")
private String score25;
/** 是否失信被执行人 */
@TableField("score_2_6")
private String score26;
/** 是否为诉讼案件被告 */
@TableField("score_2_7")
private String score27;
/** 三、高管信用评价模块 */
/** 是否存在查封信息(score_3) */
@TableField("score_3_1")
private String score31;
/** 是否存在执行案件信息(score_3) */
@TableField("score_3_2")
private String score32;
/** 是否存在个人未履行生效裁判信息 */
@TableField("score_3_3")
private String score33;
/** 是否存在拖欠工资黑名单(score_3) */
@TableField("score_3_4")
private String score34;
/** 是否失信被执行人(score_3) */
@TableField("score_3_5")
private String score35;
/** 是否存在刑事案件被告人生效判决信息 */
@TableField("score_3_6")
private String score36;
/** 是否为诉讼案件被告(score_3) */
@TableField("score_3_7")
private String score37;
/** 四、股东信用评价模块 */
/** 是否存在查封信息(score_4) */
@TableField("score_4_1")
private String score41;
/** 是否存在执行案件信息(score_4) */
@TableField("score_4_2")
private String score42;
/** 是否存在个人未履行生效裁判信息(score_4) */
@TableField("score_4_3")
private String score43;
/** 是否存在拖欠工资黑名单(score_4) */
@TableField("score_4_4")
private String score44;
/** 是否失信被执行人(score_4) */
@TableField("score_4_5")
private String score45;
/** 是否存在刑事案件被告人生效判决信息(score_4) */
@TableField("score_4_6")
private String score46;
/** 是否为诉讼案件被告(score_4) */
@TableField("score_4_7")
private String score47;
/** 是否存在企业未履行生效裁判信息 */
@TableField("score_4_8")
private String score48;
/** 五、企业社会贡献度模块 */
/** 前12个月纳税金额 */
@TableField("score_5_1")
private String score51;
/** 纳税等级 */
@TableField("score_5_2")
private String score52;
/** 缴纳社保人数 */
@TableField("score_5_3")
private String score53;
/** 公积金缴纳人数 */
@TableField("score_5_4")
private String score54;
/** 是否为出口退税生产清单企业 */
@TableField("score_5_5")
private String score55;
/** 六、企业稳定经营模块 */
/** 市场主体经营年限 */
@TableField("score_6_1")
private String score61;
/** 股东(或发起人)或投资人信息认缴出资人数 */
@TableField("score_6_2")
private String score62;
/** 最大股东持股占比 */
@TableField("score_6_3")
private String score63;
/** 近三年法定代表人变更次数 */
@TableField("score_6_4")
private String score64;
/** 近三年股东变更次数 */
@TableField("score_6_5")
private String score65;
/** 近三年经营范围变更次数 */
@TableField("score_6_6")
private String score66;
/** 近三年经营地址变更次数 */
@TableField("score_6_7")
private String score67;
/** 近三年缴税年数 */
@TableField("score_6_8")
private String score68;
/** 法人户籍 */
@TableField("score_6_9")
private String score69;
/** 法人婚姻状况 */
@TableField("score_6_10")
private String score610;
/** 七、企业经营能力模块 */
/** 上年增值税金额 */
@TableField("score_7_1")
private String score71;
/** 今年增值税同比变动 */
@TableField("score_7_2")
private String score72;
/** 上年企业所得税金额 */
@TableField("score_7_3")
private String score73;
/** 今年所得税同比变动 */
@TableField("score_7_4")
private String score74;
/** 缴纳社保人数同比变动 */
@TableField("score_7_5")
private String score75;
/** 公积金缴纳人数同比变动 */
@TableField("score_7_6")
private String score76;
/** 当年纳税金额同比变动 */
@TableField("score_7_7")
private String score77;
/** 上年出口退税金额 */
@TableField("score_7_8")
private String score78;
/** 八、企业偿债能力模块 */
/** 房产套数 */
@TableField("score_8_1")
private String score81;
/** 房产面积 */
@TableField("score_8_2")
private String score82;
/** 未抵押房产套数 */
@TableField("score_8_3")
private String score83;
/** 未抵押房产面积 */
@TableField("score_8_4")
private String score84;
/** 已抵押房产被担保债权数额 */
@TableField("score_8_5")
private String score85;
/** 企业车产金额 */
@TableField("score_8_6")
private String score86;
/** 九、潜在代偿资源模块 */
/** 法人及股东房产面积合计 */
@TableField("score_9_1")
private String score91;
/** 法人及股东房产套数合计 */
@TableField("score_9_2")
private String score92;
/** 法人及股东未抵押房产套数合计 */
@TableField("score_9_3")
private String score93;
/** 法人及股东未抵押房产面积 */
@TableField("score_9_4")
private String score94;
/** 法人及股东已抵押房产被担保债权数额合计 */
@TableField("score_9_5")
private String score95;
/** 法人代表车产金额 */
@TableField("score_9_6")
private String score96;
/** 十、企业环境信用模块 */
/** 生态信用扣分 */
@TableField("score_b015")
private String scoreB015;
/** 列入环境失信黑名单时间 */
@TableField("score_b016_time")
private String scoreB016Time;
/** 列入环境失信黑名单原因 */
@TableField("score_b016_reason")
private String scoreB016Reason;
/** 环境行为信用评价等级 */
@TableField("score_a020")
private String scoreA020;
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.ibs.list.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class VisitFeedbackItemDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "反馈类型")
private String feedbackType;
@ApiModelProperty(value = "反馈产品列表")
private List<String> products;
}

View File

@@ -117,4 +117,40 @@ public class VisitInfo {
@ApiModelProperty(value = "走访备注") @ApiModelProperty(value = "走访备注")
private String remark; private String remark;
@ApiModelProperty(value = "互动地址")
private String interAddr;
@ApiModelProperty(value = "协同员工名称")
private String colStafName;
@ApiModelProperty(value = "后续备注")
private String laterNote;
@ApiModelProperty(value = "意向产品值")
private String intentionProductValue;
@ApiModelProperty(value = "反馈状态")
private String feedbackStatus;
@ApiModelProperty(value = "走访来源")
private String sourceOfInterview;
@ApiModelProperty(value = "文件名")
private String filename;
@ApiModelProperty(value = "外呼状态")
private String outCallStatus;
@ApiModelProperty(value = "外呼意向")
private String outCallIntention;
@ApiModelProperty(value = "来源")
private String source;
@ApiModelProperty(value = "分析值")
private String analysisValue;
@ApiModelProperty(value = "设备")
private String facility;
} }

View File

@@ -13,6 +13,10 @@ public class VisitInfoDTO {
@ApiModelProperty(value = "柜员名称") @ApiModelProperty(value = "柜员名称")
private String visName; private String visName;
/** 柜员号 */
@ApiModelProperty(value = "柜员号")
private String visId;
/** 走访时间 */ /** 走访时间 */
@ApiModelProperty(value = "走访时间") @ApiModelProperty(value = "走访时间")
private String visTime; private String visTime;
@@ -29,6 +33,10 @@ public class VisitInfoDTO {
@ApiModelProperty(value = "客户类型 0个人1商户2企业") @ApiModelProperty(value = "客户类型 0个人1商户2企业")
private String custType; private String custType;
/** 活动ID */
@ApiModelProperty(value = "活动ID")
private String campaignId;
/** 异常走访标签(筛选是否异常) */ /** 异常走访标签(筛选是否异常) */
@ApiModelProperty(value = "异常走访标签 0正常 1走访频率异常 2走访持续时长异常 3签退时间异常") @ApiModelProperty(value = "异常走访标签 0正常 1走访频率异常 2走访持续时长异常 3签退时间异常")
private String abnormalVisitTag; private String abnormalVisitTag;

View File

@@ -0,0 +1,34 @@
package com.ruoyi.ibs.list.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class VisitInfoFeedbackUpdateDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "走访记录ID")
private Long id;
@ApiModelProperty(value = "走访渠道")
private String source;
@ApiModelProperty(value = "走访备注")
private String remark;
@ApiModelProperty(value = "客户意愿结构化数据")
private List<VisitFeedbackItemDTO> feedbackItems;
@ApiModelProperty(value = "客户意愿拼接值")
private String intentionProductValue;
private String userName;
private String userRole;
private String deptId;
}

View File

@@ -128,4 +128,40 @@ public class VisitInfoVO {
@ApiModelProperty(value = "走访结果") @ApiModelProperty(value = "走访结果")
private String interRes; private String interRes;
@ApiModelProperty(value = "实地拜访地址")
private String interAddr;
@ApiModelProperty(value = "协同走访客户经理")
private String colStafName;
@ApiModelProperty(value = "事后备注")
private String laterNote;
@ApiModelProperty(value = "走访反馈")
private String intentionProductValue;
@ApiModelProperty(value = "反馈状态")
private String feedbackStatus;
@ApiModelProperty(value = "走访来源")
private String sourceOfInterview;
@ApiModelProperty(value = "批量导入文件名")
private String filename;
@ApiModelProperty(value = "外呼状态")
private String outCallStatus;
@ApiModelProperty(value = "客户意愿")
private String outCallIntention;
@ApiModelProperty(value = "走访来源1nullpad 2企业微信 3pc")
private String source;
@ApiModelProperty(value = "nlp模型提取")
private String analysisValue;
@ApiModelProperty(value = "预授信额度")
private String facility;
} }

View File

@@ -0,0 +1,13 @@
package com.ruoyi.ibs.list.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ibs.list.domain.Ent9vPortraitOrc;
/**
* 企业九维画像分数Mapper接口
*
* @author ruoyi
* @date 2026-03-18
*/
public interface Ent9vPortraitOrcMapper extends BaseMapper<Ent9vPortraitOrc> {
}

View File

@@ -0,0 +1,13 @@
package com.ruoyi.ibs.list.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ibs.list.domain.NineVFinalInfoOrc;
/**
* 企业九维画像详细信息Mapper接口
*
* @author ruoyi
* @date 2026-03-18
*/
public interface NineVFinalInfoOrcMapper extends BaseMapper<NineVFinalInfoOrc> {
}

View File

@@ -156,6 +156,10 @@ public interface SysCampaignMapper extends BaseMapper<SysCampaign> {
List<VisitInfoVO> selectVisitInfoList(VisitInfoDTO visitInfoDTO); List<VisitInfoVO> selectVisitInfoList(VisitInfoDTO visitInfoDTO);
List<VisitInfoVO> selectVisitInfoList875(VisitInfoDTO visitInfoDTO);
int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO);
@Update("UPDATE sys_campaign SET del_flag = '2' where campaign_id = #{campaignId}") @Update("UPDATE sys_campaign SET del_flag = '2' where campaign_id = #{campaignId}")
int deleteSysCampaignByCampaignId(@Param("campaignId") String campaignId); int deleteSysCampaignByCampaignId(@Param("campaignId") String campaignId);

View File

@@ -115,5 +115,7 @@ public interface ISysCampaignService {
public List<VisitInfoVO> selectVisitInfoVoList(VisitInfoDTO visitInfoDTO); public List<VisitInfoVO> selectVisitInfoVoList(VisitInfoDTO visitInfoDTO);
int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO);
public int deleteSysCampaign(String campaignId); public int deleteSysCampaign(String campaignId);
} }

View File

@@ -24,7 +24,9 @@ import com.ruoyi.ibs.grid.util.CustExcelUtil;
import com.ruoyi.ibs.list.domain.*; import com.ruoyi.ibs.list.domain.*;
import com.ruoyi.ibs.list.mapper.CorporateShareholderMapper; import com.ruoyi.ibs.list.mapper.CorporateShareholderMapper;
import com.ruoyi.ibs.list.mapper.CustInfoRetailMapper; import com.ruoyi.ibs.list.mapper.CustInfoRetailMapper;
import com.ruoyi.ibs.list.mapper.Ent9vPortraitOrcMapper;
import com.ruoyi.ibs.list.mapper.FamilyMembersMapper; import com.ruoyi.ibs.list.mapper.FamilyMembersMapper;
import com.ruoyi.ibs.list.mapper.NineVFinalInfoOrcMapper;
import com.ruoyi.ibs.list.mapper.SignedProductsMapper; import com.ruoyi.ibs.list.mapper.SignedProductsMapper;
import com.ruoyi.ibs.list.service.ICustInfoBusinessService; import com.ruoyi.ibs.list.service.ICustInfoBusinessService;
import com.ruoyi.system.mapper.SysIndustryMapper; import com.ruoyi.system.mapper.SysIndustryMapper;
@@ -75,6 +77,12 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
@Autowired @Autowired
private FamilyMembersMapper familyMembersMapper; private FamilyMembersMapper familyMembersMapper;
@Autowired
private Ent9vPortraitOrcMapper ent9vPortraitOrcMapper;
@Autowired
private NineVFinalInfoOrcMapper nineVFinalInfoOrcMapper;
@Autowired @Autowired
private CustMapMapper custMapMapper; private CustMapMapper custMapMapper;
@@ -138,6 +146,17 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
//查询手动标签列表 //查询手动标签列表
List<CustManualTagVO> tagCreateVos = familyMembersMapper.selectManualTag(params); List<CustManualTagVO> tagCreateVos = familyMembersMapper.selectManualTag(params);
custInfoBusinessVo.setTagManual(TreeNode.convertToTreeByParentId(tagCreateVos)); custInfoBusinessVo.setTagManual(TreeNode.convertToTreeByParentId(tagCreateVos));
// 仅当登录机构为825时查询九维画像数据
if ("825".equals(getHeadId())) {
LambdaQueryWrapper<Ent9vPortraitOrc> portraitWrapper = new LambdaQueryWrapper<>();
portraitWrapper.eq(Ent9vPortraitOrc::getUniscid, custInfoBusiness.getSocialCreditCode());
custInfoBusinessVo.setEnt9vPortrait(ent9vPortraitOrcMapper.selectOne(portraitWrapper));
LambdaQueryWrapper<NineVFinalInfoOrc> finalInfoWrapper = new LambdaQueryWrapper<>();
finalInfoWrapper.eq(NineVFinalInfoOrc::getUniscid, custInfoBusiness.getSocialCreditCode());
custInfoBusinessVo.setNineVFinalInfo(nineVFinalInfoOrcMapper.selectOne(finalInfoWrapper));
}
} }
return custInfoBusinessVo; return custInfoBusinessVo;
} }

View File

@@ -2175,9 +2175,31 @@ public class SysCampaignServiceImpl implements ISysCampaignService
visitInfoDTO.setUserName(SecurityUtils.getUsername()); visitInfoDTO.setUserName(SecurityUtils.getUsername());
visitInfoDTO.setUserRole(SecurityUtils.userRole()); visitInfoDTO.setUserRole(SecurityUtils.userRole());
visitInfoDTO.setDeptId(String.valueOf(SecurityUtils.getDeptId())); visitInfoDTO.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
if ("875".equals(SecurityUtils.getHeadId())) {
return sysCampaignMapper.selectVisitInfoList875(visitInfoDTO);
}
return sysCampaignMapper.selectVisitInfoList(visitInfoDTO); return sysCampaignMapper.selectVisitInfoList(visitInfoDTO);
} }
@Override
public int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO) {
if (updateDTO == null || updateDTO.getId() == null) {
throw new ServiceException("走访记录ID不能为空");
}
String deptId = String.valueOf(SecurityUtils.getDeptId());
if (!deptId.startsWith("875")) {
throw new ServiceException("当前机构无权维护PAD走访反馈");
}
updateDTO.setUserName(SecurityUtils.getUsername());
updateDTO.setUserRole(SecurityUtils.userRole());
updateDTO.setDeptId(deptId);
updateDTO.setSource(StringUtils.trimToNull(updateDTO.getSource()));
updateDTO.setRemark(StringUtils.trimToNull(updateDTO.getRemark()));
updateDTO.setIntentionProductValue(buildIntentionProductValue(updateDTO.getFeedbackItems()));
int rows = sysCampaignMapper.updateVisitInfoFeedback(updateDTO);
return rows;
}
@Override @Override
public int deleteSysCampaign(String campaignId) { public int deleteSysCampaign(String campaignId) {
SysCampaign sysCampaign = sysCampaignMapper.selectSysCampaignByCampaignId(campaignId); SysCampaign sysCampaign = sysCampaignMapper.selectSysCampaignByCampaignId(campaignId);
@@ -2186,4 +2208,28 @@ public class SysCampaignServiceImpl implements ISysCampaignService
} }
return sysCampaignMapper.deleteSysCampaignByCampaignId(campaignId); return sysCampaignMapper.deleteSysCampaignByCampaignId(campaignId);
} }
private String buildIntentionProductValue(List<VisitFeedbackItemDTO> feedbackItems) {
if (feedbackItems == null || feedbackItems.isEmpty()) {
return null;
}
String joinedValue = feedbackItems.stream()
.filter(Objects::nonNull)
.map(item -> {
String feedbackType = StringUtils.trimToNull(item.getFeedbackType());
List<String> products = Optional.ofNullable(item.getProducts())
.orElse(Collections.emptyList())
.stream()
.map(StringUtils::trimToNull)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (feedbackType == null || products.isEmpty()) {
return null;
}
return feedbackType + ":" + String.join(",", products);
})
.filter(Objects::nonNull)
.collect(Collectors.joining(";"));
return StringUtils.trimToNull(joinedValue);
}
} }

View File

@@ -1,12 +1,15 @@
package com.ruoyi.ibs.task.controller; package com.ruoyi.ibs.task.controller;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.page.TableDataPageInfo; import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.ibs.task.domain.dto.AlterForwardRequest;
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO; import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
import com.ruoyi.ibs.task.domain.entity.Alter; import com.ruoyi.ibs.task.domain.entity.Alter;
import com.ruoyi.ibs.task.domain.entity.AlterConfig; import com.ruoyi.ibs.task.domain.entity.AlterConfig;
@@ -95,12 +98,37 @@ public class WorkRecordController extends BaseController {
@GetMapping("/alter/allList") @GetMapping("/alter/allList")
@Log(title = "工作台-查询预警信息") @Log(title = "工作台-查询预警信息")
@ApiOperation("查询所有预警信息") @ApiOperation("查询所有预警信息")
public TableDataPageInfo<AlterVO> allAlterList(String status, String alterType) { public TableDataPageInfo<AlterVO> allAlterList(String status, String alterType, String forwardFlag) {
Page<Object> page = startPage(); Page<Object> page = startPage();
List<AlterVO> list = workRecordService.getAllAlterList(status, alterType); List<AlterVO> list = workRecordService.getAllAlterList(status, alterType, forwardFlag);
return getDataTable(list, page); return getDataTable(list, page);
} }
@GetMapping("/alter/forward/meta")
@Log(title = "工作台-查询预警转发元数据")
@ApiOperation("查询预警转发元数据")
public AjaxResult getAlterForwardMeta(@RequestParam String ids) {
List<Long> idList = Arrays.stream(ids.split(","))
.filter(org.apache.commons.lang3.StringUtils::isNotBlank)
.map(Long::valueOf)
.collect(Collectors.toList());
return AjaxResult.success(workRecordService.getAlterForwardMeta(idList));
}
@PostMapping("/alter/forward")
@Log(title = "工作台-转发预警信息")
@ApiOperation("批量转发预警信息")
public AjaxResult forwardAlter(@Validated @RequestBody AlterForwardRequest request) {
AlterForwardResultVO result = workRecordService.forwardAlter(request);
if (result.getSuccessCount() > 0 && result.getFailCount() > 0) {
return AjaxResult.success("成功转发" + result.getSuccessCount() + "条,失败" + result.getFailCount() + "", result);
}
if (result.getSuccessCount() > 0) {
return AjaxResult.success("成功转发" + result.getSuccessCount() + "", result);
}
return AjaxResult.error("未能成功转发,失败" + result.getFailCount() + "");
}
@GetMapping("/alter/count") @GetMapping("/alter/count")
@Log(title = "工作台-查询预警推送次数") @Log(title = "工作台-查询预警推送次数")
@ApiOperation("查询预警推送次数") @ApiOperation("查询预警推送次数")
@@ -131,4 +159,15 @@ public class WorkRecordController extends BaseController {
return toAjax(workRecordService.updateReadTime(id)); return toAjax(workRecordService.updateReadTime(id));
} }
/**
* 查询所有预警类型
*/
@GetMapping("/alter/types")
@Log(title = "工作台-查询预警类型")
@ApiOperation("查询所有预警类型")
public AjaxResult getAlterTypes() {
List<String> types = workRecordService.getAlterTypes();
return AjaxResult.success(types);
}
} }

View File

@@ -0,0 +1,23 @@
package com.ruoyi.ibs.task.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
public class AlterForwardRequest {
@NotEmpty(message = "请选择需要转发的预警")
private List<Long> ids;
@NotBlank(message = "请选择转发目标类型")
private String targetType;
private Long deptId;
private String userName;
private Boolean useHistory;
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.ibs.task.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ForwardTarget {
private String targetType;
private Long deptId;
private String userName;
private String nickName;
private String targetName;
}

View File

@@ -0,0 +1,53 @@
package com.ruoyi.ibs.task.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("alter_assign_history")
public class AlterAssignHistory implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("预警类型")
private String alterType;
@ApiModelProperty("客户号")
private String custId;
@ApiModelProperty("客户姓名")
private String custName;
@ApiModelProperty("所属支行/当前操作机构ID")
private Long deptId;
@ApiModelProperty("最近一次转发目标类型dept/user")
private String lastAssignTargetType;
@ApiModelProperty("最近一次转发目标部门ID")
private Long lastAssignTargetDeptId;
@ApiModelProperty("最近一次转发目标用户账号")
private String lastAssignTargetUser;
@ApiModelProperty("最近一次转发目标名称")
private String lastAssignTargetName;
@ApiModelProperty("最近一次转发操作人账号")
private String lastAssignByUser;
@ApiModelProperty("最近一次转发操作人角色")
private String lastAssignByRole;
@ApiModelProperty("更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}

View File

@@ -94,4 +94,8 @@ public class WorkRecord implements Serializable {
@ApiModelProperty(value = "是否逾期", notes = "") @ApiModelProperty(value = "是否逾期", notes = "")
private Integer isOverdue; private Integer isOverdue;
/** 当前承接部门ID仅转发到网点时使用 */
@ApiModelProperty(value = "当前承接部门ID", notes = "")
private Long deptId;
} }

View File

@@ -0,0 +1,14 @@
package com.ruoyi.ibs.task.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class AlterAssignTargetVO {
@ApiModelProperty("目标编码")
private String targetCode;
@ApiModelProperty("目标名称")
private String targetName;
}

View File

@@ -0,0 +1,32 @@
package com.ruoyi.ibs.task.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class AlterForwardMetaVO {
@ApiModelProperty("可选网点列表")
private List<AlterAssignTargetVO> deptOptions = new ArrayList<>();
@ApiModelProperty("可选客户经理列表")
private List<AlterAssignTargetVO> userOptions = new ArrayList<>();
@ApiModelProperty("历史推荐目标类型dept/user")
private String recommendedTargetType;
@ApiModelProperty("历史推荐目标编码")
private String recommendedTargetCode;
@ApiModelProperty("历史推荐目标名称")
private String recommendedTargetName;
@ApiModelProperty("是否仅允许按历史推荐转发")
private Boolean historyOnly = false;
@ApiModelProperty("是否存在历史推荐")
private Boolean hasHistoryRecommend = false;
}

View File

@@ -0,0 +1,14 @@
package com.ruoyi.ibs.task.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class AlterForwardResultVO {
@ApiModelProperty("成功数量")
private int successCount;
@ApiModelProperty("失败数量")
private int failCount;
}

View File

@@ -47,6 +47,14 @@ public class AlterVO {
@ApiModelProperty(value = "客户内码", notes = "") @ApiModelProperty(value = "客户内码", notes = "")
private String custIsn; private String custIsn;
/** 当前承接部门ID */
@ApiModelProperty(value = "当前承接部门ID", notes = "")
private Long deptId;
/** 当前承接网点名称 */
@ApiModelProperty(value = "当前承接网点名称", notes = "")
private String deptName;
/** 状态 */ /** 状态 */
@ApiModelProperty(value = "状态", notes = "") @ApiModelProperty(value = "状态", notes = "")
private String status; private String status;
@@ -62,4 +70,12 @@ public class AlterVO {
/** 是否需要反馈 */ /** 是否需要反馈 */
@ApiModelProperty(value = "是否需要反馈", notes = "") @ApiModelProperty(value = "是否需要反馈", notes = "")
private String isFeedback; private String isFeedback;
/** 转发标识1需要转发0无需转发 */
@ApiModelProperty(value = "转发标识", notes = "")
private String forwardFlag;
/** 是否可转发 */
@ApiModelProperty(value = "是否可转发", notes = "")
private Boolean canForward;
} }

View File

@@ -0,0 +1,9 @@
package com.ruoyi.ibs.task.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ibs.task.domain.entity.AlterAssignHistory;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AlterAssignHistoryMapper extends BaseMapper<AlterAssignHistory> {
}

View File

@@ -6,9 +6,9 @@ import java.util.Date;
import java.util.List; import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ibs.task.domain.entity.WorkRecord;
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO; import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
import com.ruoyi.ibs.task.domain.entity.AlterConfig; import com.ruoyi.ibs.task.domain.entity.AlterConfig;
import com.ruoyi.ibs.task.domain.entity.WorkRecord;
import com.ruoyi.ibs.task.domain.vo.*; import com.ruoyi.ibs.task.domain.vo.*;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -86,10 +86,26 @@ public interface WorkRecordMapper extends BaseMapper<WorkRecord> {
* @return 预警信息 * @return 预警信息
*/ */
List<AlterVO> selectAllAlterList(@Param("deptId") String deptId, @Param("role") String role, List<AlterVO> selectAllAlterList(@Param("deptId") String deptId, @Param("role") String role,
@Param("status") String status, @Param("alterType") String alterType, @Param("username") String username); @Param("status") String status, @Param("alterType") String alterType,
@Param("username") String username, @Param("forwardFlag") String forwardFlag);
AlterCountVO selectAlterCount(@Param("reportTime") Date reportTime, @Param("role") String role, @Param("deptId") String deptId); AlterCountVO selectAlterCount(@Param("reportTime") Date reportTime, @Param("role") String role, @Param("deptId") String deptId);
List<AlterAssignTargetVO> selectForwardDeptTargets(@Param("branchDeptId") Long branchDeptId);
List<AlterAssignTargetVO> selectForwardUserTargets(@Param("deptId") Long deptId, @Param("role") String role);
int updateAlterForwardToDept(@Param("id") Long id, @Param("sourceUserName") String sourceUserName,
@Param("targetDeptId") Long targetDeptId, @Param("updateBy") String updateBy);
int updateAlterForwardToUserByBranch(@Param("id") Long id, @Param("sourceUserName") String sourceUserName,
@Param("targetUserName") String targetUserName, @Param("targetNickName") String targetNickName,
@Param("updateBy") String updateBy);
int updateAlterForwardToUserByOutlet(@Param("id") Long id, @Param("sourceDeptId") Long sourceDeptId,
@Param("targetUserName") String targetUserName, @Param("targetNickName") String targetNickName,
@Param("updateBy") String updateBy);
/** /**
* 设置工作清单为过期 * 设置工作清单为过期
* *
@@ -112,4 +128,12 @@ public interface WorkRecordMapper extends BaseMapper<WorkRecord> {
List<AlterConfigVO> selectAlterConfigList(String alterType); List<AlterConfigVO> selectAlterConfigList(String alterType);
int updateAlterConfig(AlterConfig alterConfig); int updateAlterConfig(AlterConfig alterConfig);
/**
* 查询所有预警类型
*
* @param headId 总部ID部门ID前三位
* @return 预警类型列表
*/
List<String> selectAlterTypes(@Param("headId") String headId);
} }

View File

@@ -5,6 +5,7 @@ import java.util.List;
import com.ruoyi.common.core.page.TableDataPageInfo; import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO; import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
import com.ruoyi.ibs.task.domain.dto.AlterForwardRequest;
import com.ruoyi.ibs.task.domain.entity.AlterConfig; import com.ruoyi.ibs.task.domain.entity.AlterConfig;
import com.ruoyi.ibs.task.domain.entity.WorkRecord; import com.ruoyi.ibs.task.domain.entity.WorkRecord;
import com.ruoyi.ibs.task.domain.vo.*; import com.ruoyi.ibs.task.domain.vo.*;
@@ -61,7 +62,23 @@ public interface WorkRecordService{
* @param alterType * @param alterType
* @return * @return
*/ */
List<AlterVO> getAllAlterList(String status, String alterType); List<AlterVO> getAllAlterList(String status, String alterType, String forwardFlag);
/**
* 获取预警转发元数据
*
* @param id 预警ID
* @return 转发元数据
*/
AlterForwardMetaVO getAlterForwardMeta(List<Long> ids);
/**
* 批量转发预警
*
* @param request 转发请求
* @return 结果
*/
AlterForwardResultVO forwardAlter(AlterForwardRequest request);
/** /**
* 定时任务生成工作清单 * 定时任务生成工作清单
* *
@@ -85,4 +102,11 @@ public interface WorkRecordService{
int updateReadTime(Long id); int updateReadTime(Long id);
AlterCountVO getAlterCount(Date reportTime); AlterCountVO getAlterCount(Date reportTime);
/**
* 查询所有预警类型
*
* @return 预警类型列表
*/
List<String> getAlterTypes();
} }

View File

@@ -11,19 +11,29 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.ruoyi.common.core.page.TableDataPageInfo; import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.ibs.task.domain.dto.AlterForwardRequest;
import com.ruoyi.ibs.task.domain.dto.ForwardTarget;
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO; import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
import com.ruoyi.ibs.task.domain.entity.AlterAssignHistory;
import com.ruoyi.ibs.task.domain.entity.AlterConfig; import com.ruoyi.ibs.task.domain.entity.AlterConfig;
import com.ruoyi.ibs.task.domain.entity.WorkRecord; import com.ruoyi.ibs.task.domain.entity.WorkRecord;
import com.ruoyi.ibs.task.domain.vo.*; import com.ruoyi.ibs.task.domain.vo.*;
import com.ruoyi.ibs.task.mapper.AlterAssignHistoryMapper;
import com.ruoyi.ibs.task.mapper.WorkRecordMapper; import com.ruoyi.ibs.task.mapper.WorkRecordMapper;
import com.ruoyi.ibs.task.service.WorkRecordService; import com.ruoyi.ibs.task.service.WorkRecordService;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.system.domain.SysPost; import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.ISysDeptService; import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysPostService; import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -39,15 +49,35 @@ import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class WorkRecordServiceImpl implements WorkRecordService { public class WorkRecordServiceImpl implements WorkRecordService {
private final static String alterTypesRedisKey = "work:record:alter:types";
private static final String ROLE_MANAGER = "manager";
private static final String ROLE_BRANCH = "branch";
private static final String ROLE_OUTLET = "outlet";
private static final String TARGET_TYPE_DEPT = "dept";
private static final String TARGET_TYPE_USER = "user";
private static final Set<String> FORWARD_ALTER_TYPES = new HashSet<>(Arrays.asList(
"个人资产客户定期存款",
"个人资产客户封闭式理财"
));
@Autowired @Autowired
private WorkRecordMapper workRecordMapper; private WorkRecordMapper workRecordMapper;
@Autowired
private AlterAssignHistoryMapper alterAssignHistoryMapper;
@Autowired @Autowired
private ISysPostService sysPostService; private ISysPostService sysPostService;
@Autowired @Autowired
private ISysDeptService sysDeptService; private ISysDeptService sysDeptService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private RedisCache redisCache;
/** /**
* 查询我的工作清单 * 查询我的工作清单
* *
@@ -137,7 +167,9 @@ public class WorkRecordServiceImpl implements WorkRecordService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public List<AlterVO> getAlterList(String status, String alterType) { public List<AlterVO> getAlterList(String status, String alterType) {
String username = SecurityUtils.getUsername(); String username = SecurityUtils.getUsername();
return workRecordMapper.selectAlterList(username, status, alterType); List<AlterVO> alterVOS = workRecordMapper.selectAlterList(username, status, alterType);
fillAlterExtFields(alterVOS);
return alterVOS;
} }
/** /**
@@ -150,27 +182,98 @@ public class WorkRecordServiceImpl implements WorkRecordService {
* @return 预警信息 * @return 预警信息
*/ */
@Override @Override
public List<AlterVO> getAllAlterList(String status, String alterType) { public List<AlterVO> getAllAlterList(String status, String alterType, String forwardFlag) {
List<AlterVO> alterVOS = workRecordMapper.selectAllAlterList(String.valueOf(SecurityUtils.getDeptId()), SecurityUtils.userRole(), status, alterType, SecurityUtils.getUsername()); List<AlterVO> alterVOS = workRecordMapper.selectAllAlterList(
// 将 userName 和 nickName 以 "userName-nickName" 的方式拼接后设置到 custInfo 字段 String.valueOf(SecurityUtils.getDeptId()),
if (alterVOS != null && !alterVOS.isEmpty()) { SecurityUtils.userRole(),
alterVOS.forEach(alterVO -> { status,
String userName = alterVO.getUserName(); alterType,
String nickName = alterVO.getNickName(); SecurityUtils.getUsername(),
if (userName != null && nickName != null) { forwardFlag
alterVO.setUserInfo(nickName + "-" + userName); );
} else if (userName != null) { fillAlterExtFields(alterVOS);
alterVO.setUserInfo(userName);
} else if (nickName != null) {
alterVO.setUserInfo(nickName);
} else {
alterVO.setUserInfo("");
}
});
}
return alterVOS; return alterVOS;
} }
/**
* 查询转发弹窗所需元数据。
* 单选时返回手动选项并附带历史推荐;多选时根据是否存在历史记录决定走手动分配还是历史推荐模式。
*/
@Override
public AlterForwardMetaVO getAlterForwardMeta(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
throw new ServiceException("请选择需要转发的预警");
}
AlterForwardMetaVO metaVO = new AlterForwardMetaVO();
String role = requireForwardRole();
Long currentDeptId = SecurityUtils.getDeptId();
String currentUser = SecurityUtils.getUsername();
List<WorkRecord> workRecords = workRecordMapper.selectBatchIds(ids);
if (workRecords == null || workRecords.size() != ids.size()) {
throw new ServiceException("存在预警记录已不存在,请刷新后重试");
}
workRecords.forEach(item -> validateRecordBeforeForward(item, role, currentUser, currentDeptId));
if (ids.size() > 1) {
boolean hasAnyHistory = workRecords.stream()
.map(item -> getHistoryRecommend(item.getCustId(), currentDeptId, role))
.anyMatch(Objects::nonNull);
metaVO.setHasHistoryRecommend(hasAnyHistory);
metaVO.setHistoryOnly(hasAnyHistory);
if (!hasAnyHistory) {
fillManualOptions(metaVO, role, currentDeptId);
}
return metaVO;
}
WorkRecord workRecord = workRecords.get(0);
fillManualOptions(metaVO, role, currentDeptId);
fillHistoryRecommend(metaVO, workRecord.getCustId(), currentDeptId, role);
return metaVO;
}
/**
* 执行预警转发。
* 支持手动分配和按历史推荐分配两种模式,并分别统计成功和失败数量。
*/
@Override
@Transactional(rollbackFor = Exception.class)
public AlterForwardResultVO forwardAlter(AlterForwardRequest request) {
String role = requireForwardRole();
String currentUser = SecurityUtils.getUsername();
Long currentDeptId = SecurityUtils.getDeptId();
AlterForwardResultVO result = new AlterForwardResultVO();
boolean useHistory = Boolean.TRUE.equals(request.getUseHistory());
ForwardTarget manualTarget = useHistory ? null : resolveManualTarget(request, role, currentDeptId);
List<WorkRecord> workRecords = workRecordMapper.selectBatchIds(request.getIds());
if (workRecords == null || workRecords.size() != request.getIds().size()) {
throw new ServiceException("存在预警记录已不存在,请刷新后重试");
}
for (WorkRecord workRecord : workRecords) {
validateRecordBeforeForward(workRecord, role, currentUser, currentDeptId);
AlterAssignHistory history = useHistory ? getHistoryRecommend(workRecord.getCustId(), currentDeptId, role) : null;
ForwardTarget target = useHistory ? resolveHistoryTarget(history, role, currentDeptId) : manualTarget;
if (target == null) {
result.setFailCount(result.getFailCount() + 1);
continue;
}
int row = doForward(workRecord.getId(), currentUser, currentDeptId, role, target);
if (row <= 0) {
result.setFailCount(result.getFailCount() + 1);
continue;
}
result.setSuccessCount(result.getSuccessCount() + row);
if (useHistory) {
if (history != null) {
saveAssignHistoryByHistory(workRecord, history, role, currentDeptId);
}
} else {
saveAssignHistory(workRecord, manualTarget, role, currentDeptId);
}
}
return result;
}
/** /**
* 定时任务生成工作清单 * 定时任务生成工作清单
* *
@@ -253,6 +356,19 @@ public class WorkRecordServiceImpl implements WorkRecordService {
return alterCountVO; return alterCountVO;
} }
@Override
public List<String> getAlterTypes() {
String headId = SecurityUtils.getHeadId();
String cacheKey = alterTypesRedisKey + ":" + headId;
List<String> cachedTypes = redisCache.getCacheObject(cacheKey);
if (cachedTypes != null) {
return cachedTypes;
}
List<String> types = workRecordMapper.selectAlterTypes(headId);
redisCache.setCacheObjectToEndDay(cacheKey, types);
return types;
}
/** /**
* 获取当前登录用户权限范围内所有post_ids * 获取当前登录用户权限范围内所有post_ids
* @return * @return
@@ -332,4 +448,306 @@ public class WorkRecordServiceImpl implements WorkRecordService {
return java.sql.Timestamp.valueOf(endTime); return java.sql.Timestamp.valueOf(endTime);
} }
private void fillAlterExtFields(List<AlterVO> alterVOS) {
if (alterVOS == null || alterVOS.isEmpty()) {
return;
}
String role = SecurityUtils.userRole();
Long currentDeptId = SecurityUtils.getDeptId();
String currentUser = SecurityUtils.getUsername();
alterVOS.forEach(alterVO -> {
alterVO.setForwardFlag(parseForwardFlag(alterVO.getAlterDetail()));
alterVO.setAlterDetail(stripForwardPrefix(alterVO.getAlterDetail()));
alterVO.setUserInfo(buildUserInfo(alterVO.getUserName(), alterVO.getNickName()));
alterVO.setCanForward(canForward(alterVO, role, currentDeptId, currentUser));
});
}
private String buildUserInfo(String userName, String nickName) {
if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(nickName)) {
return nickName + "-" + userName;
}
if (StringUtils.isNotEmpty(userName)) {
return userName;
}
if (StringUtils.isNotEmpty(nickName)) {
return nickName;
}
return "";
}
private String parseForwardFlag(String alterDetail) {
if (StringUtils.isEmpty(alterDetail)) {
return "";
}
if (alterDetail.startsWith("[FORWARD=1]")) {
return "1";
}
if (alterDetail.startsWith("[FORWARD=0]")) {
return "0";
}
return "";
}
private String stripForwardPrefix(String alterDetail) {
if (StringUtils.isEmpty(alterDetail)) {
return alterDetail;
}
if (alterDetail.startsWith("[FORWARD=1] ")) {
return alterDetail.substring(12);
}
if (alterDetail.startsWith("[FORWARD=0] ")) {
return alterDetail.substring(12);
}
if (alterDetail.startsWith("[FORWARD=1]") || alterDetail.startsWith("[FORWARD=0]")) {
return alterDetail.substring(11);
}
return alterDetail;
}
private Boolean canForward(AlterVO alterVO, String role, Long currentDeptId, String currentUser) {
if (!FORWARD_ALTER_TYPES.contains(alterVO.getAlterType())) {
return false;
}
if (!"1".equals(alterVO.getForwardFlag())) {
return false;
}
if ("2".equals(alterVO.getStatus())) {
return false;
}
if (ROLE_BRANCH.equals(role)) {
return StringUtils.equals(currentUser, alterVO.getUserName()) && alterVO.getDeptId() == null;
}
if (ROLE_OUTLET.equals(role)) {
return alterVO.getDeptId() != null && alterVO.getDeptId().equals(currentDeptId) && StringUtils.isEmpty(alterVO.getUserName());
}
return false;
}
/**
* 校验当前登录角色是否具备预警转发能力,并返回角色标识。
*/
private String requireForwardRole() {
String role = SecurityUtils.userRole();
if (!ROLE_BRANCH.equals(role) && !ROLE_OUTLET.equals(role)) {
throw new ServiceException("当前角色不支持转发预警");
}
return role;
}
/**
* 校验预警记录当前是否仍处于登录人可转发范围内,防止页面停留期间数据状态已变化。
*/
private void validateRecordBeforeForward(WorkRecord workRecord, String role, String currentUser, Long currentDeptId) {
if (workRecord == null || workRecord.getId() == null) {
throw new ServiceException("预警记录不存在");
}
if (!Integer.valueOf(1).equals(workRecord.getIsAlter())) {
throw new ServiceException("当前记录不是预警信息");
}
if (!FORWARD_ALTER_TYPES.contains(workRecord.getAlterType())) {
throw new ServiceException("当前预警类型不支持转发");
}
if (!"1".equals(parseForwardFlag(workRecord.getAlterDetail()))) {
throw new ServiceException("当前预警已有管户,无需转发");
}
if ("2".equals(workRecord.getStatus())) {
throw new ServiceException("当前预警已完成,不能转发");
}
if (ROLE_BRANCH.equals(role)) {
if (!StringUtils.equals(currentUser, workRecord.getUserName()) || workRecord.getDeptId() != null) {
throw new ServiceException("当前预警已不在您的待转发范围内");
}
return;
}
if (ROLE_OUTLET.equals(role)) {
if (workRecord.getDeptId() == null || !workRecord.getDeptId().equals(currentDeptId) || StringUtils.isNotEmpty(workRecord.getUserName())) {
throw new ServiceException("当前预警已不在本网点待转发范围内");
}
return;
}
throw new ServiceException("当前角色不支持转发预警");
}
/**
* 解析手动转发目标,并顺带完成目标权限范围校验。
*/
private ForwardTarget resolveManualTarget(AlterForwardRequest request, String role, Long currentDeptId) {
if (TARGET_TYPE_DEPT.equals(request.getTargetType())) {
if (request.getDeptId() == null) {
throw new ServiceException("请选择转发网点");
}
if (!ROLE_BRANCH.equals(role)) {
throw new ServiceException("网点管理员只能转发给客户经理");
}
String deptName = getAllowedDeptName(request.getDeptId(), currentDeptId);
return new ForwardTarget(TARGET_TYPE_DEPT, request.getDeptId(), null, null, deptName);
}
if (!TARGET_TYPE_USER.equals(request.getTargetType()) || StringUtils.isEmpty(request.getUserName())) {
throw new ServiceException("请选择转发客户经理");
}
SysUser user = getAllowedTargetUser(request.getUserName(), role, currentDeptId);
String targetName = StringUtils.isNotEmpty(user.getNickName()) ? user.getNickName() : user.getUserName();
return new ForwardTarget(TARGET_TYPE_USER, null, user.getUserName(), user.getNickName(), targetName);
}
/**
* 将历史分配记录转换成可执行的转发目标;若历史目标已失效,则返回空。
*/
private ForwardTarget resolveHistoryTarget(AlterAssignHistory history, String role, Long currentDeptId) {
if (history == null) {
return null;
}
if (TARGET_TYPE_DEPT.equals(history.getLastAssignTargetType())) {
if (history.getLastAssignTargetDeptId() == null) {
return null;
}
String deptName = getAllowedDeptName(history.getLastAssignTargetDeptId(), currentDeptId);
return new ForwardTarget(TARGET_TYPE_DEPT, history.getLastAssignTargetDeptId(), null, null, deptName);
}
if (!TARGET_TYPE_USER.equals(history.getLastAssignTargetType()) || StringUtils.isEmpty(history.getLastAssignTargetUser())) {
return null;
}
try {
SysUser user = getAllowedTargetUser(history.getLastAssignTargetUser(), role, currentDeptId);
String targetName = StringUtils.isNotEmpty(user.getNickName()) ? user.getNickName() : user.getUserName();
return new ForwardTarget(TARGET_TYPE_USER, null, user.getUserName(), user.getNickName(), targetName);
} catch (ServiceException ex) {
return null;
}
}
/**
* 按目标类型执行真正的转发落库更新。
*/
private int doForward(Long workRecordId, String currentUser, Long currentDeptId, String role, ForwardTarget target) {
if (TARGET_TYPE_DEPT.equals(target.getTargetType())) {
return workRecordMapper.updateAlterForwardToDept(workRecordId, currentUser, target.getDeptId(), currentUser);
}
if (ROLE_BRANCH.equals(role)) {
return workRecordMapper.updateAlterForwardToUserByBranch(
workRecordId, currentUser, target.getUserName(), target.getNickName(), currentUser
);
}
return workRecordMapper.updateAlterForwardToUserByOutlet(
workRecordId, currentDeptId, target.getUserName(), target.getNickName(), currentUser
);
}
/**
* 校验目标网点是否在当前支行管理员的可转发范围内,并返回网点名称。
*/
private String getAllowedDeptName(Long targetDeptId, Long currentDeptId) {
List<AlterAssignTargetVO> deptOptions = workRecordMapper.selectForwardDeptTargets(currentDeptId);
return deptOptions.stream()
.filter(item -> String.valueOf(targetDeptId).equals(item.getTargetCode()))
.map(AlterAssignTargetVO::getTargetName)
.findFirst()
.orElseThrow(() -> new ServiceException("所选网点不在当前支行可转发范围内"));
}
/**
* 校验目标客户经理是否在当前管理员可转发范围内,并返回用户信息。
*/
private SysUser getAllowedTargetUser(String targetUserName, String role, Long currentDeptId) {
List<AlterAssignTargetVO> userOptions = workRecordMapper.selectForwardUserTargets(currentDeptId, ROLE_MANAGER);
boolean matched = userOptions.stream().anyMatch(item -> StringUtils.equals(targetUserName, item.getTargetCode()));
if (!matched) {
if (ROLE_BRANCH.equals(role)) {
throw new ServiceException("所选客户经理不在当前支行可转发范围内");
}
throw new ServiceException("所选客户经理不在当前网点可转发范围内");
}
SysUser user = sysUserService.selectUserByUserName(targetUserName);
if (user == null || StringUtils.isEmpty(user.getUserName())) {
throw new ServiceException("转发目标客户经理不存在");
}
return user;
}
/**
* 填充手动分配弹窗可选项;支行管理员可选网点和客户经理,网点管理员仅可选客户经理。
*/
private void fillManualOptions(AlterForwardMetaVO metaVO, String role, Long currentDeptId) {
if (ROLE_BRANCH.equals(role)) {
metaVO.setDeptOptions(workRecordMapper.selectForwardDeptTargets(currentDeptId));
metaVO.setUserOptions(workRecordMapper.selectForwardUserTargets(currentDeptId, ROLE_MANAGER));
return;
}
if (ROLE_OUTLET.equals(role)) {
metaVO.setUserOptions(workRecordMapper.selectForwardUserTargets(currentDeptId, ROLE_MANAGER));
return;
}
throw new ServiceException("当前角色不支持转发预警");
}
/**
* 按客户、当前部门和当前角色查询历史推荐,并回填到前端元数据中。
*/
private void fillHistoryRecommend(AlterForwardMetaVO metaVO, String custId, Long deptId, String role) {
AlterAssignHistory history = getHistoryRecommend(custId, deptId, role);
if (history == null) {
return;
}
metaVO.setHasHistoryRecommend(true);
metaVO.setRecommendedTargetType(history.getLastAssignTargetType());
if (TARGET_TYPE_DEPT.equals(history.getLastAssignTargetType())) {
metaVO.setRecommendedTargetCode(history.getLastAssignTargetDeptId() == null ? "" : String.valueOf(history.getLastAssignTargetDeptId()));
} else {
metaVO.setRecommendedTargetCode(history.getLastAssignTargetUser());
}
metaVO.setRecommendedTargetName(history.getLastAssignTargetName());
}
/**
* 保存本次手动分配结果;同一客户在同部门、同角色下只维护一条最新历史。
*/
private void saveAssignHistory(WorkRecord workRecord, ForwardTarget target, String role, Long currentDeptId) {
AlterAssignHistory history = getHistoryRecommend(workRecord.getCustId(), currentDeptId, role);
if (history == null) {
history = new AlterAssignHistory();
history.setCustId(workRecord.getCustId());
}
history.setAlterType(workRecord.getAlterType());
history.setCustName(workRecord.getCustName());
history.setDeptId(currentDeptId);
history.setLastAssignTargetType(target.getTargetType());
history.setLastAssignTargetDeptId(target.getDeptId());
history.setLastAssignTargetUser(target.getUserName());
history.setLastAssignTargetName(target.getTargetName());
history.setLastAssignByUser(SecurityUtils.getUsername());
history.setLastAssignByRole(role);
history.setUpdateTime(new Date());
if (history.getId() == null) {
alterAssignHistoryMapper.insert(history);
} else {
alterAssignHistoryMapper.updateById(history);
}
}
/**
* 按历史推荐成功转发后,刷新该客户最新一次分配历史的更新时间和目标信息。
*/
private void saveAssignHistoryByHistory(WorkRecord workRecord, AlterAssignHistory sourceHistory, String role, Long currentDeptId) {
ForwardTarget target = new ForwardTarget(
sourceHistory.getLastAssignTargetType(),
sourceHistory.getLastAssignTargetDeptId(),
sourceHistory.getLastAssignTargetUser(),
null,
sourceHistory.getLastAssignTargetName()
);
saveAssignHistory(workRecord, target, role, currentDeptId);
}
/**
* 获取当前角色在当前部门下,对指定客户的最近一次分配历史。
*/
private AlterAssignHistory getHistoryRecommend(String custId, Long deptId, String role) {
return alterAssignHistoryMapper.selectOne(new LambdaQueryWrapper<AlterAssignHistory>()
.eq(AlterAssignHistory::getCustId, custId)
.eq(AlterAssignHistory::getDeptId, deptId)
.eq(AlterAssignHistory::getLastAssignByRole, role)
.last("limit 1"));
}
} }

View File

@@ -857,6 +857,28 @@
select id,cust_name from cust_info_business where social_credit_code = #{socialCreditCode} select id,cust_name from cust_info_business where social_credit_code = #{socialCreditCode}
</select> </select>
<select id="selectExistingSocialCreditCodes" resultType="java.lang.String">
select social_credit_code
from cust_info_business_${deptCode}
where social_credit_code in
<foreach item="socialCreditCode" collection="socialCreditCodes" open="(" separator="," close=")">
#{socialCreditCode}
</foreach>
</select>
<update id="batchUpdateCustLevelBySocialCreditCode">
update cust_info_business_${deptCode}
set cust_level = case social_credit_code
<foreach item="item" collection="list">
when #{item.socialCreditCode} then #{item.custLevel}
</foreach>
end
where social_credit_code in
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item.socialCreditCode}
</foreach>
</update>
<insert id="insertCustomersToBusinessByScCode" parameterType="java.util.List"> <insert id="insertCustomersToBusinessByScCode" parameterType="java.util.List">
INSERT INTO cust_info_business INSERT INTO cust_info_business
(cust_id,cust_name, social_credit_code, update_by, update_time, cust_phone, industry, asset, credit) (cust_id,cust_name, social_credit_code, update_by, update_time, cust_phone, industry, asset, credit)
@@ -866,4 +888,4 @@
</foreach> </foreach>
</insert> </insert>
</mapper> </mapper>

View File

@@ -184,19 +184,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</insert> </insert>
<select id="selectAlterList" resultType="AlterVO"> <select id="selectAlterList" resultType="AlterVO">
select id, user_name, nick_name, alter_type, alter_detail, cust_id, cust_name, read_time, status, remark, is_feedback, cust_isn select wr.id, wr.user_name, wr.nick_name, wr.alter_type, wr.alter_detail, wr.cust_id, wr.cust_name,
from work_record 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
<where> <where>
is_alter = 1 wr.is_alter = 1
and user_name= #{username} and wr.user_name= #{username}
<if test="status != null and status != ''"> <if test="status != null and status != ''">
and status = #{status} and wr.status = #{status}
</if> </if>
<if test="alterType != null and alterType != ''"> <if test="alterType != null and alterType != ''">
and alter_type LIKE CONCAT('%', #{alterType}, '%') and wr.alter_type = #{alterType}
</if> </if>
</where> </where>
order by create_time desc, status asc order by wr.create_time desc, wr.status asc
</select> </select>
<select id="selectAlterCount" resultType="AlterCountVO"> <select id="selectAlterCount" resultType="AlterCountVO">
@@ -209,45 +211,148 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from work_record wr from work_record wr
left join sys_user su on su.user_name = wr.user_name left join sys_user su on su.user_name = wr.user_name
left join sys_dept d on su.dept_id = d.dept_id left join sys_dept d on su.dept_id = d.dept_id
left join sys_dept wd on wr.dept_id = wd.dept_id
where wr.is_alter = 1 where wr.is_alter = 1
and wr.create_time &lt;= #{reportTime} and wr.create_time &lt;= #{reportTime}
and ( and (
<choose> <choose>
<when test="role != null and role == 'outlet'"> su.dept_id = #{deptId} </when> <when test="role != null and role == 'outlet'"> (su.dept_id = #{deptId} or wr.dept_id = #{deptId}) </when>
<when test="role != null and role == 'branch'"> (su.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) </when> <when test="role != null and role == 'branch'"> ((su.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) or (wr.dept_id = #{deptId} or find_in_set(#{deptId},wd.ancestors))) </when>
<when test="role != null and role == 'private'"> (left(su.dept_id,3) = left(#{deptId},3) and wr.cust_type in ('0', '1')) </when> <when test="role != null and role == 'private'"> ((left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) and wr.cust_type in ('0', '1')) </when>
<when test="role != null and role == 'public'"> (left(su.dept_id,3) = left(#{deptId},3) and wr.cust_type = '2') </when> <when test="role != null and role == 'public'"> ((left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) and wr.cust_type = '2') </when>
<when test="role != null and role == 'ops'"> left(su.dept_id,3) = left(#{deptId},3) </when> <when test="role != null and role == 'ops'"> (left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) </when>
<when test="role != null and role == 'head'"> left(su.dept_id,3) = left(#{deptId},3) </when> <when test="role != null and role == 'head'"> (left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) </when>
</choose>) </choose>)
</select> </select>
<select id="selectAllAlterList" resultType="AlterVO"> <select id="selectAllAlterList" resultType="AlterVO">
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.cust_type, wr.is_feedback, wr.cust_isn 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.cust_type, wr.is_feedback, wr.cust_isn, wr.dept_id, wd.dept_name
from work_record wr from work_record wr
left join sys_user su on su.user_name = wr.user_name left join sys_user su on su.user_name = wr.user_name
left join sys_dept d on su.dept_id = d.dept_id left join sys_dept d on su.dept_id = d.dept_id
left join sys_dept wd on wr.dept_id = wd.dept_id
where wr.is_alter = 1 where wr.is_alter = 1
<if test="status != null and status != ''"> <if test="status != null and status != ''">
and wr.status = #{status} and wr.status = #{status}
</if> </if>
<if test="alterType != null and alterType != ''"> <if test="alterType != null and alterType != ''">
and wr.alter_type LIKE CONCAT('%', #{alterType}, '%') and wr.alter_type = #{alterType}
</if>
<if test="forwardFlag != null and forwardFlag != ''">
<choose>
<when test='forwardFlag == "1"'>
and wr.alter_type in ('个人资产客户定期存款', '个人资产客户封闭式理财')
and wr.alter_detail like '[FORWARD=1]%'
</when>
<when test='forwardFlag == "0"'>
and (
(wr.alter_type in ('个人资产客户定期存款', '个人资产客户封闭式理财')
and wr.alter_detail like '[FORWARD=0]%')
or wr.alter_type not in ('个人资产客户定期存款', '个人资产客户封闭式理财')
)
</when>
</choose>
</if> </if>
<!-- "走访异常提醒"类型直接通过用户名匹配,其他类型按角色权限处理 --> <!-- "走访异常提醒"类型直接通过用户名匹配,其他类型按角色权限处理 -->
and ((wr.alter_type = '走访异常提醒' and wr.user_name = #{username}) or and ((wr.alter_type = '走访异常提醒' and wr.user_name = #{username}) or
(wr.alter_type != '走访异常提醒' and ( (wr.alter_type != '走访异常提醒' and (
<choose> <choose>
<when test="role != null and role == 'outlet'"> su.dept_id = #{deptId} </when> <when test="role != null and role == 'outlet'"> (su.dept_id = #{deptId} or wr.dept_id = #{deptId}) </when>
<when test="role != null and role == 'branch'"> (su.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) </when> <when test="role != null and role == 'branch'"> ((su.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) or (wr.dept_id = #{deptId} or find_in_set(#{deptId},wd.ancestors))) </when>
<when test="role != null and role == 'private'"> (left(su.dept_id,3) = left(#{deptId},3) and wr.cust_type in ('0', '1')) </when> <when test="role != null and role == 'private'"> ((left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) and wr.cust_type in ('0', '1')) </when>
<when test="role != null and role == 'public'"> (left(su.dept_id,3) = left(#{deptId},3) and wr.cust_type = '2') </when> <when test="role != null and role == 'public'"> ((left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) and wr.cust_type = '2') </when>
<when test="role != null and role == 'ops'"> left(su.dept_id,3) = left(#{deptId},3) </when> <when test="role != null and role == 'ops'"> (left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) </when>
<when test="role != null and role == 'head'"> left(su.dept_id,3) = left(#{deptId},3) </when> <when test="role != null and role == 'head'"> (left(su.dept_id,3) = left(#{deptId},3) or left(wr.dept_id,3) = left(#{deptId},3)) </when>
</choose> ))) </choose> )))
order by wr.create_time desc, wr.status asc, wr.user_name desc order by wr.create_time desc, wr.status asc, wr.user_name desc
</select> </select>
<select id="selectForwardDeptTargets" resultType="AlterAssignTargetVO">
select cast(sd.dept_id as char) as targetCode, sd.dept_name as targetName
from sys_dept sd
where sd.del_flag = '0'
and sd.status = '0'
and sd.dept_type = 'outlet'
and sd.parent_id = #{branchDeptId}
order by sd.order_num, sd.dept_id
</select>
<select id="selectForwardUserTargets" resultType="AlterAssignTargetVO">
select su.user_name as targetCode, concat(su.nick_name, '-', su.user_name) as targetName
from sys_user su
where su.del_flag = '0'
and su.status = '0'
and su.dept_id = #{deptId}
<choose>
<when test="role == 'manager'">
and su.role_id = 102
</when>
<when test="role == 'branch'">
and su.role_id = 101
</when>
<when test="role == 'outlet'">
and su.role_id = 116
</when>
<otherwise>
and 1 = 2
</otherwise>
</choose>
order by su.nick_name, su.user_name
</select>
<update id="updateAlterForwardToDept">
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>
<update id="updateAlterForwardToUserByBranch">
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>
<update id="updateAlterForwardToUserByOutlet">
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
</update>
<select id="selectAlterConfigList" resultType="AlterConfigVO"> <select id="selectAlterConfigList" resultType="AlterConfigVO">
select id, alter_type, prod_type, warn_role, warn_threshold, update_time, update_by, type select id, alter_type, prod_type, warn_role, warn_threshold, update_time, update_by, type
from alter_config from alter_config
@@ -268,4 +373,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{id} where id = #{id}
</update> </update>
</mapper> <select id="selectAlterTypes" resultType="string">
select distinct alter_type
from work_record
where is_alter = 1
and alter_type is not null
and alter_type != ''
and left(user_name, 3) = #{headId}
order by alter_type
</select>
</mapper>

View File

@@ -167,6 +167,36 @@
</where> </where>
</select> </select>
<select id="getCustManagerListByPage" resultType="DwbRetailCustLevelManagerDetailVO">
select id, outlet_id, outlet_name, branch_id, branch_name, cust_name, cust_idc, cust_isn, cust_age, cust_sex, cust_phone, cust_address, cust_aum_bal,
aum_bal_comp_lm, cust_aum_month_avg, cust_level, cust_level_comp_lm, manager_name, manager_id
from dwb_retail_cust_level_manager_detail_${headId}
<where>
<if test="dto.managerId != null and dto.managerId !='' ">and manager_id = #{dto.managerId}</if>
<if test="dto.outletId != null and dto.outletId !='' ">and outlet_id = #{dto.outletId}</if>
<if test="dto.branchId != null and dto.branchId !='' ">and branch_id = #{dto.branchId}</if>
<if test="dto.statusType != null and dto.statusType !=''">
<choose>
<when test="dto.statusType == 'current'">
and cust_level = #{dto.custLevel}
</when>
<when test="dto.statusType == 'last'">
and cust_level_lm = #{dto.custLevel}
</when>
<when test="dto.statusType == 'rise'">
and cust_level = #{dto.custLevel}
and cust_level_comp_lm like '上升%'
</when>
<when test="dto.statusType == 'fall'">
and cust_level_lm = #{dto.custLevel}
and cust_level_comp_lm like '下降%'
</when>
</choose>
</if>
</where>
limit #{offset}, #{pageSize}
</select>
<select id="getCustLevelCount" resultType="Integer"> <select id="getCustLevelCount" resultType="Integer">
select count(1) select count(1)
from dwb_retail_cust_level_manager_detail from dwb_retail_cust_level_manager_detail
@@ -505,4 +535,20 @@
where manager_id is not null where manager_id is not null
</select> </select>
</mapper> <!-- 全量查询绩效网格客户列表(用于客群管户关系匹配,不按客户经理过滤)-->
<select id="getAllCustomerListForImport" resultType="GridCmpmVO">
select cust_id, cust_type, user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name
from grid_cmpm_${gridType}_${headId}
</select>
<!-- 根据客户ID列表查询管户关系用于动态客群更新管户关系-->
<select id="getRelationshipsByCustList" resultType="GridCmpmVO">
select cust_id, cust_type, user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name
from grid_cmpm_${gridType}_${headId}
where
<foreach item="custInfo" collection="custInfoList" separator=" or " open="(" close=")">
(cust_id = #{custInfo.custId} and cust_type = #{custInfo.custType})
</foreach>
</select>
</mapper>

View File

@@ -55,8 +55,5 @@
<if test="custName != null and custName != ''">AND b.cust_name like concat('%', #{custName}, '%')</if> <if test="custName != null and custName != ''">AND b.cust_name like concat('%', #{custName}, '%')</if>
<if test="custType != null and custType != ''">AND b.cust_type = #{custType}</if> <if test="custType != null and custType != ''">AND b.cust_type = #{custType}</if>
</select> </select>
<select id="">
</select> </mapper>
</mapper>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ibs.draw.mapper.DrawGridShapeRelateMapper">
<select id="selectByGridId" resultType="com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate">
SELECT *
FROM grid_draw_shape_relate
WHERE grid_id = #{gridId}
AND delete_flag = '0'
</select>
<select id="selectCustByDrawGridId" resultType="com.ruoyi.ibs.grid.domain.entity.RegionCustUser">
SELECT DISTINCT
sc.cust_id,
sc.cust_name,
sc.cust_type
FROM grid_draw_shape_relate sr
INNER JOIN draw_shape_cust_${headId} sc ON sc.shape_id = sr.shape_id
WHERE sr.grid_id = #{gridId}
AND sr.delete_flag = '0'
</select>
</mapper>

View File

@@ -21,6 +21,24 @@
</where> </where>
</select> </select>
<select id="selectLsCountListNew825" resultType="com.ruoyi.ibs.grid.domain.entity.GridCmpmCountLingshouNew825">
SELECT dt, grid_name, grid_name2, county, town, village, dept_id, dept_name, outlets_id, outlets_name,
user_name, cust_num, zf_365cnt, zf_180cnt, zf_90cnt, zf_30cnt, zf_365rt, zf_180rt, zf_90rt,
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, hxsd_num, region_code, ops_dept
FROM grid_cmpm_count_lingshou_new_825
<where>
<if test=" dt != null and dt != ''">and dt = #{dt}</if>
<if test=" town != null and town != ''">and town like concat('%',concat(#{town},'%'))</if>
<if test=" village != null and village != ''">and village like concat('%',concat(#{village},'%'))</if>
<if test=" isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test=" isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test=" isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
</where>
</select>
<select id="selectGsCountList" resultType="com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi"> <select id="selectGsCountList" resultType="com.ruoyi.ibs.grid.domain.entity.GridCmpmCountGongsi">
SELECT dt, grid_name, grid_name2, town, dept_id,dept_name, outlets_id,outlets_name, user_name, SELECT dt, grid_name, grid_name2, town, dept_id,dept_name, outlets_id,outlets_name, user_name,
cust_num, hq_cur_balance, bz_cur_balance, loan_balance_cny, finance_prod_711_balance,ustr_count_per_m,ustr_bal_m, cust_num, hq_cur_balance, bz_cur_balance, loan_balance_cny, finance_prod_711_balance,ustr_count_per_m,ustr_bal_m,
@@ -59,6 +77,47 @@
<if test=" custIdc != null and custIdc != ''">and cust_idc like concat('%',concat(#{custIdc},'%'))</if> <if test=" custIdc != null and custIdc != ''">and cust_idc like concat('%',concat(#{custIdc},'%'))</if>
</select> </select>
<select id="selectLsCustListNew825" resultType="com.ruoyi.ibs.grid.domain.entity.GridCustCountLingshouNew825">
SELECT cust_name,
cust_idc,
cust_isn,
cur_bal_d,
bal_loan,
loan_ave,
is_sx,
is_yx,
sx_bal,
is_yxht,
is_xyk,
fshl,
is_sd,
dian,
shui,
tax,
open_num,
dep_bal,
fin_bal,
is_grhx,
is_cfyx,
is_yxsbk,
is_2to3_sbk,
is_ylj_to_sbk,
is_hxsd,
region_code,
ops_dept,
cust_type,
is_365zf,
is_180zf,
is_90zf,
is_30zf,
dt
FROM grid_cust_count_lingshou_new_825
where region_code = #{regionCode}
<if test=" dt != null and dt != ''">and dt = #{dt}</if>
<if test=" custName != null and custName != ''">and cust_name like concat('%',concat(#{custName},'%'))</if>
<if test=" custIdc != null and custIdc != ''">and cust_idc like concat('%',concat(#{custIdc},'%'))</if>
</select>
<select id="selectGsCustList" resultType="com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi"> <select id="selectGsCustList" resultType="com.ruoyi.ibs.grid.domain.entity.GridCustCountGongsi">
SELECT cust_name, social_credit_code, cust_isn, hq_cur_balance, bz_cur_balance, is_credit, SELECT cust_name, social_credit_code, cust_isn, hq_cur_balance, bz_cur_balance, is_credit,
@@ -77,4 +136,4 @@
<if test=" socialCreditCode != null and socialCreditCode != ''">and social_credit_code like concat('%',concat(#{socialCreditCode},'%'))</if> <if test=" socialCreditCode != null and socialCreditCode != ''">and social_credit_code like concat('%',concat(#{socialCreditCode},'%'))</if>
</select> </select>
</mapper> </mapper>

View File

@@ -121,4 +121,30 @@
WHERE sec_grid_id = #{gridId} WHERE sec_grid_id = #{gridId}
AND cust_type = #{custType} AND cust_type = #{custType}
</select> </select>
<!-- 根据网格ID列表查询所有客户一级或二级网格 -->
<select id="selectAllCustByGridIds" resultType="RegionCustUser">
SELECT DISTINCT cust_id, cust_name, cust_type
FROM grid_region_cust_user_${headId}
WHERE (top_grid_id IN
<foreach collection="gridIds" item="gridId" index="index" open="(" separator="," close=")">
#{gridId}
</foreach>
OR sec_grid_id IN
<foreach collection="gridIds" item="gridId" index="index" open="(" separator="," close=")">
#{gridId}
</foreach>)
</select>
<!-- 根据绘制网格ID查询所有客户用于客群导入直接拼接headId绕过拦截器 -->
<select id="selectCustByDrawGridId" resultType="RegionCustUser">
SELECT DISTINCT
sc.cust_id,
sc.cust_name,
sc.cust_type
FROM grid_draw_shape_relate sr
LEFT JOIN draw_shape_cust_${headId} sc ON sc.shape_id = sr.shape_id
WHERE sr.grid_id = #{gridId}
AND sr.delete_flag = '0'
</select>
</mapper> </mapper>

View File

@@ -339,4 +339,26 @@
<select id="getSecGridIdByTopGridId" resultType="Long"> <select id="getSecGridIdByTopGridId" resultType="Long">
select grid_id from grid_region_grid where parent_grid_id = #{gridId} and grid_level = '2' and delete_flag = '0' select grid_id from grid_region_grid where parent_grid_id = #{gridId} and grid_level = '2' and delete_flag = '0'
</select> </select>
<!-- 查询地理网格列表(简化版)- 专用于客群创建 -->
<select id="getRegionGridListForGroup" parameterType="RegionGridListDTO" resultType="RegionGridGroupVO">
select distinct a.grid_id, a.grid_name
from grid_region_grid a
left join grid_region_user_relate c on c.grid_id = a.grid_id
where a.delete_flag = '0'
and c.delete_flag = '0'
and a.grid_level = #{gridLevel}
and (a.dept_id in (select dept_id from sys_dept where dept_id = #{deptId} or find_in_set(#{deptId}, ancestors))
or c.relate_dept_id in (select dept_id from sys_dept where dept_id = #{deptId} or find_in_set(#{deptId}, ancestors)))
<if test="opsDept != null and opsDept != ''">
and a.ops_dept = #{opsDept}
</if>
<if test="gridDutyType != null and gridDutyType != ''">
and a.grid_duty_type = #{gridDutyType}
</if>
<if test="gridName != null and gridName != ''">
and a.grid_name like concat('%', #{gridName}, '%')
</if>
ORDER BY a.grid_name
</select>
</mapper> </mapper>

View File

@@ -239,17 +239,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
case when left(vc.campaign_id,7)='appoint' then '-1' else sc.create_role end as create_role , case when left(vc.campaign_id,7)='appoint' then '-1' else sc.create_role end as create_role ,
case case
when dep2.dept_type = 'head' then dep.dept_id when hist_branch_dep.dept_id is not null then hist_branch_dep.dept_id
else dep2.dept_id when current_branch_dep.dept_type = 'head' or current_branch_dep.dept_type is null then current_outlet_dep.dept_id
else current_branch_dep.dept_id
end as dept_id, end as dept_id,
case case
when dep2.dept_type = 'head' then '' when hist_branch_dep.dept_id is not null then ifnull(hist_outlet_dep.dept_name, '')
else dep.dept_name when current_branch_dep.dept_type = 'head' or current_branch_dep.dept_type is null then ''
else current_outlet_dep.dept_name
end as outlets, end as outlets,
case case
when dep2.dept_type = 'head' then dep.dept_name when hist_branch_dep.dept_id is not null then hist_branch_dep.dept_name
else dep2.dept_name when current_branch_dep.dept_type = 'head' or current_branch_dep.dept_type is null then current_outlet_dep.dept_name
else current_branch_dep.dept_name
end as dept_name, end as dept_name,
concat( su.nick_name, '-', su.user_name ) as nick_name, concat( su.nick_name, '-', su.user_name ) as nick_name,
@@ -261,25 +264,48 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
visit_campaign_count vc left join sys_campaign sc on visit_campaign_count vc left join sys_campaign sc on
vc.campaign_id = sc.campaign_id left join sys_user su on vc.campaign_id = sc.campaign_id left join sys_user su on
vc.dept_id = su.user_name vc.dept_id = su.user_name
left join sys_dept dep on su.dept_id = dep.dept_id left join (
left join sys_dept dep2 on dep.parent_id = dep2.dept_id select campaign_id, user_id, max(dept_id) as dept_id, max(outlets_id) as outlets_id
from sys_campaign_group_customer
where user_id is not null
group by campaign_id, user_id
) scgc_his on scgc_his.campaign_id = vc.campaign_id and scgc_his.user_id = su.user_id
left join sys_dept hist_branch_dep on scgc_his.dept_id = hist_branch_dep.dept_id
left join sys_dept hist_outlet_dep on scgc_his.outlets_id = hist_outlet_dep.dept_id
left join sys_dept current_outlet_dep on su.dept_id = current_outlet_dep.dept_id
left join sys_dept current_branch_dep on current_outlet_dep.parent_id = current_branch_dep.dept_id
where where
vc.dept_type = '3' vc.dept_type = '3'
<if test="campaignId != null and campaignId != ''"> and vc.campaign_id = #{campaignId}</if> <if test="campaignId != null and campaignId != ''"> and vc.campaign_id = #{campaignId}</if>
<if test="campaignName != null and campaignName != ''"> AND sc.campaign_name like concat('%', #{campaignName}, '%')</if> <if test="campaignName != null and campaignName != ''"> AND sc.campaign_name like concat('%', #{campaignName}, '%')</if>
<choose> <choose>
<when test="outletsId != null"> <when test="outletsId != null">
AND (dep.dept_id = #{outletsId} OR dep2.dept_id = #{outletsId}) AND ifnull(
scgc_his.outlets_id,
case
when current_branch_dep.dept_type = 'head' or current_branch_dep.dept_type is null then null
else current_outlet_dep.dept_id
end
) = #{outletsId}
</when> </when>
<otherwise> <otherwise>
<if test="deptId != null and personType == 'branch' and deptId.toString().endsWith('000')">
AND dep.dept_id = #{deptId}
</if>
<if test="deptId != null and personType == 'branch'"> <if test="deptId != null and personType == 'branch'">
AND (dep.dept_id = #{deptId} OR dep2.dept_id = #{deptId}) AND ifnull(
scgc_his.dept_id,
case
when current_branch_dep.dept_type = 'head' or current_branch_dep.dept_type is null then current_outlet_dep.dept_id
else current_branch_dep.dept_id
end
) = #{deptId}
</if> </if>
<if test="deptId != null and personType == 'outlet'"> <if test="deptId != null and personType == 'outlet'">
AND (dep.dept_id = #{deptId}) AND ifnull(
scgc_his.outlets_id,
case
when current_branch_dep.dept_type = 'head' or current_branch_dep.dept_type is null then null
else current_outlet_dep.dept_id
end
) = #{deptId}
</if> </if>
</otherwise> </otherwise>
</choose> </choose>
@@ -370,4 +396,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{dt} #{dt}
</foreach> </foreach>
</delete> </delete>
</mapper> </mapper>

View File

@@ -127,6 +127,18 @@
<result property="remark" column="remark" /> <result property="remark" column="remark" />
<result property="custType" column="cust_type" /> <result property="custType" column="cust_type" />
<result property="deptName" column="dept_name" /> <result property="deptName" column="dept_name" />
<result property="interAddr" column="inter_addr" />
<result property="colStafName" column="col_staf_name" />
<result property="laterNote" column="later_note" />
<result property="intentionProductValue" column="intention_product_value" />
<result property="feedbackStatus" column="feedback_status" />
<result property="sourceOfInterview" column="source_of_interview" />
<result property="filename" column="filename" />
<result property="outCallStatus" column="out_call_status" />
<result property="outCallIntention" column="out_call_intention" />
<result property="source" column="source" />
<result property="analysisValue" column="analysis_value" />
<result property="facility" column="facility" />
</resultMap> </resultMap>
<sql id="selectSysCampaignVo"> <sql id="selectSysCampaignVo">
@@ -1388,8 +1400,10 @@
from visit_info vi from visit_info vi
left join sys_dept d on vi.dept_id = d.dept_id left join sys_dept d on vi.dept_id = d.dept_id
<where> <where>
<if test="campaignId != null and campaignId != ''"> and vi.campaign_id = #{campaignId}</if>
<if test="custType != null and custType != ''"> and vi.cust_type = #{custType}</if> <if test="custType != null and custType != ''"> and vi.cust_type = #{custType}</if>
<if test="visName != null and visName != ''"> and vi.vis_name like concat('%', #{visName}, '%')</if> <if test="visName != null and visName != ''"> and vi.vis_name like concat('%', #{visName}, '%')</if>
<if test="visId != null and visId != ''"> and vi.vis_id = #{visId}</if>
<if test="custIdc != null and custIdc != ''"> and vi.cust_idc = #{custIdc}</if> <if test="custIdc != null and custIdc != ''"> and vi.cust_idc = #{custIdc}</if>
<if test="socialCreditCode != null and socialCreditCode != ''"> and vi.social_credit_code = #{socialCreditCode}</if> <if test="socialCreditCode != null and socialCreditCode != ''"> and vi.social_credit_code = #{socialCreditCode}</if>
<if test="abnormalVisitTag != null and abnormalVisitTag != ''"> and vi.abnormal_visit_tag = #{abnormalVisitTag}</if> <if test="abnormalVisitTag != null and abnormalVisitTag != ''"> and vi.abnormal_visit_tag = #{abnormalVisitTag}</if>
@@ -1410,4 +1424,57 @@
order by vi.sign_in_time desc order by vi.sign_in_time desc
</select> </select>
</mapper> <select id="selectVisitInfoList875" parameterType="VisitInfoDTO" resultType="VisitInfoVO">
select vi.id,vi.campaign_id,vi.campaign_name,vi.vis_name,vi.vis_id,vi.dept_id,d.dept_name,vi.vis_time,vi.cust_name,vi.cust_type,vi.cust_idc,
vi.social_credit_code,vi.create_by,vi.create_time,vi.update_by,vi.update_time,vi.remark,vi.sign_in_time,vi.sign_out_time,vi.sign_in_address,
vi.sign_out_address,vi.abnormal_visit_tag,vi.abnormal_visit_info,vi.sign_in_coordinate,vi.sign_out_coordinate,vi.is_valid_cust,vi.marketing_way,vi.inter_res,
vi.inter_addr,vi.col_staf_name,vi.later_note,vi.intention_product_value,vi.feedback_status,vi.source_of_interview,vi.filename,
vi.out_call_status,vi.out_call_intention,vi.source,vi.analysis_value,vi.facility
from visit_info_875 vi
left join sys_dept d on vi.dept_id = d.dept_id
<where>
<if test="campaignId != null and campaignId != ''"> and vi.campaign_id = #{campaignId}</if>
<if test="custType != null and custType != ''"> and vi.cust_type = #{custType}</if>
<if test="visName != null and visName != ''"> and vi.vis_name like concat('%', #{visName}, '%')</if>
<if test="visId != null and visId != ''"> and vi.vis_id = #{visId}</if>
<if test="custIdc != null and custIdc != ''"> and vi.cust_idc = #{custIdc}</if>
<if test="socialCreditCode != null and socialCreditCode != ''"> and vi.social_credit_code = #{socialCreditCode}</if>
<if test="abnormalVisitTag != null and abnormalVisitTag != ''"> and vi.abnormal_visit_tag = #{abnormalVisitTag}</if>
<if test="marketingWay != null and marketingWay != ''"> and vi.marketing_way = #{marketingWay}</if>
<if test="interRes != null and interRes != ''"> and vi.inter_res = #{interRes}</if>
<if test="visTime != null">
and (vi.sign_in_time like concat(#{visTime}, '%') or vi.sign_out_time like concat(#{visTime}, '%'))
</if>
<if test="userRole != null">
<choose>
<when test="userRole == 'manager'"> and vi.vis_id = #{userName} </when>
<when test="userRole == 'outlet'"> and d.dept_id = #{deptId} </when>
<when test="userRole == 'branch'"> and (d.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) </when>
<when test="userRole in {'head', 'ops', 'public', 'private'}"> and left(d.dept_id,3) = left(#{deptId},3) </when>
</choose>
</if>
</where>
order by vi.sign_in_time desc
</select>
<update id="updateVisitInfoFeedback" parameterType="VisitInfoFeedbackUpdateDTO">
update visit_info_875 vi
left join sys_dept d on vi.dept_id = d.dept_id
set vi.source = #{source},
vi.remark = #{remark},
vi.intention_product_value = #{intentionProductValue},
vi.inter_res = '0',
vi.update_by = #{userName},
vi.update_time = sysdate()
where vi.id = #{id}
<if test="userRole != null">
<choose>
<when test="userRole == 'manager'"> and vi.vis_id = #{userName} </when>
<when test="userRole == 'outlet'"> and d.dept_id = #{deptId} </when>
<when test="userRole == 'branch'"> and (d.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) </when>
<when test="userRole in {'head', 'ops', 'public', 'private'}"> and left(d.dept_id,3) = left(#{deptId},3) </when>
</choose>
</if>
</update>
</mapper>

View File

@@ -215,6 +215,13 @@
<version>${ruoyi.version}</version> <version>${ruoyi.version}</version>
</dependency> </dependency>
<!-- 数字支行-客群模块 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ibs-group</artifactId>
<version>3.8.8</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -28,6 +28,10 @@ spring:
username: root username: root
password: Kfcx@1234 password: Kfcx@1234
driverClassName: com.mysql.cj.jdbc.Driver 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: slave:
# 从数据源开关/默认关闭 # 从数据源开关/默认关闭
@@ -78,12 +82,14 @@ spring:
redis: redis:
# 地址 # 地址
host: 116.62.17.81 host: 116.62.17.81
# host: r-bp1mmtcknvsscsrjrypd.redis.rds.aliyuncs.com
# 端口默认为6379 # 端口默认为6379
port: 6380 port: 6379
# 数据库索引 # 数据库索引
database: 0 database: 0
# 密码 # 密码
password: Kfcx@1234 password: Kfcx@1234
# password: N0f3d12c4a927eee1+
# 连接超时时间 # 连接超时时间
timeout: 10s timeout: 10s
lettuce: lettuce:
@@ -121,5 +127,9 @@ oss:
accessKeyId: LTAI5tMsUgorcgnpTxZDV1wS accessKeyId: LTAI5tMsUgorcgnpTxZDV1wS
accessKeySecret: c7qIjXIPx8Cz2CriJpYGyCFwFjRxeB accessKeySecret: c7qIjXIPx8Cz2CriJpYGyCFwFjRxeB
bucketName: oss-wkc bucketName: oss-wkc
# endpoint: oss-cn-hangzhou.aliyuncs.comBucket:znjgoss.oss-cn-hangzhou.aliyuncs.com
# accessKeyId: LTAI5tCRocKhQaCtFnYKp46w
# accessKeySecret: 0ovFbMQWas1wOZTG91mpKbV70JgR32
# bucketName: oss-wkc

View File

@@ -68,7 +68,7 @@ mybatis-plus:
# 搜索指定包别名 # 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件 # 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml mapperLocations: classpath*:mapper/*Mapper.xml,classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件 # 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml configLocation: classpath:mybatis/mybatis-config.xml
type-handlers-package: com.ruoyi.ibs.handler type-handlers-package: com.ruoyi.ibs.handler

View File

@@ -38,6 +38,10 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ibs</artifactId> <artifactId>ibs</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ibs-group</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@@ -1,6 +1,7 @@
package com.ruoyi.quartz.task; package com.ruoyi.quartz.task;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.group.service.ICustGroupService;
import com.ruoyi.ibs.cmpm.service.GridCmpmService; import com.ruoyi.ibs.cmpm.service.GridCmpmService;
import com.ruoyi.ibs.dashboard.service.FileOptService; import com.ruoyi.ibs.dashboard.service.FileOptService;
import com.ruoyi.ibs.draw.service.DrawGridCustService; import com.ruoyi.ibs.draw.service.DrawGridCustService;
@@ -47,6 +48,9 @@ public class RyTask
@Resource @Resource
private AddressAnalyseService addressAnalyseService; private AddressAnalyseService addressAnalyseService;
@Resource
private ICustGroupService custGroupService;
public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
{ {
@@ -111,4 +115,12 @@ public class RyTask
addressAnalyseService.pointInGeometryScheduled(); addressAnalyseService.pointInGeometryScheduled();
} }
public void updateDynamicCustGroups() {
custGroupService.updateDynamicCustGroups();
}
public void checkAndDisableExpiredGroups() {
custGroupService.checkAndDisableExpiredGroups();
}
} }

View File

@@ -31,6 +31,12 @@ public class SysNotice extends BaseEntity
/** 公告状态0正常 1关闭 */ /** 公告状态0正常 1关闭 */
private String status; private String status;
/** 可见总行部门ID多选逗号分隔 */
private String deptIds;
/** 当前登录用户所属总行部门ID仅用于查询过滤 */
private String currentHeadDeptId;
public Long getNoticeId() public Long getNoticeId()
{ {
return noticeId; return noticeId;
@@ -84,6 +90,26 @@ public class SysNotice extends BaseEntity
return status; return status;
} }
public String getDeptIds()
{
return deptIds;
}
public void setDeptIds(String deptIds)
{
this.deptIds = deptIds;
}
public String getCurrentHeadDeptId()
{
return currentHeadDeptId;
}
public void setCurrentHeadDeptId(String currentHeadDeptId)
{
this.currentHeadDeptId = currentHeadDeptId;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -92,6 +118,7 @@ public class SysNotice extends BaseEntity
.append("noticeType", getNoticeType()) .append("noticeType", getNoticeType())
.append("noticeContent", getNoticeContent()) .append("noticeContent", getNoticeContent())
.append("status", getStatus()) .append("status", getStatus())
.append("deptIds", getDeptIds())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())
.append("createTime", getCreateTime()) .append("createTime", getCreateTime())
.append("updateBy", getUpdateBy()) .append("updateBy", getUpdateBy())

View File

@@ -20,7 +20,9 @@ public enum OssFileEnum {
VISIT_RECORD("VISIT_RECORD/"), 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; private String PREFIX;

Some files were not shown because too many files have changed in this diff Show More