Compare commits
34 Commits
dev-ui
...
0bf73a923f
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bf73a923f | |||
| ec67794f88 | |||
| 3ef45bc398 | |||
| 37e17ac903 | |||
| d561d068d6 | |||
| 43bc0e4f65 | |||
| 3fe78d8d3a | |||
| 4c58966529 | |||
| 3bc60fedeb | |||
| 4d1acc7484 | |||
| 402a0c3e2f | |||
| 5980ed0790 | |||
| 75cb8967da | |||
| 90a5c42313 | |||
| 356bcdd6de | |||
| 9a60371a8f | |||
| 380f9b4e7a | |||
| 928f65dfca | |||
| c64146ac40 | |||
| 0541ce0ac6 | |||
| 26c639134e | |||
| 0f7b57e824 | |||
| 104e8697fe | |||
| bbc6a2050b | |||
| bf7a4c0538 | |||
| b2e177dd24 | |||
| 2071d04c08 | |||
| 4988ab5944 | |||
| c00d5475e6 | |||
| 0b64532959 | |||
| 9f0ad4ce87 | |||
| 75b5989774 | |||
| d8c069a836 | |||
| 26be75adad |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -97,7 +97,3 @@ tongweb_62318.properties
|
|||||||
.superpowers/
|
.superpowers/
|
||||||
|
|
||||||
tmp/
|
tmp/
|
||||||
|
|
||||||
.codegraph/
|
|
||||||
|
|
||||||
.claude/
|
|
||||||
@@ -67,7 +67,6 @@
|
|||||||
- 前端相关安装、构建、调试、测试命令执行前,必须先通过 `nvm` 切换并确认 Node 版本
|
- 前端相关安装、构建、调试、测试命令执行前,必须先通过 `nvm` 切换并确认 Node 版本
|
||||||
- 测试结束后,自动关闭测试过程中启动的前后端进程
|
- 测试结束后,自动关闭测试过程中启动的前后端进程
|
||||||
- 重启后端时,必须优先使用 `bin/restart_java_backend.sh`
|
- 重启后端时,必须优先使用 `bin/restart_java_backend.sh`
|
||||||
- 禁止在前端源码、配置、示例数据或页面默认值中硬编码或预填真实账号密码;登录页不得将密码保存到 Cookie、localStorage 或 sessionStorage
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -258,7 +257,6 @@ return AjaxResult.success(result);
|
|||||||
- 请求统一使用 `@/utils/request`
|
- 请求统一使用 `@/utils/request`
|
||||||
- 新增页面或功能入口时,同步检查 `sys_menu`、路由、权限标识
|
- 新增页面或功能入口时,同步检查 `sys_menu`、路由、权限标识
|
||||||
- 优先延续现有 `ccdi*` 业务目录与命名方式,不随意新造平行目录
|
- 优先延续现有 `ccdi*` 业务目录与命名方式,不随意新造平行目录
|
||||||
- 登录页只能在用户主动选择时保存用户名,不允许保存密码或预填默认密码
|
|
||||||
|
|
||||||
### 导入功能规范
|
### 导入功能规范
|
||||||
|
|
||||||
|
|||||||
669
CLAUDE.md
Normal file
669
CLAUDE.md
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## 快速参考
|
||||||
|
|
||||||
|
**启动项目:**
|
||||||
|
- 后端: `mvn spring-boot:run` 或运行 `ry.bat`
|
||||||
|
- 前端: `cd ruoyi-ui && npm run dev`
|
||||||
|
|
||||||
|
**访问地址:**
|
||||||
|
- 前端: http://localhost:80
|
||||||
|
- 后端: http://localhost:8080
|
||||||
|
- Swagger: http://localhost:8080/swagger-ui/index.html
|
||||||
|
- Druid 监控: http://localhost:8080/druid/ (ruoyi/123456)
|
||||||
|
|
||||||
|
**测试账号:**
|
||||||
|
- 用户名: `admin`
|
||||||
|
- 密码: `admin123`
|
||||||
|
|
||||||
|
**获取 Token:**
|
||||||
|
```bash
|
||||||
|
POST http://localhost:8080/login/test?username=admin&password=admin123
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
**纪检初核系统** - 基于 **若依管理系统 v3.9.1** 构建的企业级前后端分离管理系统,用于员工异常行为风险识别。
|
||||||
|
|
||||||
|
### 技术栈版本
|
||||||
|
|
||||||
|
| 后端技术 | 版本 | 前端技术 | 版本 |
|
||||||
|
|-----------------------------|--------|------------|---------|
|
||||||
|
| Spring Boot | 3.5.8 | Vue.js | 2.6.12 |
|
||||||
|
| Java | 21 | Element UI | 2.15.14 |
|
||||||
|
| MyBatis Spring Boot Starter | 3.0.5 | Vuex | 3.6.0 |
|
||||||
|
| MySQL Connector | 8.2.0 | Vue Router | 3.4.9 |
|
||||||
|
| SpringDoc OpenAPI | 2.8.14 | Axios | 0.28.1 |
|
||||||
|
| EasyExcel | 3.3.4 | ECharts | 5.4.0 |
|
||||||
|
| Quartz | 2.5.2 | Sass | 1.32.13 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
### 后端 (Maven)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 编译项目
|
||||||
|
mvn clean compile
|
||||||
|
|
||||||
|
# 运行应用 (开发环境)
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
|
# 打包部署
|
||||||
|
mvn clean package
|
||||||
|
|
||||||
|
# Windows 启动
|
||||||
|
ry.bat
|
||||||
|
|
||||||
|
# Linux/Mac 启动
|
||||||
|
./ry.sh start
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前端 (npm)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ruoyi-ui
|
||||||
|
|
||||||
|
# 安装依赖 (推荐使用国内镜像)
|
||||||
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
|
# 开发服务器 (端口 80)
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 生产构建
|
||||||
|
npm run build:prod
|
||||||
|
|
||||||
|
# 预览生产构建
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据库初始化
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 初始化若依框架基础表
|
||||||
|
mysql -u root -p < sql/ry_20250522.sql
|
||||||
|
|
||||||
|
# 初始化定时任务表
|
||||||
|
mysql -u root -p < sql/quartz.sql
|
||||||
|
|
||||||
|
# 导入业务表(根据需要执行)
|
||||||
|
mysql -u root -p ccdi < sql/dpc_employee.sql
|
||||||
|
mysql -u root -p ccdi < sql/dpc_intermediary_blacklist.sql
|
||||||
|
# ... 其他业务表脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意:**
|
||||||
|
- 业务表脚本文件名以 `ccdi_` 或 `dpc_` 开头
|
||||||
|
- 部分脚本包含菜单数据,需要按顺序执行
|
||||||
|
- 数据库需要先创建(数据库名: `ccdi`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 模块架构
|
||||||
|
|
||||||
|
```
|
||||||
|
ccdi/
|
||||||
|
├── ruoyi-admin/ # 主应用入口 (Spring Boot 启动类)
|
||||||
|
├── ruoyi-framework/ # 核心框架 (Security, Config, Filters)
|
||||||
|
├── ruoyi-system/ # 系统管理 (Users, Roles, Menus, Depts)
|
||||||
|
├── ruoyi-common/ # 通用工具 (annotations, utils, constants)
|
||||||
|
├── ruoyi-quartz/ # 定时任务
|
||||||
|
├── ruoyi-generator/ # 代码生成器
|
||||||
|
├── ccdi-info-collection/ # 【核心业务模块】信息采集
|
||||||
|
├── ccdi-project/ # 【核心业务模块】项目管理
|
||||||
|
├── ccdi-lsfx/ # 【核心业务模块】流水分析对接
|
||||||
|
├── lsfx-mock-server/ # 流水分析模拟服务器 (Python)
|
||||||
|
├── ruoyi-ui/ # 前端 Vue 应用
|
||||||
|
├── sql/ # 数据库脚本
|
||||||
|
├── bin/ # 启动脚本
|
||||||
|
└── doc/ # 项目文档
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
ruoyi-admin (启动模块)
|
||||||
|
├── ruoyi-framework (核心安全配置)
|
||||||
|
├── ruoyi-system (系统核心业务)
|
||||||
|
├── ruoyi-common (共享工具)
|
||||||
|
├── ruoyi-quartz (定时任务)
|
||||||
|
├── ruoyi-generator (代码生成)
|
||||||
|
├── ccdi-info-collection (信息采集模块)
|
||||||
|
│ └── 依赖 ruoyi-common
|
||||||
|
├── ccdi-project (项目管理模块)
|
||||||
|
│ └── 依赖 ruoyi-common
|
||||||
|
└── ccdi-lsfx (流水分析对接模块)
|
||||||
|
└── 依赖 ruoyi-common
|
||||||
|
```
|
||||||
|
|
||||||
|
**添加新业务模块:**
|
||||||
|
1. 在根目录 `pom.xml` 的 `<modules>` 中添加新模块
|
||||||
|
2. 在新模块的 `pom.xml` 中添加对 `ruoyi-common` 的依赖
|
||||||
|
3. 在 `ruoyi-admin/pom.xml` 中添加对新模块的依赖
|
||||||
|
4. 在新模块中按照分层规范创建 controller/service/mapper/domain 包
|
||||||
|
|
||||||
|
### ccdi-info-collection 业务模块 (核心)
|
||||||
|
|
||||||
|
自定义业务模块,包含以下核心功能:
|
||||||
|
|
||||||
|
| 功能 | Controller | 实体类 |
|
||||||
|
|----------|---------------------------------------|-----------------------------|
|
||||||
|
| 员工基础信息 | CcdiBaseStaffController | CcdiBaseStaff |
|
||||||
|
| 中介黑名单 | CcdiIntermediaryController | CcdiBizIntermediary |
|
||||||
|
| 员工家庭关系 | CcdiStaffFmyRelationController | CcdiStaffFmyRelation |
|
||||||
|
| 员工企业关系 | CcdiStaffEnterpriseRelationController | CcdiStaffEnterpriseRelation |
|
||||||
|
| 信贷客户家庭关系 | CcdiCustFmyRelationController | CcdiCustFmyRelation |
|
||||||
|
| 信贷客户企业关系 | CcdiCustEnterpriseRelationController | CcdiCustEnterpriseRelation |
|
||||||
|
| 员工调动记录 | CcdiStaffTransferController | CcdiStaffTransfer |
|
||||||
|
| 员工招聘记录 | CcdiStaffRecruitmentController | CcdiStaffRecruitment |
|
||||||
|
| 采购交易 | CcdiPurchaseTransactionController | CcdiPurchaseTransaction |
|
||||||
|
|
||||||
|
**分层结构:**
|
||||||
|
|
||||||
|
- Controller: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/`
|
||||||
|
- Service: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/`
|
||||||
|
- Mapper: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/`
|
||||||
|
- Domain: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/`
|
||||||
|
- dto/: 数据传输对象
|
||||||
|
- vo/: 视图对象
|
||||||
|
- excel/: Excel导入导出实体
|
||||||
|
- XML映射: `ccdi-info-collection/src/main/resources/mapper/info/collection/`
|
||||||
|
|
||||||
|
### ccdi-project 业务模块 (核心)
|
||||||
|
|
||||||
|
项目管理模块,用于管理纪检初核项目的全生命周期:
|
||||||
|
|
||||||
|
**核心功能:**
|
||||||
|
- 项目创建、更新、删除、查询
|
||||||
|
- 项目状态管理 (进行中、已完成、已归档)
|
||||||
|
- 项目统计(按状态统计数量)
|
||||||
|
- 模型参数配置管理
|
||||||
|
|
||||||
|
**主要 Controller:**
|
||||||
|
- CcdiProjectController: 项目管理
|
||||||
|
- CcdiModelParamController: 模型参数配置
|
||||||
|
|
||||||
|
**分层结构:**
|
||||||
|
- Controller: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/`
|
||||||
|
- Service: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/`
|
||||||
|
- Mapper: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/`
|
||||||
|
- Domain: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/`
|
||||||
|
- XML映射: `ccdi-project/src/main/resources/mapper/ccdi/project/`
|
||||||
|
|
||||||
|
### ccdi-lsfx 业务模块 (核心)
|
||||||
|
|
||||||
|
流水分析平台对接模块,用于与外部流水分析系统交互:
|
||||||
|
|
||||||
|
**核心功能:**
|
||||||
|
- 获取访问令牌 (Token)
|
||||||
|
- 上传流水文件并解析
|
||||||
|
- 拉取行内流水数据
|
||||||
|
- 查询解析状态和结果
|
||||||
|
- 获取银行流水明细
|
||||||
|
|
||||||
|
**主要组件:**
|
||||||
|
- LsfxAnalysisClient: 流水分析平台客户端
|
||||||
|
- LsfxTestController: 测试接口
|
||||||
|
|
||||||
|
**配置项 (application-dev.yml):**
|
||||||
|
```yaml
|
||||||
|
lsfx:
|
||||||
|
api:
|
||||||
|
base-url: http://localhost:8000 # 流水分析平台地址
|
||||||
|
app-id: your-app-id
|
||||||
|
app-secret: your-app-secret
|
||||||
|
client-id: your-client-id
|
||||||
|
endpoints:
|
||||||
|
get-token: /api/auth/token
|
||||||
|
upload-file: /api/files/upload
|
||||||
|
fetch-inner-flow: /api/flow/inner
|
||||||
|
```
|
||||||
|
|
||||||
|
**分层结构:**
|
||||||
|
- Client: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/`
|
||||||
|
- Controller: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/`
|
||||||
|
- Domain: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/`
|
||||||
|
- request/: 请求对象
|
||||||
|
- response/: 响应对象
|
||||||
|
- Config: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/`
|
||||||
|
|
||||||
|
### lsfx-mock-server (开发测试工具)
|
||||||
|
|
||||||
|
Python 实现的流水分析平台模拟服务器,用于本地开发和测试:
|
||||||
|
|
||||||
|
**用途:**
|
||||||
|
- 模拟流水分析平台的 API 接口
|
||||||
|
- 提供测试数据和模拟响应
|
||||||
|
- 支持错误场景模拟
|
||||||
|
|
||||||
|
**启动方式:**
|
||||||
|
```bash
|
||||||
|
cd lsfx-mock-server
|
||||||
|
python app.py # 默认监听 http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后端开发规范
|
||||||
|
|
||||||
|
### 通用规范
|
||||||
|
|
||||||
|
- **新模块命名**: 项目英文名首字母集合 + 主要功能 (如 `ruoyi-info-collection`)
|
||||||
|
- **代码分离**: 新功能代码与若依框架自带代码分离,Controller 放在新模块中
|
||||||
|
- **审计字段**: 实体类不继承 BaseEntity,单独添加审计字段,通过注释实现自动插入
|
||||||
|
|
||||||
|
### Java 代码风格
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 使用 @Data 注解
|
||||||
|
@Data
|
||||||
|
public class CcdiBaseStaff {
|
||||||
|
// 审计字段通过注释实现自动插入
|
||||||
|
/** 创建者 */
|
||||||
|
private String createBy;
|
||||||
|
/** 创建时间 */
|
||||||
|
private Date createTime;
|
||||||
|
/** 更新者 */
|
||||||
|
private String updateBy;
|
||||||
|
/** 更新时间 */
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务层使用 @Resource 注入
|
||||||
|
@Resource
|
||||||
|
private ICcdiBaseStaffService baseStaffService;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 分层规范
|
||||||
|
|
||||||
|
- **Controller**: 所有接口添加 Swagger 注释,分页使用 MyBatis Plus Page
|
||||||
|
- **Service**: 简单 CRUD 用 MyBatis Plus 方法,复杂操作在 XML 写 SQL
|
||||||
|
- **DTO/VO**: 接口传参使用独立 DTO,返回使用独立 VO,不与 entity 混用
|
||||||
|
- **Mapper**: 简单操作继承 BaseMapper,复杂操作在 XML 中定义
|
||||||
|
|
||||||
|
### 禁止事项
|
||||||
|
|
||||||
|
- **禁止使用全限定类名**: 必须使用 `import` 语句导入类,不要在代码中使用 `java.util.List` 这样的全限定名
|
||||||
|
- **禁止使用 `extends ServiceImpl<>`**: Service 接口和实现类分离定义
|
||||||
|
- **禁止 Entity 混用**: DTO、VO、Excel 类必须独立,不与 Entity 混用
|
||||||
|
- **禁止缺少 `@Resource`**: Service 注入必须使用 `@Resource` 注解
|
||||||
|
|
||||||
|
### API 响应格式
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 成功
|
||||||
|
AjaxResult.success("操作成功", data);
|
||||||
|
|
||||||
|
// 错误
|
||||||
|
AjaxResult.error("操作失败");
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
Page<CcdiBaseStaff> page = new Page<>(pageNum, pageSize);
|
||||||
|
IPage<CcdiBaseStaff> result = baseStaffMapper.selectPage(page, queryWrapper);
|
||||||
|
return AjaxResult.success(result);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前端开发规范
|
||||||
|
|
||||||
|
### 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ruoyi-ui/src/
|
||||||
|
├── api/ # API 请求定义 (与后端 Controller 对应)
|
||||||
|
├── views/ # 页面组件 (按功能模块组织)
|
||||||
|
│ ├── ccdiBaseStaff/
|
||||||
|
│ ├── ccdiIntermediary/
|
||||||
|
│ └── ...
|
||||||
|
├── components/ # 可复用组件 (复杂组件需拆分)
|
||||||
|
├── router/ # 路由配置
|
||||||
|
└── store/ # Vuex 状态管理
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 调用示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function listStaff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ccdi/baseStaff/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 菜单联动
|
||||||
|
|
||||||
|
添加页面和组件后,需要同步修改数据库中的菜单表 (`sys_menu`)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 特殊功能
|
||||||
|
|
||||||
|
### 异步导入
|
||||||
|
|
||||||
|
支持大数据量异步 Excel 导入,通过 taskId 查询导入状态:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@PostMapping("/import")
|
||||||
|
public AjaxResult asyncImport(@RequestParam("file") MultipartFile file) {
|
||||||
|
String taskId = asyncImportService.startImport(file);
|
||||||
|
return AjaxResult.success("导入任务已启动", taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/import/status/{taskId}")
|
||||||
|
public AjaxResult getImportStatus(@PathVariable String taskId) {
|
||||||
|
return AjaxResult.success(asyncImportService.getStatus(taskId));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**导入流程:**
|
||||||
|
1. 前端上传 Excel 文件
|
||||||
|
2. 后端异步处理,返回 taskId
|
||||||
|
3. 前端轮询 `/import/status/{taskId}` 获取导入进度
|
||||||
|
4. 导入完成后,可获取成功/失败数据统计
|
||||||
|
|
||||||
|
**导入结果处理:**
|
||||||
|
- 只返回导入失败的数据(含失败原因)
|
||||||
|
- 成功数据不返回,减少响应体积
|
||||||
|
- 支持批量插入,提高性能
|
||||||
|
|
||||||
|
### EasyExcel 字典下拉框
|
||||||
|
|
||||||
|
导入模板支持字典下拉框配置,提升数据录入准确性。使用 `DictDropdownWriteHandler` 实现。
|
||||||
|
|
||||||
|
### 权限控制
|
||||||
|
|
||||||
|
基于 Spring Security + JWT 的角色菜单权限系统:
|
||||||
|
|
||||||
|
- 权限格式: `system:user:edit`, `ccdi:staff:list`
|
||||||
|
- 数据权限: 支持全部、自定义、部门等范围
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试与验证
|
||||||
|
|
||||||
|
### 测试账号
|
||||||
|
|
||||||
|
- **用户名**: `admin`
|
||||||
|
- **密码**: `admin123`
|
||||||
|
|
||||||
|
### 登录获取 Token
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 登录接口
|
||||||
|
POST /login/test?username=admin&password=admin123
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 文档
|
||||||
|
|
||||||
|
- **Swagger UI**: `/swagger-ui/index.html`
|
||||||
|
- **API Docs**: `/v3/api-docs`
|
||||||
|
|
||||||
|
### 测试规范
|
||||||
|
|
||||||
|
- 不在命令行启动后端进行测试
|
||||||
|
- 生成可执行的测试脚本进行验证
|
||||||
|
- 测试完成后保存接口输出并生成测试用例报告
|
||||||
|
|
||||||
|
### 开发调试技巧
|
||||||
|
|
||||||
|
**使用 Swagger 测试接口:**
|
||||||
|
1. 访问 `/swagger-ui/index.html`
|
||||||
|
2. 点击接口展开详情
|
||||||
|
3. 点击 "Try it out" 进行测试
|
||||||
|
4. 填写参数后点击 "Execute" 执行
|
||||||
|
|
||||||
|
**查看 SQL 执行日志:**
|
||||||
|
- 在 `application.yml` 中设置日志级别: `com.ruoyi: debug`
|
||||||
|
- 使用 Druid 监控台查看慢 SQL
|
||||||
|
|
||||||
|
**前端代理配置:**
|
||||||
|
前端开发服务器通过代理转发请求到后端:
|
||||||
|
- 前端地址: `http://localhost:80`
|
||||||
|
- 后端地址: `http://localhost:8080`
|
||||||
|
- 代理配置文件: `ruoyi-ui/vue.config.js`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
|
| 配置项 | 值 |
|
||||||
|
|---------|-------------------|
|
||||||
|
| 后端端口 | 8080 |
|
||||||
|
| 前端开发端口 | 80 |
|
||||||
|
| 默认管理员 | admin/admin123 |
|
||||||
|
| JWT 有效期 | 30 分钟 |
|
||||||
|
| 文件上传限制 | 单文件 10MB, 总计 20MB |
|
||||||
|
|
||||||
|
### 配置文件位置
|
||||||
|
|
||||||
|
| 配置 | 路径 |
|
||||||
|
|----------|------------------------------------------------------|
|
||||||
|
| 主配置 | `ruoyi-admin/src/main/resources/application.yml` |
|
||||||
|
| 开发环境 | `ruoyi-admin/src/main/resources/application-dev.yml` |
|
||||||
|
| 数据库连接 | `application-dev.yml` |
|
||||||
|
| Redis 配置 | `application-dev.yml` |
|
||||||
|
|
||||||
|
### 数据源配置
|
||||||
|
|
||||||
|
项目使用 Druid 连接池,支持主从分离(默认关闭从库):
|
||||||
|
|
||||||
|
- **数据库连接**: `jdbc:mysql://host:3306/ccdi`
|
||||||
|
- **初始连接数**: 5
|
||||||
|
- **最小连接数**: 10
|
||||||
|
- **最大连接数**: 20
|
||||||
|
- **慢 SQL 记录**: 超过 1000ms 的 SQL 会被记录
|
||||||
|
|
||||||
|
### Redis 配置
|
||||||
|
|
||||||
|
- **默认端口**: 6379
|
||||||
|
- **数据库索引**: 0
|
||||||
|
- **连接超时**: 10s
|
||||||
|
|
||||||
|
### 流水分析平台配置
|
||||||
|
|
||||||
|
项目集成了外部流水分析平台,配置项位于 `application-dev.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
lsfx:
|
||||||
|
api:
|
||||||
|
base-url: http://localhost:8000 # 流水分析平台基础地址
|
||||||
|
app-id: ccdi-app # 应用ID
|
||||||
|
app-secret: ccdi-secret-2024 # 应用密钥
|
||||||
|
client-id: ccdi-client # 客户端ID
|
||||||
|
endpoints:
|
||||||
|
get-token: /api/auth/token # 获取令牌接口
|
||||||
|
upload-file: /api/files/upload # 文件上传接口
|
||||||
|
fetch-inner-flow: /api/flow/inner # 拉取行内流水接口
|
||||||
|
```
|
||||||
|
|
||||||
|
**开发环境使用 Mock 服务器:**
|
||||||
|
- 本地开发时,将 `base-url` 设置为 `http://localhost:8000`
|
||||||
|
- 启动 `lsfx-mock-server` 提供模拟接口
|
||||||
|
- 生产环境替换为真实的流水分析平台地址
|
||||||
|
|
||||||
|
### MCP 配置
|
||||||
|
|
||||||
|
项目使用 MCP (Model Context Protocol) 连接数据库,配置文件: `.mcp.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"mysql": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "@fhuang/mcp-mysql-server"],
|
||||||
|
"env": {
|
||||||
|
"MYSQL_HOST": "116.62.17.81",
|
||||||
|
"MYSQL_PORT": "3306",
|
||||||
|
"MYSQL_USER": "root",
|
||||||
|
"MYSQL_PASSWORD": "Kfcx@1234",
|
||||||
|
"MYSQL_DATABASE": "ccdi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用场景:**
|
||||||
|
- 通过 MCP 工具直接查询和操作数据库
|
||||||
|
- 在开发过程中快速验证数据
|
||||||
|
- 生成测试数据和调试 SQL
|
||||||
|
|
||||||
|
### Druid 监控台
|
||||||
|
|
||||||
|
访问地址: `http://localhost:8080/druid/`
|
||||||
|
- 用户名: `ruoyi`
|
||||||
|
- 密码: `123456`
|
||||||
|
|
||||||
|
用于监控 SQL 执行情况、连接池状态等。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 重要文件路径
|
||||||
|
|
||||||
|
| 用途 | 路径 |
|
||||||
|
|---------------|--------------------------------------------------------------------------------|
|
||||||
|
| 应用入口 | `ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java` |
|
||||||
|
| 安全配置 | `ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java` |
|
||||||
|
| 信息采集 Controller | `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/` |
|
||||||
|
| 信息采集 Mapper XML | `ccdi-info-collection/src/main/resources/mapper/info/collection/` |
|
||||||
|
| 项目管理 Controller | `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/` |
|
||||||
|
| 项目管理 Mapper XML | `ccdi-project/src/main/resources/mapper/ccdi/project/` |
|
||||||
|
| 流水分析 Client | `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` |
|
||||||
|
| Vue 路由 | `ruoyi-ui/src/router/index.js` |
|
||||||
|
| Vuex Store | `ruoyi-ui/src/store/` |
|
||||||
|
| 前端 API | `ruoyi-ui/src/api/` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据库规范
|
||||||
|
|
||||||
|
- **新建表名**: 需要加上项目英文名首字母集合前缀 `ccdi_` (如 `ccdi_base_staff`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文档管理
|
||||||
|
|
||||||
|
- **文档语言**: 使用简体中文编写 .md 文档
|
||||||
|
- **文档目录**: 所有生成的文档放在 `doc/` 目录下,按类型分类
|
||||||
|
- **需求分析**: 在 `doc/` 目录下新建文件夹,以需求内容命名
|
||||||
|
|
||||||
|
### doc 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
doc/
|
||||||
|
├── api-docs/ # API 文档
|
||||||
|
├── database/ # 数据库相关
|
||||||
|
├── design/ # 设计文档
|
||||||
|
├── implementation/ # 实施文档
|
||||||
|
├── requirements/ # 需求文档
|
||||||
|
└── test-scripts/ # 测试脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OpenSpec 工作流
|
||||||
|
|
||||||
|
项目使用 OpenSpec 进行规范驱动开发,参考 `openspec/AGENTS.md`。
|
||||||
|
|
||||||
|
### 何时创建 Proposal
|
||||||
|
|
||||||
|
**需要创建:**
|
||||||
|
|
||||||
|
- 新功能或能力
|
||||||
|
- 破坏性变更 (API, 数据库结构)
|
||||||
|
- 架构变更
|
||||||
|
- 改变行为的性能优化
|
||||||
|
|
||||||
|
**无需创建:**
|
||||||
|
|
||||||
|
- Bug 修复 (恢复预期行为)
|
||||||
|
- 拼写错误、格式、注释
|
||||||
|
- 非破坏性依赖更新
|
||||||
|
- 配置变更
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 沟通规范
|
||||||
|
|
||||||
|
- 永远使用简体中文进行思考和对话
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常见问题排查
|
||||||
|
|
||||||
|
### 数据库连接失败
|
||||||
|
|
||||||
|
**检查项:**
|
||||||
|
1. 确认 MySQL 服务已启动
|
||||||
|
2. 检查 `application-dev.yml` 中的数据库连接配置
|
||||||
|
3. 确认数据库用户名和密码正确
|
||||||
|
4. 检查数据库是否已创建(数据库名: `ccdi`)
|
||||||
|
|
||||||
|
### Redis 连接失败
|
||||||
|
|
||||||
|
**检查项:**
|
||||||
|
1. 确认 Redis 服务已启动
|
||||||
|
2. 检查 `application-dev.yml` 中的 Redis 配置
|
||||||
|
3. 如果 Redis 不需要密码,将 `password` 配置注释掉
|
||||||
|
|
||||||
|
### 前端无法访问后端接口
|
||||||
|
|
||||||
|
**检查项:**
|
||||||
|
1. 确认后端已启动(端口 8080)
|
||||||
|
2. 检查前端代理配置(`ruoyi-ui/vue.config.js`)
|
||||||
|
3. 确认后端接口路径正确(查看 Controller 的 `@RequestMapping`)
|
||||||
|
|
||||||
|
### 导入功能无响应
|
||||||
|
|
||||||
|
**检查项:**
|
||||||
|
1. 检查文件大小是否超过限制(默认 10MB)
|
||||||
|
2. 查看后端日志是否有异常
|
||||||
|
3. 确认 Excel 模板格式正确
|
||||||
|
4. 检查必填字段是否为空
|
||||||
|
|
||||||
|
### 流水分析平台连接失败
|
||||||
|
|
||||||
|
**检查项:**
|
||||||
|
1. 确认 `lsfx-mock-server` 已启动(开发环境)
|
||||||
|
2. 检查 `application-dev.yml` 中的 `lsfx.api.base-url` 配置
|
||||||
|
3. 验证 app-id、app-secret、client-id 是否正确
|
||||||
|
4. 检查网络连接和防火墙设置
|
||||||
|
5. 查看后端日志中的 HTTP 请求错误信息
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MyBatis Plus 分页使用
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Controller 层
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo list(QueryDTO queryDTO) {
|
||||||
|
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||||
|
Page<VO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
||||||
|
Page<VO> result = service.selectPage(page, queryDTO);
|
||||||
|
return getDataTable(result.getRecords(), result.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service 层
|
||||||
|
Page<VO> selectPage(Page<VO> page, QueryDTO queryDTO);
|
||||||
|
|
||||||
|
// Mapper 层 (使用 XML)
|
||||||
|
<select id="selectPage" resultType="VO">
|
||||||
|
SELECT * FROM table_name
|
||||||
|
<where>
|
||||||
|
<if test="queryDTO.name != null">
|
||||||
|
AND name LIKE CONCAT('%', #{queryDTO.name}, '%')
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
```
|
||||||
92
build_release_ccdi.sh
Executable file
92
build_release_ccdi.sh
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
|
||||||
|
DATE_STAMP=$(date "+%Y%m%d")
|
||||||
|
RELEASE_ZIP="$ROOT_DIR/ccdi_${DATE_STAMP}.zip"
|
||||||
|
STAGE_DIR="$ROOT_DIR/.deploy/ccdi-release-package"
|
||||||
|
WORK_DIR="$STAGE_DIR/files"
|
||||||
|
BACKEND_JAR_SOURCE="$ROOT_DIR/ruoyi-admin/target/ruoyi-admin.jar"
|
||||||
|
FRONTEND_DIR="$ROOT_DIR/ruoyi-ui"
|
||||||
|
FRONTEND_DIST_DIR="$FRONTEND_DIR/dist"
|
||||||
|
FRONTEND_DIST_ZIP="$WORK_DIR/dist.zip"
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_command() {
|
||||||
|
if ! command -v "$1" >/dev/null 2>&1; then
|
||||||
|
log_error "缺少命令: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_stage_dir() {
|
||||||
|
rm -rf "$STAGE_DIR"
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_backend() {
|
||||||
|
log_info "开始构建后端生产 jar"
|
||||||
|
(
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
mvn -pl ruoyi-admin -am clean package -DskipTests
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ ! -f "$BACKEND_JAR_SOURCE" ]; then
|
||||||
|
log_error "未生成后端 jar: $BACKEND_JAR_SOURCE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_frontend() {
|
||||||
|
log_info "开始构建前端生产 dist"
|
||||||
|
FRONTEND_DIR="$FRONTEND_DIR" zsh -lic 'cd "$FRONTEND_DIR" && nvm use >/dev/null && npm run build:prod'
|
||||||
|
|
||||||
|
if [ ! -f "$FRONTEND_DIST_DIR/index.html" ]; then
|
||||||
|
log_error "前端生产构建失败,未找到: $FRONTEND_DIST_DIR/index.html"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$FRONTEND_DIR"
|
||||||
|
zip -qr "$FRONTEND_DIST_ZIP" dist
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ ! -f "$FRONTEND_DIST_ZIP" ]; then
|
||||||
|
log_error "未生成前端压缩包: $FRONTEND_DIST_ZIP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
package_release() {
|
||||||
|
cp "$BACKEND_JAR_SOURCE" "$WORK_DIR/ruoyi-admin.jar"
|
||||||
|
|
||||||
|
rm -f "$RELEASE_ZIP"
|
||||||
|
(
|
||||||
|
cd "$WORK_DIR"
|
||||||
|
zip -qr "$RELEASE_ZIP" ruoyi-admin.jar dist.zip
|
||||||
|
)
|
||||||
|
|
||||||
|
log_info "上线压缩包已生成: $RELEASE_ZIP"
|
||||||
|
log_info "压缩包根层内容: ruoyi-admin.jar, dist.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_command mvn
|
||||||
|
require_command zsh
|
||||||
|
require_command zip
|
||||||
|
|
||||||
|
reset_stage_dir
|
||||||
|
build_backend
|
||||||
|
build_frontend
|
||||||
|
package_release
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -14,10 +14,8 @@ import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
|
|||||||
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
||||||
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
||||||
import com.ruoyi.info.collection.service.ICcdiCreditInfoService;
|
import com.ruoyi.info.collection.service.ICcdiCreditInfoService;
|
||||||
import com.ruoyi.info.collection.service.support.CreditHtmlStorageService;
|
|
||||||
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
||||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -25,6 +23,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -36,16 +36,9 @@ import java.util.Map;
|
|||||||
@Service
|
@Service
|
||||||
public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
||||||
|
|
||||||
private static final int CREDIT_PARSE_SUCCESS_CODE = 10000;
|
|
||||||
private static final int CREDIT_PARSE_SUCCESS_STATUS = 1;
|
|
||||||
private static final int CREDIT_PARSE_SUCCESS_REASON_CODE = 200;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CreditParseClient creditParseClient;
|
private CreditParseClient creditParseClient;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CreditHtmlStorageService creditHtmlStorageService;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CreditInfoPayloadAssembler assembler;
|
private CreditInfoPayloadAssembler assembler;
|
||||||
|
|
||||||
@@ -148,9 +141,10 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSingleFile(MultipartFile multipartFile, String userName) throws Exception {
|
private void handleSingleFile(MultipartFile multipartFile, String userName) throws IOException {
|
||||||
CreditHtmlStorageService.StoredCreditHtml storedHtml = creditHtmlStorageService.save(multipartFile);
|
File tempFile = createTempFile(multipartFile);
|
||||||
CreditParseInvokeResponse response = creditParseClient.parse(storedHtml.remotePath());
|
try {
|
||||||
|
CreditParseResponse response = creditParseClient.parse("LXCUSTALL", "PERSON", tempFile);
|
||||||
CreditParsePayload payload = requireResponse(response).getPayload();
|
CreditParsePayload payload = requireResponse(response).getPayload();
|
||||||
Map<String, Object> header = requireHeader(payload);
|
Map<String, Object> header = requireHeader(payload);
|
||||||
String personId = stringValue(header.get("query_cert_no"));
|
String personId = stringValue(header.get("query_cert_no"));
|
||||||
@@ -162,6 +156,22 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
|||||||
List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
|
List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
|
||||||
CcdiCreditNegativeInfo negative = assembler.buildNegative(personId, personName, queryDate, payload);
|
CcdiCreditNegativeInfo negative = assembler.buildNegative(personId, personName, queryDate, payload);
|
||||||
replaceEmployeeCredit(personId, debts, negative, userName);
|
replaceEmployeeCredit(personId, debts, negative, userName);
|
||||||
|
} finally {
|
||||||
|
if (tempFile.exists()) {
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createTempFile(MultipartFile multipartFile) throws IOException {
|
||||||
|
String originalFilename = multipartFile.getOriginalFilename();
|
||||||
|
String suffix = ".html";
|
||||||
|
if (originalFilename != null && originalFilename.contains(".")) {
|
||||||
|
suffix = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
File tempFile = File.createTempFile("credit-info-", suffix);
|
||||||
|
multipartFile.transferTo(tempFile);
|
||||||
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateHtmlFile(MultipartFile file) {
|
private void validateHtmlFile(MultipartFile file) {
|
||||||
@@ -175,41 +185,14 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CreditParseResponse requireResponse(CreditParseInvokeResponse response) {
|
private CreditParseResponse requireResponse(CreditParseResponse response) {
|
||||||
if (response == null || response.getData() == null || response.getData().getMappingOutputFields() == null) {
|
if (response == null || response.getPayload() == null) {
|
||||||
throw new RuntimeException("征信解析结果为空");
|
throw new RuntimeException("征信解析结果为空");
|
||||||
}
|
}
|
||||||
CreditParseResponse mappingOutputFields = response.getData().getMappingOutputFields();
|
if (!"0".equals(response.getStatusCode())) {
|
||||||
if (!Boolean.TRUE.equals(response.getSuccess())) {
|
throw new RuntimeException(stringValue(response.getMessage(), "征信解析失败"));
|
||||||
throw new RuntimeException(stringValue(mappingOutputFields.getMessage(), "征信解析平台调用失败"));
|
|
||||||
}
|
}
|
||||||
if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_CODE).equals(response.getCode())) {
|
return response;
|
||||||
throw new RuntimeException("征信解析平台状态码异常: " + response.getCode());
|
|
||||||
}
|
|
||||||
if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_STATUS).equals(response.getData().getStatus())) {
|
|
||||||
throw new RuntimeException(parseErrorMessage(response, "征信解析状态异常: " + response.getData().getStatus()));
|
|
||||||
}
|
|
||||||
if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_REASON_CODE).equals(response.getData().getReasonCode())) {
|
|
||||||
throw new RuntimeException(parseErrorMessage(response, "征信解析原因码异常: " + response.getData().getReasonCode()));
|
|
||||||
}
|
|
||||||
if (mappingOutputFields.getPayload() == null) {
|
|
||||||
throw new RuntimeException("征信解析结果为空");
|
|
||||||
}
|
|
||||||
return mappingOutputFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseErrorMessage(CreditParseInvokeResponse response, String defaultValue) {
|
|
||||||
if (response == null || response.getData() == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
String reasonMessage = stringValue(response.getData().getReasonMessage());
|
|
||||||
if (!isBlank(reasonMessage)) {
|
|
||||||
return reasonMessage;
|
|
||||||
}
|
|
||||||
if (response.getData().getMappingOutputFields() != null) {
|
|
||||||
return stringValue(response.getData().getMappingOutputFields().getMessage(), defaultValue);
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> requireHeader(CreditParsePayload payload) {
|
private Map<String, Object> requireHeader(CreditParsePayload payload) {
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.ruoyi.info.collection.service.support;
|
|
||||||
|
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 征信 HTML 服务器落盘与远程访问地址生成。
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class CreditHtmlStorageService {
|
|
||||||
|
|
||||||
private static final String CREDIT_HTML_DIR = "credit-html";
|
|
||||||
private static final String[] HTML_EXTENSIONS = {"html", "htm"};
|
|
||||||
|
|
||||||
@Value("${credit-parse.api.file-public-base-url}")
|
|
||||||
private String filePublicBaseUrl;
|
|
||||||
|
|
||||||
public StoredCreditHtml save(MultipartFile file) throws Exception {
|
|
||||||
String profilePath = FileUploadUtils.upload(getCreditHtmlBaseDir(), file, HTML_EXTENSIONS);
|
|
||||||
return new StoredCreditHtml(profilePath, buildRemotePath(profilePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCreditHtmlBaseDir() {
|
|
||||||
return RuoYiConfig.getProfile() + File.separator + CREDIT_HTML_DIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildRemotePath(String profilePath) {
|
|
||||||
if (StringUtils.isBlank(filePublicBaseUrl)) {
|
|
||||||
throw new IllegalStateException("征信HTML公开访问地址未配置");
|
|
||||||
}
|
|
||||||
String normalizedBaseUrl = StringUtils.stripEnd(filePublicBaseUrl.trim(), "/");
|
|
||||||
String normalizedProfilePath = profilePath.startsWith("/") ? profilePath : "/" + profilePath;
|
|
||||||
return normalizedBaseUrl + normalizedProfilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public record StoredCreditHtml(String profilePath, String remotePath) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,8 +19,6 @@ import java.util.Objects;
|
|||||||
@Component
|
@Component
|
||||||
public class CreditInfoPayloadAssembler {
|
public class CreditInfoPayloadAssembler {
|
||||||
|
|
||||||
private static final BigDecimal MISSING_SENTINEL = new BigDecimal("-9999");
|
|
||||||
|
|
||||||
private static final List<DebtMapping> DEBT_MAPPINGS = List.of(
|
private static final List<DebtMapping> DEBT_MAPPINGS = List.of(
|
||||||
new DebtMapping("uncle_bank_house", "银行", "住房贷款", "银行", "未结清银行住房贷款"),
|
new DebtMapping("uncle_bank_house", "银行", "住房贷款", "银行", "未结清银行住房贷款"),
|
||||||
new DebtMapping("uncle_bank_car", "银行", "汽车贷款", "银行", "未结清银行汽车贷款"),
|
new DebtMapping("uncle_bank_car", "银行", "汽车贷款", "银行", "未结清银行汽车贷款"),
|
||||||
@@ -28,7 +26,7 @@ public class CreditInfoPayloadAssembler {
|
|||||||
new DebtMapping("uncle_bank_consume", "银行", "消费贷款", "银行", "未结清银行消费贷款"),
|
new DebtMapping("uncle_bank_consume", "银行", "消费贷款", "银行", "未结清银行消费贷款"),
|
||||||
new DebtMapping("uncle_bank_other", "银行", "其他贷款", "银行", "未结清银行其他贷款"),
|
new DebtMapping("uncle_bank_other", "银行", "其他贷款", "银行", "未结清银行其他贷款"),
|
||||||
new DebtMapping("uncle_not_bank", "非银", "非银行贷款", "非银", "未结清非银行贷款"),
|
new DebtMapping("uncle_not_bank", "非银", "非银行贷款", "非银", "未结清非银行贷款"),
|
||||||
new DebtMapping("uncle_credit_card", "银行", "信用卡", "银行", "未结清信用卡")
|
new DebtMapping("uncle_credit_cart", "银行", "信用卡", "银行", "未结清信用卡")
|
||||||
);
|
);
|
||||||
|
|
||||||
public List<CcdiDebtsInfo> buildDebts(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) {
|
public List<CcdiDebtsInfo> buildDebts(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) {
|
||||||
@@ -63,13 +61,9 @@ public class CreditInfoPayloadAssembler {
|
|||||||
|
|
||||||
private CcdiDebtsInfo buildDebtRow(String personId, String personName, LocalDate queryDate,
|
private CcdiDebtsInfo buildDebtRow(String personId, String personName, LocalDate queryDate,
|
||||||
Map<String, Object> source, DebtMapping mapping) {
|
Map<String, Object> source, DebtMapping mapping) {
|
||||||
Object stateValue = source.get(mapping.prefix() + "_state");
|
|
||||||
if (isMissingSentinel(stateValue)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
BigDecimal principalBalance = toBigDecimal(source.get(mapping.prefix() + "_bal"));
|
BigDecimal principalBalance = toBigDecimal(source.get(mapping.prefix() + "_bal"));
|
||||||
BigDecimal debtTotalAmount = toBigDecimal(source.get(mapping.prefix() + "_lmt"));
|
BigDecimal debtTotalAmount = toBigDecimal(source.get(mapping.prefix() + "_lmt"));
|
||||||
String debtStatus = toStringValue(stateValue);
|
String debtStatus = toStringValue(source.get(mapping.prefix() + "_state"));
|
||||||
if (isEmptyMetrics(principalBalance, debtTotalAmount, debtStatus)) {
|
if (isEmptyMetrics(principalBalance, debtTotalAmount, debtStatus)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -103,9 +97,6 @@ public class CreditInfoPayloadAssembler {
|
|||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (isMissingSentinel(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (value instanceof BigDecimal decimal) {
|
if (value instanceof BigDecimal decimal) {
|
||||||
return decimal;
|
return decimal;
|
||||||
}
|
}
|
||||||
@@ -120,28 +111,10 @@ public class CreditInfoPayloadAssembler {
|
|||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (isMissingSentinel(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String text = Objects.toString(value, "").trim();
|
String text = Objects.toString(value, "").trim();
|
||||||
return text.isEmpty() ? null : text;
|
return text.isEmpty() ? null : text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMissingSentinel(Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String text = Objects.toString(value, "").trim();
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new BigDecimal(text).compareTo(MISSING_SENTINEL) == 0;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBlank(String value) {
|
private boolean isBlank(String value) {
|
||||||
return value == null || value.trim().isEmpty();
|
return value == null || value.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,6 @@
|
|||||||
|
|
||||||
<sql id="AccountInfoWhereClause">
|
<sql id="AccountInfoWhereClause">
|
||||||
WHERE 1 = 1
|
WHERE 1 = 1
|
||||||
AND ai.owner_type <> 'CREDIT_CUSTOMER'
|
|
||||||
<if test="query.staffName != null and query.staffName != ''">
|
<if test="query.staffName != null and query.staffName != ''">
|
||||||
AND (
|
AND (
|
||||||
(ai.owner_type = 'EMPLOYEE' AND bs.name LIKE CONCAT('%', #{query.staffName}, '%'))
|
(ai.owner_type = 'EMPLOYEE' AND bs.name LIKE CONCAT('%', #{query.staffName}, '%'))
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ class CcdiAccountInfoMapperTest {
|
|||||||
assertTrue(sql.contains("ai.is_self_account as isactualcontrol"), sql);
|
assertTrue(sql.contains("ai.is_self_account as isactualcontrol"), sql);
|
||||||
assertTrue(sql.contains("ai.monthly_avg_trans_count as avgmonthtxncount"), sql);
|
assertTrue(sql.contains("ai.monthly_avg_trans_count as avgmonthtxncount"), sql);
|
||||||
assertTrue(sql.contains("ai.trans_risk_level as txnrisklevel"), sql);
|
assertTrue(sql.contains("ai.trans_risk_level as txnrisklevel"), sql);
|
||||||
assertTrue(sql.contains("ai.owner_type <> 'credit_customer'"), sql);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MappedStatement loadMappedStatement(String statementId) throws Exception {
|
private MappedStatement loadMappedStatement(String statementId) throws Exception {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.ruoyi.info.collection.service;
|
package com.ruoyi.info.collection.service;
|
||||||
|
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
|
||||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||||
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
|
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
|
||||||
@@ -8,33 +7,26 @@ import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
|
|||||||
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
||||||
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
||||||
import com.ruoyi.info.collection.service.impl.CcdiCreditInfoServiceImpl;
|
import com.ruoyi.info.collection.service.impl.CcdiCreditInfoServiceImpl;
|
||||||
import com.ruoyi.info.collection.service.support.CreditHtmlStorageService;
|
|
||||||
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
||||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeData;
|
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -49,9 +41,6 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private CreditParseClient creditParseClient;
|
private CreditParseClient creditParseClient;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private CreditHtmlStorageService creditHtmlStorageService;
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private CreditInfoPayloadAssembler assembler;
|
private CreditInfoPayloadAssembler assembler;
|
||||||
|
|
||||||
@@ -65,15 +54,11 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
private CcdiCreditInfoQueryMapper queryMapper;
|
private CcdiCreditInfoQueryMapper queryMapper;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() throws Exception {
|
void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() {
|
||||||
MockMultipartFile file = new MockMultipartFile(
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
"files", "family.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
|
"files", "family.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
when(creditHtmlStorageService.save(any()))
|
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
|
||||||
.thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
|
|
||||||
"/profile/credit-html/2026/05/12/family_1.html",
|
|
||||||
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html"));
|
|
||||||
when(creditParseClient.parse(anyString()))
|
|
||||||
.thenReturn(successResponse("330101199202020022", "李四", "2026-03-24"));
|
.thenReturn(successResponse("330101199202020022", "李四", "2026-03-24"));
|
||||||
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||||
.thenReturn(List.of(buildDebt("330101199202020022")));
|
.thenReturn(List.of(buildDebt("330101199202020022")));
|
||||||
@@ -84,20 +69,15 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
|
|
||||||
assertEquals(1, result.getSuccessCount());
|
assertEquals(1, result.getSuccessCount());
|
||||||
assertEquals(0, result.getFailureCount());
|
assertEquals(0, result.getFailureCount());
|
||||||
verify(creditParseClient).parse("http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html");
|
|
||||||
verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
|
verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
|
||||||
verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
|
verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void uploadHtmlFiles_shouldRejectOlderReportDate() throws Exception {
|
void uploadHtmlFiles_shouldRejectOlderReportDate() {
|
||||||
MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
when(creditHtmlStorageService.save(any()))
|
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
|
||||||
.thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
|
|
||||||
"/profile/credit-html/2026/05/12/a_1.html",
|
|
||||||
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
|
|
||||||
when(creditParseClient.parse(anyString()))
|
|
||||||
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
||||||
when(queryMapper.selectLatestQueryDate("330101199001010011"))
|
when(queryMapper.selectLatestQueryDate("330101199001010011"))
|
||||||
.thenReturn(LocalDate.parse("2026-03-05"));
|
.thenReturn(LocalDate.parse("2026-03-05"));
|
||||||
@@ -108,68 +88,7 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
|
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private CreditParseResponse successResponse(String personId, String personName, String reportTime) {
|
||||||
void uploadHtmlFiles_shouldRejectInvalidPlatformCode() throws Exception {
|
|
||||||
MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
when(creditHtmlStorageService.save(any()))
|
|
||||||
.thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
|
|
||||||
"/profile/credit-html/2026/05/12/a_1.html",
|
|
||||||
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
|
|
||||||
CreditParseInvokeResponse response = successResponse("330101199001010011", "张三", "2026-03-03");
|
|
||||||
response.setCode(99999);
|
|
||||||
when(creditParseClient.parse(anyString())).thenReturn(response);
|
|
||||||
|
|
||||||
CreditInfoUploadResultVO result = service.upload(List.of(file));
|
|
||||||
|
|
||||||
assertEquals(0, result.getSuccessCount());
|
|
||||||
assertEquals("征信解析平台状态码异常: 99999", result.getFailures().get(0).getReason());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void uploadHtmlFiles_shouldRejectInvalidResultStatus() throws Exception {
|
|
||||||
MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
when(creditHtmlStorageService.save(any()))
|
|
||||||
.thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
|
|
||||||
"/profile/credit-html/2026/05/12/a_1.html",
|
|
||||||
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
|
|
||||||
CreditParseInvokeResponse response = successResponse("330101199001010011", "张三", "2026-03-03");
|
|
||||||
response.getData().setStatus(0);
|
|
||||||
response.getData().setReasonCode(500);
|
|
||||||
response.getData().setReasonMessage("结果解析失败");
|
|
||||||
when(creditParseClient.parse(anyString())).thenReturn(response);
|
|
||||||
|
|
||||||
CreditInfoUploadResultVO result = service.upload(List.of(file));
|
|
||||||
|
|
||||||
assertEquals(0, result.getSuccessCount());
|
|
||||||
assertEquals("结果解析失败", result.getFailures().get(0).getReason());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void creditHtmlStorage_shouldStoreHtmlUnderProfileAndBuildRemotePath(@TempDir Path profileDir) throws Exception {
|
|
||||||
String oldProfile = RuoYiConfig.getProfile();
|
|
||||||
new RuoYiConfig().setProfile(profileDir.toString());
|
|
||||||
try {
|
|
||||||
CreditHtmlStorageService storageService = new CreditHtmlStorageService();
|
|
||||||
ReflectionTestUtils.setField(storageService, "filePublicBaseUrl", "http://127.0.0.1:62318/");
|
|
||||||
MockMultipartFile file = new MockMultipartFile(
|
|
||||||
"files", "credit.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
CreditHtmlStorageService.StoredCreditHtml storedHtml = storageService.save(file);
|
|
||||||
|
|
||||||
assertTrue(storedHtml.profilePath().startsWith("/profile/credit-html/"));
|
|
||||||
assertTrue(storedHtml.profilePath().endsWith(".html"));
|
|
||||||
assertEquals("http://127.0.0.1:62318" + storedHtml.profilePath(), storedHtml.remotePath());
|
|
||||||
Path savedFile = profileDir.resolve(storedHtml.profilePath().substring("/profile/".length()));
|
|
||||||
assertTrue(Files.exists(savedFile));
|
|
||||||
} finally {
|
|
||||||
new RuoYiConfig().setProfile(oldProfile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreditParseInvokeResponse successResponse(String personId, String personName, String reportTime) {
|
|
||||||
CreditParsePayload payload = new CreditParsePayload();
|
CreditParsePayload payload = new CreditParsePayload();
|
||||||
Map<String, Object> header = new HashMap<>();
|
Map<String, Object> header = new HashMap<>();
|
||||||
header.put("query_cert_no", personId);
|
header.put("query_cert_no", personId);
|
||||||
@@ -180,20 +99,9 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
payload.setLxPublictype(Map.of("civil_cnt", 1));
|
payload.setLxPublictype(Map.of("civil_cnt", 1));
|
||||||
|
|
||||||
CreditParseResponse response = new CreditParseResponse();
|
CreditParseResponse response = new CreditParseResponse();
|
||||||
response.setMessage("成功");
|
response.setStatusCode("0");
|
||||||
response.setStatusCode("ERR_SHOULD_IGNORE");
|
|
||||||
response.setPayload(payload);
|
response.setPayload(payload);
|
||||||
|
return response;
|
||||||
CreditParseInvokeData data = new CreditParseInvokeData();
|
|
||||||
data.setMappingOutputFields(response);
|
|
||||||
data.setStatus(1);
|
|
||||||
data.setReasonCode(200);
|
|
||||||
|
|
||||||
CreditParseInvokeResponse invokeResponse = new CreditParseInvokeResponse();
|
|
||||||
invokeResponse.setSuccess(true);
|
|
||||||
invokeResponse.setCode(10000);
|
|
||||||
invokeResponse.setData(data);
|
|
||||||
return invokeResponse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CcdiDebtsInfo buildDebt(String personId) {
|
private CcdiDebtsInfo buildDebt(String personId) {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class CreditInfoPayloadAssemblerTest {
|
class CreditInfoPayloadAssemblerTest {
|
||||||
@@ -29,18 +28,13 @@ class CreditInfoPayloadAssemblerTest {
|
|||||||
debt.put("uncle_not_bank_bal", "2000");
|
debt.put("uncle_not_bank_bal", "2000");
|
||||||
debt.put("uncle_not_bank_lmt", "3000");
|
debt.put("uncle_not_bank_lmt", "3000");
|
||||||
debt.put("uncle_not_bank_state", "逾期");
|
debt.put("uncle_not_bank_state", "逾期");
|
||||||
debt.put("uncle_credit_card_bal", "100");
|
|
||||||
debt.put("uncle_credit_card_lmt", "500");
|
|
||||||
debt.put("uncle_credit_card_state", "正常");
|
|
||||||
payload.setLxDebt(debt);
|
payload.setLxDebt(debt);
|
||||||
|
|
||||||
List<CcdiDebtsInfo> rows = assembler.buildDebts("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
|
List<CcdiDebtsInfo> rows = assembler.buildDebts("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
|
||||||
|
|
||||||
assertEquals(3, rows.size());
|
assertEquals(2, rows.size());
|
||||||
assertEquals("住房贷款", rows.get(0).getDebtSubType());
|
assertEquals("住房贷款", rows.get(0).getDebtSubType());
|
||||||
assertEquals("非银", rows.get(1).getCreditorType());
|
assertEquals("非银", rows.get(1).getCreditorType());
|
||||||
assertEquals("信用卡", rows.get(2).getDebtSubType());
|
|
||||||
assertEquals(new BigDecimal("500"), rows.get(2).getDebtTotalAmount());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -57,42 +51,6 @@ class CreditInfoPayloadAssemblerTest {
|
|||||||
assertEquals(new BigDecimal("9800"), info.getCivilLmt());
|
assertEquals(new BigDecimal("9800"), info.getCivilLmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldSkipDebtTypeWhenStateIsMissingSentinel() {
|
|
||||||
CreditParsePayload payload = new CreditParsePayload();
|
|
||||||
Map<String, Object> debt = new HashMap<>();
|
|
||||||
debt.put("uncle_bank_house_bal", "50000");
|
|
||||||
debt.put("uncle_bank_house_lmt", "100000");
|
|
||||||
debt.put("uncle_bank_house_state", "-9999");
|
|
||||||
debt.put("uncle_not_bank_bal", "2000");
|
|
||||||
debt.put("uncle_not_bank_lmt", "3000");
|
|
||||||
debt.put("uncle_not_bank_state", "正常");
|
|
||||||
payload.setLxDebt(debt);
|
|
||||||
|
|
||||||
List<CcdiDebtsInfo> rows = assembler.buildDebts("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
|
|
||||||
|
|
||||||
assertEquals(1, rows.size());
|
|
||||||
assertEquals("非银行贷款", rows.get(0).getDebtSubType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldTreatNegativeRiskMissingSentinelAsEmptyValue() {
|
|
||||||
CreditParsePayload payload = new CreditParsePayload();
|
|
||||||
Map<String, Object> publictype = new HashMap<>();
|
|
||||||
publictype.put("civil_cnt", "-9999");
|
|
||||||
publictype.put("civil_lmt", "-9999.0");
|
|
||||||
publictype.put("enforce_cnt", 1);
|
|
||||||
publictype.put("enforce_lmt", "1200");
|
|
||||||
payload.setLxPublictype(publictype);
|
|
||||||
|
|
||||||
CcdiCreditNegativeInfo info = assembler.buildNegative("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
|
|
||||||
|
|
||||||
assertEquals(0, info.getCivilCnt());
|
|
||||||
assertNull(info.getCivilLmt());
|
|
||||||
assertEquals(1, info.getEnforceCnt());
|
|
||||||
assertEquals(new BigDecimal("1200"), info.getEnforceLmt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldSkipDebtRowWhenAllMetricsAreEmpty() {
|
void shouldSkipDebtRowWhenAllMetricsAreEmpty() {
|
||||||
CreditParsePayload payload = new CreditParsePayload();
|
CreditParsePayload payload = new CreditParsePayload();
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.ruoyi.lsfx.client;
|
package com.ruoyi.lsfx.client;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
|
||||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
|
||||||
import com.ruoyi.lsfx.exception.LsfxApiException;
|
import com.ruoyi.lsfx.exception.LsfxApiException;
|
||||||
import com.ruoyi.lsfx.util.HttpUtil;
|
import com.ruoyi.lsfx.util.HttpUtil;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -11,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -18,191 +16,32 @@ import java.util.Map;
|
|||||||
@Component
|
@Component
|
||||||
public class CreditParseClient {
|
public class CreditParseClient {
|
||||||
|
|
||||||
private static final int PLATFORM_SUCCESS_CODE = 10000;
|
|
||||||
private static final int INITIATE_SUCCESS_STATUS = 1;
|
|
||||||
private static final int INITIATE_SUCCESS_REASON_CODE = 200;
|
|
||||||
private static final int RESULT_QUERY_MAX_ATTEMPTS = 5;
|
|
||||||
private static final long RESULT_QUERY_INTERVAL_MILLIS = 2000L;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private HttpUtil httpUtil;
|
private HttpUtil httpUtil;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Value("${credit-parse.api.url}")
|
@Value("${credit-parse.api.url}")
|
||||||
private String creditParseUrl;
|
private String creditParseUrl;
|
||||||
|
|
||||||
@Value("${credit-parse.api.result-url}")
|
public CreditParseResponse parse(String model, String hType, File file) {
|
||||||
private String creditParseResultUrl;
|
|
||||||
|
|
||||||
@Value("${credit-parse.api.org-code:999000}")
|
|
||||||
private String orgCode;
|
|
||||||
|
|
||||||
@Value("${credit-parse.api.run-type:1}")
|
|
||||||
private String runType;
|
|
||||||
|
|
||||||
@Value("${credit-parse.api.model:LXCUSTALL}")
|
|
||||||
private String defaultModel;
|
|
||||||
|
|
||||||
public CreditParseInvokeResponse parse(String remotePath) {
|
|
||||||
return parse(defaultModel, remotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreditParseInvokeResponse parse(String model, String remotePath) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
String actualModel = StringUtils.isBlank(model) ? defaultModel : model;
|
log.info("【征信解析】开始调用: fileName={}, model={}, hType={}", file.getName(), model, hType);
|
||||||
String serialNum = buildSerialNum();
|
|
||||||
try {
|
|
||||||
Map<String, Object> initiateParams = buildInitiateParams(serialNum, actualModel, remotePath);
|
|
||||||
CreditParseInvokeResponse initiateResponse = request(creditParseUrl, initiateParams, "发起接口");
|
|
||||||
requireSuccessfulInitiateResponse(initiateResponse, "征信解析发起接口");
|
|
||||||
|
|
||||||
CreditParseInvokeResponse response = queryResult(serialNum);
|
try {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("model", model);
|
||||||
|
params.put("hType", hType);
|
||||||
|
params.put("file", file);
|
||||||
|
|
||||||
|
CreditParseResponse response = httpUtil.uploadFile(creditParseUrl, params, null, CreditParseResponse.class);
|
||||||
|
|
||||||
long elapsed = System.currentTimeMillis() - startTime;
|
long elapsed = System.currentTimeMillis() - startTime;
|
||||||
log.info("【征信解析】调用完成: success={}, code={}, businessStatusCode={}, cost={}ms",
|
log.info("【征信解析】调用完成: statusCode={}, cost={}ms",
|
||||||
response != null ? response.getSuccess() : null,
|
response != null ? response.getStatusCode() : null, elapsed);
|
||||||
response != null ? response.getCode() : null,
|
|
||||||
response != null && response.getData() != null && response.getData().getMappingOutputFields() != null
|
|
||||||
? response.getData().getMappingOutputFields().getStatusCode() : null,
|
|
||||||
elapsed);
|
|
||||||
return response;
|
return response;
|
||||||
} catch (LsfxApiException e) {
|
|
||||||
log.error("【征信解析】调用失败: serialNum={}, model={}, remotePath={}, error={}",
|
|
||||||
serialNum, actualModel, remotePath, e.getMessage(), e);
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("【征信解析】调用失败: serialNum={}, model={}, remotePath={}, error={}",
|
log.error("【征信解析】调用失败: fileName={}, model={}, hType={}, error={}",
|
||||||
serialNum, actualModel, remotePath, e.getMessage(), e);
|
file.getName(), model, hType, e.getMessage(), e);
|
||||||
throw new LsfxApiException("征信解析调用失败: " + e.getMessage(), e);
|
throw new LsfxApiException("征信解析调用失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildInitiateParams(String serialNum, String model, String remotePath) {
|
|
||||||
Map<String, Object> params = buildBaseParams(serialNum);
|
|
||||||
params.put("remotePath", remotePath);
|
|
||||||
params.put("model", model);
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> buildBaseParams(String serialNum) {
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
|
||||||
params.put("serialNum", serialNum);
|
|
||||||
params.put("orgCode", orgCode);
|
|
||||||
params.put("runType", runType);
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreditParseInvokeResponse queryResult(String serialNum) {
|
|
||||||
Map<String, Object> params = buildBaseParams(serialNum);
|
|
||||||
for (int attempt = 1; attempt <= RESULT_QUERY_MAX_ATTEMPTS; attempt++) {
|
|
||||||
CreditParseInvokeResponse response = request(creditParseResultUrl, params,
|
|
||||||
"结果接口第" + attempt + "次查询");
|
|
||||||
requireSuccessfulServiceResponse(response, "征信解析结果接口");
|
|
||||||
if (response.getData() == null || response.getData().getMappingOutputFields() == null) {
|
|
||||||
waitForNextResult(serialNum, attempt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (response.getData().getMappingOutputFields().getPayload() != null) {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
waitForNextResult(serialNum, attempt);
|
|
||||||
}
|
|
||||||
throw new LsfxApiException("征信解析结果未返回");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForNextResult(String serialNum, int attempt) {
|
|
||||||
if (attempt >= RESULT_QUERY_MAX_ATTEMPTS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.info("【征信解析】结果未返回: serialNum={}, attempt={}/{}, {}ms后重试",
|
|
||||||
serialNum, attempt, RESULT_QUERY_MAX_ATTEMPTS, RESULT_QUERY_INTERVAL_MILLIS);
|
|
||||||
try {
|
|
||||||
sleepBeforeNextResultQuery(RESULT_QUERY_INTERVAL_MILLIS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new LsfxApiException("征信解析结果查询被中断", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sleepBeforeNextResultQuery(long intervalMillis) throws InterruptedException {
|
|
||||||
Thread.sleep(intervalMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreditParseInvokeResponse request(String url, Map<String, Object> params, String stage) {
|
|
||||||
try {
|
|
||||||
log.info("【征信解析】{}请求: url={}, params={}", stage, url, toJson(params));
|
|
||||||
String responseJson = httpUtil.postUrlEncodedFormForString(url, params, null);
|
|
||||||
log.info("【征信解析】{}返回JSON: {}", stage, responseJson);
|
|
||||||
return objectMapper.readValue(responseJson, CreditParseInvokeResponse.class);
|
|
||||||
} catch (LsfxApiException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new LsfxApiException("征信解析" + stage + "调用失败: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireSuccessfulInitiateResponse(CreditParseInvokeResponse response, String stage) {
|
|
||||||
requireSuccessfulServiceResponse(response, stage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireSuccessfulServiceResponse(CreditParseInvokeResponse response, String stage) {
|
|
||||||
requireSuccessfulPlatformResponse(response, stage);
|
|
||||||
if (response.getData() == null) {
|
|
||||||
throw new LsfxApiException(stage + "返回结果为空");
|
|
||||||
}
|
|
||||||
if (!Integer.valueOf(INITIATE_SUCCESS_STATUS).equals(response.getData().getStatus())) {
|
|
||||||
throw new LsfxApiException(serviceErrorMessage(response, stage + "状态异常: " + response.getData().getStatus()));
|
|
||||||
}
|
|
||||||
if (!Integer.valueOf(INITIATE_SUCCESS_REASON_CODE).equals(response.getData().getReasonCode())) {
|
|
||||||
throw new LsfxApiException(serviceErrorMessage(response, stage + "原因码异常: " + response.getData().getReasonCode()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireSuccessfulPlatformResponse(CreditParseInvokeResponse response, String stage) {
|
|
||||||
if (response == null) {
|
|
||||||
throw new LsfxApiException(stage + "返回结果为空");
|
|
||||||
}
|
|
||||||
if (!Boolean.TRUE.equals(response.getSuccess())) {
|
|
||||||
throw new LsfxApiException(stage + "平台调用失败");
|
|
||||||
}
|
|
||||||
if (!Integer.valueOf(PLATFORM_SUCCESS_CODE).equals(response.getCode())) {
|
|
||||||
throw new LsfxApiException(stage + "平台状态码异常: " + response.getCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildSerialNum() {
|
|
||||||
return "CCDI_CREDIT_" + System.currentTimeMillis() + "_" + IdUtils.fastSimpleUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String stringValue(Object value, String defaultValue) {
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
String text = value.toString().trim();
|
|
||||||
return text.isEmpty() ? defaultValue : text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String serviceErrorMessage(CreditParseInvokeResponse response, String defaultValue) {
|
|
||||||
if (response == null || response.getData() == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
String reasonMessage = stringValue(response.getData().getReasonMessage(), null);
|
|
||||||
if (reasonMessage != null) {
|
|
||||||
return reasonMessage;
|
|
||||||
}
|
|
||||||
if (response.getData().getMappingOutputFields() != null) {
|
|
||||||
return stringValue(response.getData().getMappingOutputFields().getMessage(), defaultValue);
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toJson(Object value) {
|
|
||||||
try {
|
|
||||||
return objectMapper.writeValueAsString(value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return String.valueOf(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import com.ruoyi.common.annotation.Anonymous;
|
|||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||||
import com.ruoyi.lsfx.exception.LsfxApiException;
|
import com.ruoyi.lsfx.exception.LsfxApiException;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@@ -14,6 +14,13 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
|
||||||
@Tag(name = "征信解析接口测试", description = "用于测试征信解析接口")
|
@Tag(name = "征信解析接口测试", description = "用于测试征信解析接口")
|
||||||
@Anonymous
|
@Anonymous
|
||||||
@@ -22,27 +29,56 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
public class CreditParseController {
|
public class CreditParseController {
|
||||||
|
|
||||||
private static final String DEFAULT_MODEL = "LXCUSTALL";
|
private static final String DEFAULT_MODEL = "LXCUSTALL";
|
||||||
|
private static final String DEFAULT_HTYPE = "PERSON";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CreditParseClient creditParseClient;
|
private CreditParseClient creditParseClient;
|
||||||
|
|
||||||
@Operation(summary = "解析征信HTML", description = "传入征信HTML远程地址并调用外部解析服务")
|
@Operation(summary = "解析征信HTML", description = "上传征信HTML文件并调用外部解析服务")
|
||||||
@PostMapping("/parse")
|
@PostMapping("/parse")
|
||||||
public AjaxResult parse(@Parameter(description = "征信HTML远程访问地址") @RequestParam("remotePath") String remotePath,
|
public AjaxResult parse(@Parameter(description = "征信HTML文件") @RequestParam("file") MultipartFile file,
|
||||||
@Parameter(description = "模型编码,默认LXCUSTALL") @RequestParam(required = false) String model) {
|
@Parameter(description = "解析模型,默认LXCUSTALL") @RequestParam(required = false) String model,
|
||||||
if (StringUtils.isBlank(remotePath)) {
|
@Parameter(description = "主体类型,默认PERSON") @RequestParam(required = false) String hType) {
|
||||||
return AjaxResult.error("征信HTML远程地址不能为空");
|
if (file == null || file.isEmpty()) {
|
||||||
|
return AjaxResult.error("征信HTML文件不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
if (StringUtils.isBlank(originalFilename)) {
|
||||||
|
return AjaxResult.error("文件名不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String lowerCaseName = originalFilename.toLowerCase();
|
||||||
|
if (!lowerCaseName.endsWith(".html") && !lowerCaseName.endsWith(".htm")) {
|
||||||
|
return AjaxResult.error("仅支持 HTML 格式文件");
|
||||||
}
|
}
|
||||||
|
|
||||||
String actualModel = StringUtils.isBlank(model) ? DEFAULT_MODEL : model;
|
String actualModel = StringUtils.isBlank(model) ? DEFAULT_MODEL : model;
|
||||||
|
String actualHType = StringUtils.isBlank(hType) ? DEFAULT_HTYPE : hType;
|
||||||
|
|
||||||
|
Path tempFile = null;
|
||||||
try {
|
try {
|
||||||
CreditParseInvokeResponse response = creditParseClient.parse(actualModel, remotePath);
|
String suffix = lowerCaseName.endsWith(".htm") ? ".htm" : ".html";
|
||||||
|
tempFile = Files.createTempFile("credit_parse_", suffix);
|
||||||
|
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
File convertedFile = tempFile.toFile();
|
||||||
|
CreditParseResponse response = creditParseClient.parse(actualModel, actualHType, convertedFile);
|
||||||
return AjaxResult.success(response);
|
return AjaxResult.success(response);
|
||||||
} catch (LsfxApiException e) {
|
} catch (LsfxApiException e) {
|
||||||
return AjaxResult.error(e.getMessage());
|
return AjaxResult.error(e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return AjaxResult.error("文件转换失败:" + e.getMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error("征信解析失败:" + e.getMessage());
|
return AjaxResult.error("征信解析失败:" + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (tempFile != null) {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// 忽略临时文件删除失败,避免影响主流程返回
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.ruoyi.lsfx.domain.response;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class CreditParseInvokeData {
|
|
||||||
|
|
||||||
private CreditParseResponse mappingOutputFields;
|
|
||||||
|
|
||||||
private Integer status;
|
|
||||||
|
|
||||||
private Integer reasonCode;
|
|
||||||
|
|
||||||
private String reasonMessage;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.ruoyi.lsfx.domain.response;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class CreditParseInvokeResponse {
|
|
||||||
|
|
||||||
private Boolean success;
|
|
||||||
|
|
||||||
private Integer code;
|
|
||||||
|
|
||||||
private CreditParseInvokeData data;
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package com.ruoyi.lsfx.domain.response;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
|
||||||
import com.fasterxml.jackson.core.ObjectCodec;
|
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class CreditParsePayloadDeserializer extends JsonDeserializer<CreditParsePayload> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CreditParsePayload deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
|
||||||
ObjectCodec codec = parser.getCodec();
|
|
||||||
JsonNode node = codec.readTree(parser);
|
|
||||||
if (node == null || node.isNull()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (node.isTextual()) {
|
|
||||||
String payloadText = node.asText();
|
|
||||||
if (payloadText == null || payloadText.trim().isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
node = codec.readTree(codec.getFactory().createParser(payloadText));
|
|
||||||
}
|
|
||||||
if (!node.isObject()) {
|
|
||||||
throw JsonMappingException.from(parser, "征信解析payload格式不支持");
|
|
||||||
}
|
|
||||||
return codec.treeToValue(node, CreditParsePayload.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.ruoyi.lsfx.domain.response;
|
package com.ruoyi.lsfx.domain.response;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -12,6 +11,5 @@ public class CreditParseResponse {
|
|||||||
@JsonProperty("status_code")
|
@JsonProperty("status_code")
|
||||||
private String statusCode;
|
private String statusCode;
|
||||||
|
|
||||||
@JsonDeserialize(using = CreditParsePayloadDeserializer.class)
|
|
||||||
private CreditParsePayload payload;
|
private CreditParsePayload payload;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,86 +206,6 @@ public class HttpUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送POST请求(application/x-www-form-urlencoded格式,带请求头)
|
|
||||||
* @param url 请求URL
|
|
||||||
* @param params 表单参数
|
|
||||||
* @param headers 请求头
|
|
||||||
* @param responseType 响应类型
|
|
||||||
* @return 响应对象
|
|
||||||
*/
|
|
||||||
public <T> T postUrlEncodedForm(String url, Map<String, Object> params, Map<String, String> headers, Class<T> responseType) {
|
|
||||||
try {
|
|
||||||
HttpHeaders httpHeaders = createHeaders(headers);
|
|
||||||
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
|
|
||||||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
|
|
||||||
if (params != null) {
|
|
||||||
params.forEach((key, value) -> {
|
|
||||||
if (value != null) {
|
|
||||||
body.add(key, value.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, httpHeaders);
|
|
||||||
|
|
||||||
ResponseEntity<T> response = restTemplate.postForEntity(url, requestEntity, responseType);
|
|
||||||
|
|
||||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
||||||
throw new LsfxApiException("API调用失败,HTTP状态码: " + response.getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
T responseBody = response.getBody();
|
|
||||||
if (responseBody == null) {
|
|
||||||
throw new LsfxApiException("API返回数据为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseBody;
|
|
||||||
} catch (RestClientException e) {
|
|
||||||
throw new LsfxApiException("网络请求失败: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送POST请求(application/x-www-form-urlencoded格式)并返回原始JSON字符串
|
|
||||||
* @param url 请求URL
|
|
||||||
* @param params 表单参数
|
|
||||||
* @param headers 请求头
|
|
||||||
* @return 原始响应内容
|
|
||||||
*/
|
|
||||||
public String postUrlEncodedFormForString(String url, Map<String, Object> params, Map<String, String> headers) {
|
|
||||||
try {
|
|
||||||
HttpHeaders httpHeaders = createHeaders(headers);
|
|
||||||
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
|
|
||||||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
|
|
||||||
if (params != null) {
|
|
||||||
params.forEach((key, value) -> {
|
|
||||||
if (value != null) {
|
|
||||||
body.add(key, value.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, httpHeaders);
|
|
||||||
ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
|
|
||||||
|
|
||||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
||||||
throw new LsfxApiException("API调用失败,HTTP状态码: " + response.getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
String responseBody = response.getBody();
|
|
||||||
if (responseBody == null) {
|
|
||||||
throw new LsfxApiException("API返回数据为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseBody;
|
|
||||||
} catch (RestClientException e) {
|
|
||||||
throw new LsfxApiException("网络请求失败: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件(Multipart格式)
|
* 上传文件(Multipart格式)
|
||||||
* @param url 请求URL
|
* @param url 请求URL
|
||||||
|
|||||||
@@ -1,34 +1,24 @@
|
|||||||
package com.ruoyi.lsfx.controller;
|
package com.ruoyi.lsfx.controller;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||||
import com.ruoyi.lsfx.exception.LsfxApiException;
|
import com.ruoyi.lsfx.exception.LsfxApiException;
|
||||||
import com.ruoyi.lsfx.util.HttpUtil;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@@ -41,21 +31,31 @@ class CreditParseControllerTest {
|
|||||||
private CreditParseController controller;
|
private CreditParseController controller;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void parse_shouldRejectBlankRemotePath() {
|
void parse_shouldRejectEmptyFile() {
|
||||||
AjaxResult result = controller.parse(null, null);
|
AjaxResult result = controller.parse(null, null, null);
|
||||||
assertEquals(500, result.get("code"));
|
assertEquals(500, result.get("code"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldUseDefaultModelWhenMissing() {
|
void parse_shouldRejectNonHtmlFile() {
|
||||||
CreditParseInvokeResponse response = new CreditParseInvokeResponse();
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
response.setSuccess(true);
|
"file", "credit.pdf", "application/pdf", "x".getBytes(StandardCharsets.UTF_8)
|
||||||
response.setCode(10000);
|
);
|
||||||
|
AjaxResult result = controller.parse(file, null, null);
|
||||||
|
assertEquals(500, result.get("code"));
|
||||||
|
}
|
||||||
|
|
||||||
String remotePath = "http://127.0.0.1:62318/profile/credit-html/a.html";
|
@Test
|
||||||
when(client.parse(eq("LXCUSTALL"), eq(remotePath))).thenReturn(response);
|
void shouldUseDefaultModelAndTypeWhenMissing() {
|
||||||
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
|
"file", "credit.html", "text/html", "<html/>".getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
CreditParseResponse response = new CreditParseResponse();
|
||||||
|
response.setStatusCode("0");
|
||||||
|
|
||||||
AjaxResult result = controller.parse(remotePath, null);
|
when(client.parse(eq("LXCUSTALL"), eq("PERSON"), any(File.class))).thenReturn(response);
|
||||||
|
|
||||||
|
AjaxResult result = controller.parse(file, null, null);
|
||||||
|
|
||||||
assertEquals(200, result.get("code"));
|
assertEquals(200, result.get("code"));
|
||||||
assertSame(response, result.get("data"));
|
assertSame(response, result.get("data"));
|
||||||
@@ -63,230 +63,14 @@ class CreditParseControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnAjaxErrorWhenClientThrows() {
|
void shouldReturnAjaxErrorWhenClientThrows() {
|
||||||
when(client.parse(anyString(), anyString()))
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
|
"file", "credit.html", "text/html", "<html/>".getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
when(client.parse(anyString(), anyString(), any(File.class)))
|
||||||
.thenThrow(new LsfxApiException("超时"));
|
.thenThrow(new LsfxApiException("超时"));
|
||||||
|
|
||||||
AjaxResult result = controller.parse("http://127.0.0.1:62318/profile/credit-html/a.html", null);
|
AjaxResult result = controller.parse(file, null, null);
|
||||||
|
|
||||||
assertEquals(500, result.get("code"));
|
assertEquals(500, result.get("code"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
void creditParseClient_shouldInitiateAndQueryResultWithSameSerialNum() throws Exception {
|
|
||||||
HttpUtil httpUtil = mock(HttpUtil.class);
|
|
||||||
CreditParseClient parseClient = new CreditParseClient();
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "runType", "1");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
|
|
||||||
|
|
||||||
String payload = "{\"lx_header\":{\"query_cert_no\":\"330101199001010011\",\"query_cust_name\":\"张三\",\"report_time\":\"2026-03-24\"},\"lx_debt\":{\"uncle_bank_house_bal\":\"1\"},\"lx_publictype\":{\"civil_cnt\":1}}";
|
|
||||||
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn(initiateSuccessResponse());
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn(resultSuccessResponse(objectMapper, payload, "ERR_SHOULD_IGNORE"));
|
|
||||||
|
|
||||||
String remotePath = "http://127.0.0.1:62318/profile/credit-html/a.html";
|
|
||||||
CreditParseInvokeResponse actual = parseClient.parse(remotePath);
|
|
||||||
|
|
||||||
assertEquals(true, actual.getSuccess());
|
|
||||||
assertEquals(10000, actual.getCode());
|
|
||||||
assertEquals("330101199001010011", actual.getData().getMappingOutputFields()
|
|
||||||
.getPayload().getLxHeader().get("query_cert_no"));
|
|
||||||
ArgumentCaptor<Map<String, Object>> initiateParamsCaptor = ArgumentCaptor.forClass((Class) Map.class);
|
|
||||||
verify(httpUtil).postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
|
||||||
initiateParamsCaptor.capture(),
|
|
||||||
isNull()
|
|
||||||
);
|
|
||||||
ArgumentCaptor<Map<String, Object>> resultParamsCaptor = ArgumentCaptor.forClass((Class) Map.class);
|
|
||||||
verify(httpUtil).postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
resultParamsCaptor.capture(),
|
|
||||||
isNull()
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, Object> initiateParams = initiateParamsCaptor.getValue();
|
|
||||||
Map<String, Object> resultParams = resultParamsCaptor.getValue();
|
|
||||||
assertNotNull(initiateParams.get("serialNum"));
|
|
||||||
assertTrue(initiateParams.get("serialNum").toString().startsWith("CCDI_CREDIT_"));
|
|
||||||
assertEquals(initiateParams.get("serialNum"), resultParams.get("serialNum"));
|
|
||||||
assertEquals("999000", initiateParams.get("orgCode"));
|
|
||||||
assertEquals("999000", resultParams.get("orgCode"));
|
|
||||||
assertEquals("1", initiateParams.get("runType"));
|
|
||||||
assertEquals("1", resultParams.get("runType"));
|
|
||||||
assertEquals(remotePath, initiateParams.get("remotePath"));
|
|
||||||
assertEquals("LXCUSTALL", initiateParams.get("model"));
|
|
||||||
assertEquals(false, resultParams.containsKey("remotePath"));
|
|
||||||
assertEquals(false, resultParams.containsKey("model"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
void creditParseClient_shouldRetryEmptyPayloadFiveTimesWithTwoSecondInterval() throws Exception {
|
|
||||||
HttpUtil httpUtil = mock(HttpUtil.class);
|
|
||||||
TestableCreditParseClient parseClient = new TestableCreditParseClient();
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "runType", "1");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
|
|
||||||
|
|
||||||
String emptyPayloadResponse = "{\"success\":true,\"code\":10000,\"data\":{\"status\":1,\"reasonCode\":200,\"mappingOutputFields\":{\"message\":\"\",\"status_code\":\"ERR_SHOULD_IGNORE\"}}}";
|
|
||||||
String payload = "{\"lx_header\":{\"query_cert_no\":\"330101199001010011\",\"query_cust_name\":\"张三\",\"report_time\":\"2026-03-24\"}}";
|
|
||||||
String resultResponse = resultSuccessResponse(objectMapper, payload, "ERR_SHOULD_IGNORE");
|
|
||||||
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn(initiateSuccessResponse());
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn(emptyPayloadResponse, emptyPayloadResponse, emptyPayloadResponse, emptyPayloadResponse, resultResponse);
|
|
||||||
|
|
||||||
CreditParseInvokeResponse actual = parseClient.parse("http://127.0.0.1:62318/profile/credit-html/a.html");
|
|
||||||
|
|
||||||
assertEquals("330101199001010011", actual.getData().getMappingOutputFields()
|
|
||||||
.getPayload().getLxHeader().get("query_cert_no"));
|
|
||||||
verify(httpUtil, times(5)).postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
);
|
|
||||||
assertEquals(List.of(2000L, 2000L, 2000L, 2000L), parseClient.getSleepIntervals());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void creditParseClient_shouldRejectInvalidOuterCode() throws Exception {
|
|
||||||
HttpUtil httpUtil = mock(HttpUtil.class);
|
|
||||||
TestableCreditParseClient parseClient = new TestableCreditParseClient();
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "runType", "1");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
|
|
||||||
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn(initiateSuccessResponse());
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn("{\"success\":true,\"code\":99999,\"data\":{\"mappingOutputFields\":{\"message\":\"\",\"status_code\":\"0\"}}}");
|
|
||||||
|
|
||||||
LsfxApiException exception = assertThrows(LsfxApiException.class,
|
|
||||||
() -> parseClient.parse("http://127.0.0.1:62318/profile/credit-html/a.html"));
|
|
||||||
|
|
||||||
assertTrue(exception.getMessage().contains("平台状态码异常"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void creditParseClient_shouldRejectFailedResultStatus() throws Exception {
|
|
||||||
HttpUtil httpUtil = mock(HttpUtil.class);
|
|
||||||
TestableCreditParseClient parseClient = new TestableCreditParseClient();
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "runType", "1");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
|
|
||||||
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn(initiateSuccessResponse());
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn("{\"success\":true,\"code\":10000,\"data\":{\"reasonMessage\":\"解析失败\",\"reasonCode\":500,\"status\":0,\"mappingOutputFields\":{\"message\":\"结果异常\"}}}");
|
|
||||||
|
|
||||||
LsfxApiException exception = assertThrows(LsfxApiException.class,
|
|
||||||
() -> parseClient.parse("http://127.0.0.1:62318/profile/credit-html/a.html"));
|
|
||||||
|
|
||||||
assertTrue(exception.getMessage().contains("解析失败"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void creditParseClient_shouldRejectFailedInitiateStatus() throws Exception {
|
|
||||||
HttpUtil httpUtil = mock(HttpUtil.class);
|
|
||||||
TestableCreditParseClient parseClient = new TestableCreditParseClient();
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "creditParseResultUrl", "http://tz/api/service/interface/invokeService/xfeatureResult");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "orgCode", "999000");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "runType", "1");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
|
|
||||||
ReflectionTestUtils.setField(parseClient, "objectMapper", objectMapper);
|
|
||||||
|
|
||||||
when(httpUtil.postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
)).thenReturn("{\"success\":true,\"code\":10000,\"data\":{\"mappingOutputFields\":{\"message\":\"文件写入失败\"},\"reasonMessage\":\"文件写入失败\",\"reasonCode\":500,\"status\":0}}");
|
|
||||||
|
|
||||||
LsfxApiException exception = assertThrows(LsfxApiException.class,
|
|
||||||
() -> parseClient.parse("http://127.0.0.1:62318/profile/credit-html/a.html"));
|
|
||||||
|
|
||||||
assertTrue(exception.getMessage().contains("文件写入失败"));
|
|
||||||
verify(httpUtil, times(0)).postUrlEncodedFormForString(
|
|
||||||
eq("http://tz/api/service/interface/invokeService/xfeatureResult"),
|
|
||||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
|
||||||
isNull()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String initiateSuccessResponse() {
|
|
||||||
return "{\"success\":true,\"code\":10000,\"data\":{\"traceId\":\"TRACE-001\","
|
|
||||||
+ "\"mappingOutputFields\":{\"message\":\"文件写入成功,流水号为: CCDI_CREDIT_TEST\"},"
|
|
||||||
+ "\"reasonMessage\":\"Running successfully\",\"procCode\":\"999000\","
|
|
||||||
+ "\"reasonCode\":200,\"status\":1}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String resultSuccessResponse(ObjectMapper objectMapper, String payload, String statusCode) throws Exception {
|
|
||||||
return "{\"success\":true,\"code\":10000,\"data\":{\"status\":1,\"reasonCode\":200,"
|
|
||||||
+ "\"mappingOutputFields\":{\"message\":\"\",\"status_code\":\"" + statusCode + "\",\"payload\":"
|
|
||||||
+ objectMapper.writeValueAsString(payload) + "}}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TestableCreditParseClient extends CreditParseClient {
|
|
||||||
private final List<Long> sleepIntervals = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void sleepBeforeNextResultQuery(long intervalMillis) {
|
|
||||||
sleepIntervals.add(intervalMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Long> getSleepIntervals() {
|
|
||||||
return sleepIntervals;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ public final class CcdiProjectStatusConstants {
|
|||||||
public static final String COMPLETED = "1";
|
public static final String COMPLETED = "1";
|
||||||
public static final String ARCHIVED = "2";
|
public static final String ARCHIVED = "2";
|
||||||
public static final String TAGGING = "3";
|
public static final String TAGGING = "3";
|
||||||
public static final String TAG_FAILED = "4";
|
|
||||||
|
|
||||||
private CcdiProjectStatusConstants() {
|
private CcdiProjectStatusConstants() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.ruoyi.ccdi.project.domain.excel.CcdiBankStatementExcel;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementFilterOptionsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementFilterOptionsVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiBankStatementService;
|
import com.ruoyi.ccdi.project.service.ICcdiBankStatementService;
|
||||||
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;
|
||||||
@@ -40,16 +39,13 @@ public class CcdiBankStatementController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiBankStatementService bankStatementService;
|
private ICcdiBankStatementService bankStatementService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询流水明细
|
* 分页查询流水明细
|
||||||
*/
|
*/
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "分页查询流水明细")
|
@Operation(summary = "分页查询流水明细")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public TableDataInfo list(CcdiBankStatementQueryDTO queryDTO) {
|
public TableDataInfo list(CcdiBankStatementQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||||
Page<CcdiBankStatementListVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
Page<CcdiBankStatementListVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
||||||
Page<CcdiBankStatementListVO> result = bankStatementService.selectStatementPage(page, queryDTO);
|
Page<CcdiBankStatementListVO> result = bankStatementService.selectStatementPage(page, queryDTO);
|
||||||
@@ -61,8 +57,8 @@ public class CcdiBankStatementController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/options")
|
@GetMapping("/options")
|
||||||
@Operation(summary = "查询项目级筛选项")
|
@Operation(summary = "查询项目级筛选项")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getOptions(Long projectId) {
|
public AjaxResult getOptions(Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiBankStatementFilterOptionsVO options = bankStatementService.getFilterOptions(projectId);
|
CcdiBankStatementFilterOptionsVO options = bankStatementService.getFilterOptions(projectId);
|
||||||
return AjaxResult.success(options);
|
return AjaxResult.success(options);
|
||||||
}
|
}
|
||||||
@@ -72,8 +68,8 @@ public class CcdiBankStatementController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/detail/{bankStatementId}")
|
@GetMapping("/detail/{bankStatementId}")
|
||||||
@Operation(summary = "查询流水详情")
|
@Operation(summary = "查询流水详情")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getDetail(@PathVariable Long bankStatementId) {
|
public AjaxResult getDetail(@PathVariable Long bankStatementId) {
|
||||||
projectAccessService.assertCanReadByBankStatementId(bankStatementId);
|
|
||||||
CcdiBankStatementDetailVO detail = bankStatementService.getStatementDetail(bankStatementId);
|
CcdiBankStatementDetailVO detail = bankStatementService.getStatementDetail(bankStatementId);
|
||||||
return AjaxResult.success(detail);
|
return AjaxResult.success(detail);
|
||||||
}
|
}
|
||||||
@@ -85,7 +81,6 @@ public class CcdiBankStatementController extends BaseController {
|
|||||||
@Operation(summary = "导出流水明细")
|
@Operation(summary = "导出流水明细")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:export')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:export')")
|
||||||
public void export(HttpServletResponse response, CcdiBankStatementQueryDTO queryDTO) {
|
public void export(HttpServletResponse response, CcdiBankStatementQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiBankStatementExcel> list = bankStatementService.selectStatementListForExport(queryDTO);
|
List<CcdiBankStatementExcel> list = bankStatementService.selectStatementListForExport(queryDTO);
|
||||||
ExcelUtil<CcdiBankStatementExcel> util = new ExcelUtil<>(CcdiBankStatementExcel.class);
|
ExcelUtil<CcdiBankStatementExcel> util = new ExcelUtil<>(CcdiBankStatementExcel.class);
|
||||||
util.exportExcel(response, list, "流水明细");
|
util.exportExcel(response, list, "流水明细");
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.ruoyi.ccdi.project.controller;
|
package com.ruoyi.ccdi.project.controller;
|
||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
|
||||||
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;
|
||||||
@@ -10,7 +9,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@@ -29,17 +27,12 @@ public class CcdiBankTagController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiBankTagService bankTagService;
|
private ICcdiBankTagService bankTagService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手动提交流水标签重算任务
|
* 手动提交流水标签重算任务
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "手动重算项目流水标签")
|
@Operation(summary = "手动重算项目流水标签")
|
||||||
@PostMapping("/rebuild")
|
@PostMapping("/rebuild")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult rebuild(@Validated @RequestBody CcdiBankTagRebuildDTO dto) {
|
public AjaxResult rebuild(@Validated @RequestBody CcdiBankTagRebuildDTO dto) {
|
||||||
projectAccessService.assertCanOperate(dto.getProjectId());
|
|
||||||
String operator = SecurityUtils.getUsername();
|
String operator = SecurityUtils.getUsername();
|
||||||
log.info("【流水标签】收到手动重算请求: projectId={}, modelCode={}, operator={}",
|
log.info("【流水标签】收到手动重算请求: projectId={}, modelCode={}, operator={}",
|
||||||
dto.getProjectId(), dto.getModelCode(), operator);
|
dto.getProjectId(), dto.getModelCode(), operator);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.ruoyi.ccdi.project.controller;
|
|||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiEvidenceQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiEvidenceQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiEvidenceSaveDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiEvidenceSaveDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiEvidenceVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiEvidenceVO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiEvidenceService;
|
import com.ruoyi.ccdi.project.service.ICcdiEvidenceService;
|
||||||
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;
|
||||||
@@ -35,17 +34,13 @@ public class CcdiEvidenceController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiEvidenceService evidenceService;
|
private ICcdiEvidenceService evidenceService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存证据
|
* 保存证据
|
||||||
*/
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "保存证据")
|
@Operation(summary = "保存证据")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult saveEvidence(@Validated @RequestBody CcdiEvidenceSaveDTO dto) {
|
public AjaxResult saveEvidence(@Validated @RequestBody CcdiEvidenceSaveDTO dto) {
|
||||||
projectAccessService.assertCanOperate(dto.getProjectId());
|
|
||||||
CcdiEvidenceVO vo = evidenceService.saveEvidence(dto, SecurityUtils.getUsername());
|
CcdiEvidenceVO vo = evidenceService.saveEvidence(dto, SecurityUtils.getUsername());
|
||||||
return AjaxResult.success("证据入库成功", vo);
|
return AjaxResult.success("证据入库成功", vo);
|
||||||
}
|
}
|
||||||
@@ -55,8 +50,8 @@ public class CcdiEvidenceController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "查询项目证据列表")
|
@Operation(summary = "查询项目证据列表")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult listEvidence(CcdiEvidenceQueryDTO queryDTO) {
|
public AjaxResult listEvidence(CcdiEvidenceQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiEvidenceVO> list = evidenceService.listEvidence(queryDTO);
|
List<CcdiEvidenceVO> list = evidenceService.listEvidence(queryDTO);
|
||||||
return AjaxResult.success(list);
|
return AjaxResult.success(list);
|
||||||
}
|
}
|
||||||
@@ -66,8 +61,8 @@ public class CcdiEvidenceController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/{evidenceId}")
|
@GetMapping("/{evidenceId}")
|
||||||
@Operation(summary = "查询证据详情")
|
@Operation(summary = "查询证据详情")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getEvidence(@PathVariable Long evidenceId) {
|
public AjaxResult getEvidence(@PathVariable Long evidenceId) {
|
||||||
projectAccessService.assertCanReadByEvidenceId(evidenceId);
|
|
||||||
CcdiEvidenceVO vo = evidenceService.getEvidence(evidenceId);
|
CcdiEvidenceVO vo = evidenceService.getEvidence(evidenceId);
|
||||||
return AjaxResult.success(vo);
|
return AjaxResult.success(vo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiPullBankInfoSubmitDTO;
|
|||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFileUploadStatisticsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiFileUploadStatisticsVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiIdCardParseVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiIdCardParseVO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiFileUploadService;
|
import com.ruoyi.ccdi.project.service.ICcdiFileUploadService;
|
||||||
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;
|
||||||
@@ -18,7 +17,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -42,22 +40,17 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiFileUploadService fileUploadService;
|
private ICcdiFileUploadService fileUploadService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量上传文件(异步)
|
* 批量上传文件(异步)
|
||||||
*/
|
*/
|
||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
@Operation(summary = "批量上传文件", description = "异步批量上传流水文件")
|
@Operation(summary = "批量上传文件", description = "异步批量上传流水文件")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult batchUpload(@RequestParam Long projectId,
|
public AjaxResult batchUpload(@RequestParam Long projectId,
|
||||||
@RequestParam MultipartFile[] files) {
|
@RequestParam MultipartFile[] files) {
|
||||||
// 参数校验
|
// 参数校验
|
||||||
if (projectId == null) {
|
if (projectId == null) {
|
||||||
return AjaxResult.error("项目ID不能为空");
|
return AjaxResult.error("项目ID不能为空");
|
||||||
}
|
}
|
||||||
projectAccessService.assertCanOperate(projectId);
|
|
||||||
if (files == null || files.length == 0) {
|
if (files == null || files.length == 0) {
|
||||||
return AjaxResult.error("请选择要上传的文件");
|
return AjaxResult.error("请选择要上传的文件");
|
||||||
}
|
}
|
||||||
@@ -78,9 +71,9 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
return AjaxResult.error("文件名不能为空");
|
return AjaxResult.error("文件名不能为空");
|
||||||
}
|
}
|
||||||
String lowerFileName = fileName.toLowerCase();
|
String lowerFileName = fileName.toLowerCase();
|
||||||
if (!lowerFileName.endsWith(".xlsx") && !lowerFileName.endsWith(".csv")
|
if (!lowerFileName.endsWith(".xlsx") && !lowerFileName.endsWith(".xls")
|
||||||
&& !lowerFileName.endsWith(".pdf")) {
|
&& !lowerFileName.endsWith(".csv") && !lowerFileName.endsWith(".pdf")) {
|
||||||
return AjaxResult.error("文件 " + fileName + " 格式不支持, 仅支持 PDF, CSV, XLSX 文件");
|
return AjaxResult.error("文件 " + fileName + " 格式不支持, 仅支持 PDF, CSV, Excel 文件");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +95,6 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/parse-id-card-file")
|
@PostMapping("/parse-id-card-file")
|
||||||
@Operation(summary = "解析身份证文件", description = "解析首个sheet第一列的身份证号")
|
@Operation(summary = "解析身份证文件", description = "解析首个sheet第一列的身份证号")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult parseIdCardFile(@RequestParam MultipartFile file) {
|
public AjaxResult parseIdCardFile(@RequestParam MultipartFile file) {
|
||||||
if (file == null || file.isEmpty()) {
|
if (file == null || file.isEmpty()) {
|
||||||
return AjaxResult.error("身份证文件不能为空");
|
return AjaxResult.error("身份证文件不能为空");
|
||||||
@@ -116,12 +108,10 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/pull-bank-info")
|
@PostMapping("/pull-bank-info")
|
||||||
@Operation(summary = "拉取本行信息", description = "按身份证号批量提交拉取本行信息任务")
|
@Operation(summary = "拉取本行信息", description = "按身份证号批量提交拉取本行信息任务")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult pullBankInfo(@RequestBody CcdiPullBankInfoSubmitDTO dto) {
|
public AjaxResult pullBankInfo(@RequestBody CcdiPullBankInfoSubmitDTO dto) {
|
||||||
if (dto == null || dto.getProjectId() == null) {
|
if (dto == null || dto.getProjectId() == null) {
|
||||||
return AjaxResult.error("项目ID不能为空");
|
return AjaxResult.error("项目ID不能为空");
|
||||||
}
|
}
|
||||||
projectAccessService.assertCanOperate(dto.getProjectId());
|
|
||||||
if (CollectionUtils.isEmpty(dto.getIdCards())) {
|
if (CollectionUtils.isEmpty(dto.getIdCards())) {
|
||||||
return AjaxResult.error("身份证号不能为空");
|
return AjaxResult.error("身份证号不能为空");
|
||||||
}
|
}
|
||||||
@@ -148,7 +138,6 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "查询上传记录列表", description = "分页查询文件上传记录")
|
@Operation(summary = "查询上传记录列表", description = "分页查询文件上传记录")
|
||||||
public TableDataInfo list(CcdiFileUploadQueryDTO queryDTO) {
|
public TableDataInfo list(CcdiFileUploadQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||||
Page<CcdiFileUploadRecord> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
Page<CcdiFileUploadRecord> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
||||||
Page<CcdiFileUploadRecord> result = fileUploadService.selectPage(page, queryDTO);
|
Page<CcdiFileUploadRecord> result = fileUploadService.selectPage(page, queryDTO);
|
||||||
@@ -161,7 +150,6 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
@GetMapping("/statistics/{projectId}")
|
@GetMapping("/statistics/{projectId}")
|
||||||
@Operation(summary = "查询上传统计", description = "统计各状态的文件数量")
|
@Operation(summary = "查询上传统计", description = "统计各状态的文件数量")
|
||||||
public AjaxResult getStatistics(@PathVariable Long projectId) {
|
public AjaxResult getStatistics(@PathVariable Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiFileUploadStatisticsVO statistics = fileUploadService.countByStatus(projectId);
|
CcdiFileUploadStatisticsVO statistics = fileUploadService.countByStatus(projectId);
|
||||||
return AjaxResult.success(statistics);
|
return AjaxResult.success(statistics);
|
||||||
}
|
}
|
||||||
@@ -172,7 +160,6 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
@GetMapping("/detail/{id}")
|
@GetMapping("/detail/{id}")
|
||||||
@Operation(summary = "查询记录详情", description = "根据ID查询文件上传记录详情")
|
@Operation(summary = "查询记录详情", description = "根据ID查询文件上传记录详情")
|
||||||
public AjaxResult getDetail(@PathVariable Long id) {
|
public AjaxResult getDetail(@PathVariable Long id) {
|
||||||
projectAccessService.assertCanReadByFileRecordId(id);
|
|
||||||
CcdiFileUploadRecord record = fileUploadService.getById(id);
|
CcdiFileUploadRecord record = fileUploadService.getById(id);
|
||||||
return AjaxResult.success(record);
|
return AjaxResult.success(record);
|
||||||
}
|
}
|
||||||
@@ -182,9 +169,7 @@ public class CcdiFileUploadController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@Operation(summary = "删除上传文件", description = "按上传记录ID删除文件并清理流水")
|
@Operation(summary = "删除上传文件", description = "按上传记录ID删除文件并清理流水")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult deleteFile(@PathVariable Long id) {
|
public AjaxResult deleteFile(@PathVariable Long id) {
|
||||||
projectAccessService.assertCanOperateByFileRecordId(id);
|
|
||||||
Long userId = SecurityUtils.getUserId();
|
Long userId = SecurityUtils.getUserId();
|
||||||
String message = fileUploadService.deleteFileUploadRecord(id, userId);
|
String message = fileUploadService.deleteFileUploadRecord(id, userId);
|
||||||
return AjaxResult.success(message);
|
return AjaxResult.success(message);
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.controller;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphEdgeDetailQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphManualEdgeSaveDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphEdgeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphStatementVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphVO;
|
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiFundGraphService;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
|
||||||
import com.ruoyi.common.core.page.PageDomain;
|
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
|
||||||
import com.ruoyi.common.core.page.TableSupport;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱Controller
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/ccdi/project/fund-graph")
|
|
||||||
@Tag(name = "资金流图谱")
|
|
||||||
public class CcdiFundGraphController extends BaseController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private ICcdiFundGraphService fundGraphService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
@GetMapping("/search")
|
|
||||||
@Operation(summary = "查询资金流图谱主体")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult searchSubjects(CcdiFundGraphQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiFundGraphNodeVO> subjects = fundGraphService.searchSubjects(queryDTO);
|
|
||||||
return AjaxResult.success(subjects);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/graph")
|
|
||||||
@Operation(summary = "查询一层资金流图谱")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getGraph(CcdiFundGraphQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiFundGraphVO graph = fundGraphService.getFundGraph(queryDTO);
|
|
||||||
return AjaxResult.success(graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/edge-detail")
|
|
||||||
@Operation(summary = "查询资金边流水明细")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public TableDataInfo getEdgeDetail(CcdiFundGraphEdgeDetailQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
|
||||||
Page<CcdiFundGraphStatementVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
|
||||||
Page<CcdiFundGraphStatementVO> result = fundGraphService.getEdgeDetails(page, queryDTO);
|
|
||||||
return getDataTable(result.getRecords(), result.getTotal());
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/manual-edge")
|
|
||||||
@Operation(summary = "新增手工资金流向")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult saveManualEdge(@RequestBody CcdiFundGraphManualEdgeSaveDTO saveDTO) {
|
|
||||||
try {
|
|
||||||
projectAccessService.assertCanOperate(saveDTO == null ? null : saveDTO.getProjectId());
|
|
||||||
CcdiFundGraphEdgeVO edge = fundGraphService.saveManualEdge(saveDTO, SecurityUtils.getUsername());
|
|
||||||
return AjaxResult.success(edge);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return AjaxResult.error(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,11 +11,9 @@ import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveAllDTO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.ModelListVO;
|
import com.ruoyi.ccdi.project.domain.vo.ModelListVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.ModelParamVO;
|
import com.ruoyi.ccdi.project.domain.vo.ModelParamVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO;
|
import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
|
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -33,17 +31,12 @@ public class CcdiModelParamController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiModelParamService modelParamService;
|
private ICcdiModelParamService modelParamService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询模型列表
|
* 查询模型列表
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "查询模型列表")
|
@Operation(summary = "查询模型列表")
|
||||||
@GetMapping("/modelList")
|
@GetMapping("/modelList")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult listModels(@RequestParam(required = false) Long projectId) {
|
public AjaxResult listModels(@RequestParam(required = false) Long projectId) {
|
||||||
assertCanReadProjectParam(projectId);
|
|
||||||
List<ModelListVO> list = modelParamService.selectModelList(projectId);
|
List<ModelListVO> list = modelParamService.selectModelList(projectId);
|
||||||
return success(list);
|
return success(list);
|
||||||
}
|
}
|
||||||
@@ -53,9 +46,7 @@ public class CcdiModelParamController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@Operation(summary = "查询模型参数列表")
|
@Operation(summary = "查询模型参数列表")
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult list(@Validated ModelParamQueryDTO queryDTO) {
|
public AjaxResult list(@Validated ModelParamQueryDTO queryDTO) {
|
||||||
assertCanReadProjectParam(queryDTO.getProjectId());
|
|
||||||
List<ModelParamVO> list = modelParamService.selectParamList(queryDTO);
|
List<ModelParamVO> list = modelParamService.selectParamList(queryDTO);
|
||||||
return success(list);
|
return success(list);
|
||||||
}
|
}
|
||||||
@@ -66,9 +57,7 @@ public class CcdiModelParamController extends BaseController {
|
|||||||
@Operation(summary = "保存模型参数")
|
@Operation(summary = "保存模型参数")
|
||||||
@Log(title = "模型参数配置", businessType = BusinessType.UPDATE)
|
@Log(title = "模型参数配置", businessType = BusinessType.UPDATE)
|
||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult save(@Validated @RequestBody ModelParamSaveDTO saveDTO) {
|
public AjaxResult save(@Validated @RequestBody ModelParamSaveDTO saveDTO) {
|
||||||
assertCanOperateProjectParam(saveDTO.getProjectId());
|
|
||||||
modelParamService.saveParams(saveDTO);
|
modelParamService.saveParams(saveDTO);
|
||||||
return success("保存成功");
|
return success("保存成功");
|
||||||
}
|
}
|
||||||
@@ -78,9 +67,7 @@ public class CcdiModelParamController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@Operation(summary = "查询所有模型及其参数")
|
@Operation(summary = "查询所有模型及其参数")
|
||||||
@GetMapping("/listAll")
|
@GetMapping("/listAll")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult listAll(@Validated ModelParamAllQueryDTO queryDTO) {
|
public AjaxResult listAll(@Validated ModelParamAllQueryDTO queryDTO) {
|
||||||
assertCanReadProjectParam(queryDTO.getProjectId());
|
|
||||||
ModelParamAllVO result = modelParamService.selectAllParams(queryDTO.getProjectId());
|
ModelParamAllVO result = modelParamService.selectAllParams(queryDTO.getProjectId());
|
||||||
return success(result);
|
return success(result);
|
||||||
}
|
}
|
||||||
@@ -91,24 +78,8 @@ public class CcdiModelParamController extends BaseController {
|
|||||||
@Operation(summary = "批量保存所有模型参数")
|
@Operation(summary = "批量保存所有模型参数")
|
||||||
@Log(title = "模型参数配置", businessType = BusinessType.UPDATE)
|
@Log(title = "模型参数配置", businessType = BusinessType.UPDATE)
|
||||||
@PostMapping("/saveAll")
|
@PostMapping("/saveAll")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
|
|
||||||
public AjaxResult saveAll(@Validated @RequestBody ModelParamSaveAllDTO saveAllDTO) {
|
public AjaxResult saveAll(@Validated @RequestBody ModelParamSaveAllDTO saveAllDTO) {
|
||||||
assertCanOperateProjectParam(saveAllDTO.getProjectId());
|
|
||||||
modelParamService.saveAllParams(saveAllDTO);
|
modelParamService.saveAllParams(saveAllDTO);
|
||||||
return success("保存成功");
|
return success("保存成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertCanReadProjectParam(Long projectId) {
|
|
||||||
if (projectId == null || projectId <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertCanOperateProjectParam(Long projectId) {
|
|
||||||
if (projectId == null || projectId <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
projectAccessService.assertCanOperate(projectId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ public class CcdiProjectController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/{projectId}")
|
@GetMapping("/{projectId}")
|
||||||
@Operation(summary = "查询项目详情")
|
@Operation(summary = "查询项目详情")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getProject(@PathVariable Long projectId) {
|
public AjaxResult getProject(@PathVariable Long projectId) {
|
||||||
CcdiProjectVO vo = projectService.getProjectById(projectId);
|
CcdiProjectVO vo = projectService.getProjectById(projectId);
|
||||||
return AjaxResult.success(vo);
|
return AjaxResult.success(vo);
|
||||||
@@ -95,6 +96,7 @@ public class CcdiProjectController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "查询项目列表")
|
@Operation(summary = "查询项目列表")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:list')")
|
||||||
public TableDataInfo listProject(CcdiProjectQueryDTO queryDTO) {
|
public TableDataInfo listProject(CcdiProjectQueryDTO queryDTO) {
|
||||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||||
Page<CcdiProjectVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
Page<CcdiProjectVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
||||||
@@ -107,6 +109,7 @@ public class CcdiProjectController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/history")
|
@GetMapping("/history")
|
||||||
@Operation(summary = "查询历史项目列表")
|
@Operation(summary = "查询历史项目列表")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:list')")
|
||||||
public AjaxResult listHistoryProjects(CcdiProjectQueryDTO queryDTO) {
|
public AjaxResult listHistoryProjects(CcdiProjectQueryDTO queryDTO) {
|
||||||
List<CcdiProjectHistoryListItemVO> result = projectService.listHistoryProjects(queryDTO);
|
List<CcdiProjectHistoryListItemVO> result = projectService.listHistoryProjects(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
@@ -128,6 +131,7 @@ public class CcdiProjectController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/statusCounts")
|
@GetMapping("/statusCounts")
|
||||||
@Operation(summary = "查询项目状态统计")
|
@Operation(summary = "查询项目状态统计")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:list')")
|
||||||
public AjaxResult getStatusCounts() {
|
public AjaxResult getStatusCounts() {
|
||||||
CcdiProjectStatusCountsVO counts = projectService.getStatusCounts();
|
CcdiProjectStatusCountsVO counts = projectService.getStatusCounts();
|
||||||
return AjaxResult.success(counts);
|
return AjaxResult.success(counts);
|
||||||
|
|||||||
@@ -2,20 +2,14 @@ package com.ruoyi.ccdi.project.controller;
|
|||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalPersonQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalRiskModelPeopleQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectExternalPersonWarningExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskModelPeopleExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountPageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountPageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalRiskSummaryVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalPersonWarningVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
||||||
@@ -23,7 +17,6 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
||||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
@@ -52,9 +45,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiProjectOverviewService overviewService;
|
private ICcdiProjectOverviewService overviewService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询风险仪表盘
|
* 查询风险仪表盘
|
||||||
*/
|
*/
|
||||||
@@ -62,7 +52,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询风险仪表盘")
|
@Operation(summary = "查询风险仪表盘")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getDashboard(Long projectId) {
|
public AjaxResult getDashboard(Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiProjectOverviewDashboardVO dashboard = overviewService.getDashboard(projectId);
|
CcdiProjectOverviewDashboardVO dashboard = overviewService.getDashboard(projectId);
|
||||||
return AjaxResult.success(dashboard);
|
return AjaxResult.success(dashboard);
|
||||||
}
|
}
|
||||||
@@ -74,35 +63,10 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询风险人员总览")
|
@Operation(summary = "查询风险人员总览")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getRiskPeople(CcdiProjectRiskPeopleQueryDTO queryDTO) {
|
public AjaxResult getRiskPeople(CcdiProjectRiskPeopleQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectRiskPeopleOverviewVO overview = overviewService.getRiskPeopleOverview(queryDTO);
|
CcdiProjectRiskPeopleOverviewVO overview = overviewService.getRiskPeopleOverview(queryDTO);
|
||||||
return AjaxResult.success(overview);
|
return AjaxResult.success(overview);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员预警
|
|
||||||
*/
|
|
||||||
@GetMapping("/external-persons")
|
|
||||||
@Operation(summary = "查询外部人员预警")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getExternalPersons(CcdiProjectExternalPersonQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExternalPersonWarningVO warnings = overviewService.getExternalPersonWarnings(queryDTO);
|
|
||||||
return AjaxResult.success(warnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险汇总
|
|
||||||
*/
|
|
||||||
@GetMapping("/external-persons/summary")
|
|
||||||
@Operation(summary = "查询外部人员风险汇总")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getExternalRiskSummary(Long projectId) {
|
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiProjectExternalRiskSummaryVO summary = overviewService.getExternalRiskSummary(projectId);
|
|
||||||
return AjaxResult.success(summary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询中高风险人员TOP10
|
* 查询中高风险人员TOP10
|
||||||
*/
|
*/
|
||||||
@@ -110,7 +74,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询中高风险人员TOP10")
|
@Operation(summary = "查询中高风险人员TOP10")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getTopRiskPeople(Long projectId) {
|
public AjaxResult getTopRiskPeople(Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiProjectTopRiskPeopleVO topRiskPeople = overviewService.getTopRiskPeople(projectId);
|
CcdiProjectTopRiskPeopleVO topRiskPeople = overviewService.getTopRiskPeople(projectId);
|
||||||
return AjaxResult.success(topRiskPeople);
|
return AjaxResult.success(topRiskPeople);
|
||||||
}
|
}
|
||||||
@@ -122,7 +85,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询风险模型卡片")
|
@Operation(summary = "查询风险模型卡片")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getRiskModelCards(Long projectId) {
|
public AjaxResult getRiskModelCards(Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiProjectRiskModelCardsVO cards = overviewService.getRiskModelCards(projectId);
|
CcdiProjectRiskModelCardsVO cards = overviewService.getRiskModelCards(projectId);
|
||||||
return AjaxResult.success(cards);
|
return AjaxResult.success(cards);
|
||||||
}
|
}
|
||||||
@@ -134,35 +96,10 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询风险模型命中人员")
|
@Operation(summary = "查询风险模型命中人员")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getRiskModelPeople(CcdiProjectRiskModelPeopleQueryDTO queryDTO) {
|
public AjaxResult getRiskModelPeople(CcdiProjectRiskModelPeopleQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectRiskModelPeopleVO people = overviewService.getRiskModelPeople(queryDTO);
|
CcdiProjectRiskModelPeopleVO people = overviewService.getRiskModelPeople(queryDTO);
|
||||||
return AjaxResult.success(people);
|
return AjaxResult.success(people);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险模型卡片
|
|
||||||
*/
|
|
||||||
@GetMapping("/external-risk-models/cards")
|
|
||||||
@Operation(summary = "查询外部人员风险模型卡片")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getExternalRiskModelCards(Long projectId) {
|
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiProjectRiskModelCardsVO cards = overviewService.getExternalRiskModelCards(projectId);
|
|
||||||
return AjaxResult.success(cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险模型命中人员
|
|
||||||
*/
|
|
||||||
@GetMapping("/external-risk-models/people")
|
|
||||||
@Operation(summary = "查询外部人员风险模型命中人员")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getExternalRiskModelPeople(CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectRiskModelPeopleVO people = overviewService.getExternalRiskModelPeople(queryDTO);
|
|
||||||
return AjaxResult.success(people);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询项目分析详情
|
* 查询项目分析详情
|
||||||
*/
|
*/
|
||||||
@@ -170,7 +107,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询项目分析详情")
|
@Operation(summary = "查询项目分析详情")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getPersonAnalysisDetail(CcdiProjectPersonAnalysisDetailQueryDTO queryDTO) {
|
public AjaxResult getPersonAnalysisDetail(CcdiProjectPersonAnalysisDetailQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectPersonAnalysisDetailVO detail = overviewService.getPersonAnalysisDetail(queryDTO);
|
CcdiProjectPersonAnalysisDetailVO detail = overviewService.getPersonAnalysisDetail(queryDTO);
|
||||||
return AjaxResult.success(detail);
|
return AjaxResult.success(detail);
|
||||||
}
|
}
|
||||||
@@ -182,7 +118,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询涉疑交易明细")
|
@Operation(summary = "查询涉疑交易明细")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getSuspiciousTransactions(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) {
|
public AjaxResult getSuspiciousTransactions(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectSuspiciousTransactionPageVO pageVO = overviewService.getSuspiciousTransactions(queryDTO);
|
CcdiProjectSuspiciousTransactionPageVO pageVO = overviewService.getSuspiciousTransactions(queryDTO);
|
||||||
return AjaxResult.success(pageVO);
|
return AjaxResult.success(pageVO);
|
||||||
}
|
}
|
||||||
@@ -194,7 +129,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询项目员工负面征信")
|
@Operation(summary = "查询项目员工负面征信")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getEmployeeCreditNegative(CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO) {
|
public AjaxResult getEmployeeCreditNegative(CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectEmployeeCreditNegativePageVO pageVO = overviewService.getEmployeeCreditNegative(queryDTO);
|
CcdiProjectEmployeeCreditNegativePageVO pageVO = overviewService.getEmployeeCreditNegative(queryDTO);
|
||||||
return AjaxResult.success(pageVO);
|
return AjaxResult.success(pageVO);
|
||||||
}
|
}
|
||||||
@@ -206,7 +140,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "查询异常账户人员信息")
|
@Operation(summary = "查询异常账户人员信息")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getAbnormalAccountPeople(CcdiProjectAbnormalAccountQueryDTO queryDTO) {
|
public AjaxResult getAbnormalAccountPeople(CcdiProjectAbnormalAccountQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectAbnormalAccountPageVO pageVO = overviewService.getAbnormalAccountPeople(queryDTO);
|
CcdiProjectAbnormalAccountPageVO pageVO = overviewService.getAbnormalAccountPeople(queryDTO);
|
||||||
return AjaxResult.success(pageVO);
|
return AjaxResult.success(pageVO);
|
||||||
}
|
}
|
||||||
@@ -221,7 +154,6 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
) {
|
) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiProjectSuspiciousTransactionExcel> rows = overviewService.exportSuspiciousTransactions(queryDTO);
|
List<CcdiProjectSuspiciousTransactionExcel> rows = overviewService.exportSuspiciousTransactions(queryDTO);
|
||||||
ExcelUtil<CcdiProjectSuspiciousTransactionExcel> util =
|
ExcelUtil<CcdiProjectSuspiciousTransactionExcel> util =
|
||||||
new ExcelUtil<>(CcdiProjectSuspiciousTransactionExcel.class);
|
new ExcelUtil<>(CcdiProjectSuspiciousTransactionExcel.class);
|
||||||
@@ -235,58 +167,12 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "导出风险人员总览")
|
@Operation(summary = "导出风险人员总览")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public void exportRiskPeople(HttpServletResponse response, Long projectId) {
|
public void exportRiskPeople(HttpServletResponse response, Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
List<CcdiProjectRiskPeopleOverviewExcel> rows = overviewService.exportRiskPeopleOverview(projectId);
|
List<CcdiProjectRiskPeopleOverviewExcel> rows = overviewService.exportRiskPeopleOverview(projectId);
|
||||||
ExcelUtil<CcdiProjectRiskPeopleOverviewExcel> util =
|
ExcelUtil<CcdiProjectRiskPeopleOverviewExcel> util =
|
||||||
new ExcelUtil<>(CcdiProjectRiskPeopleOverviewExcel.class);
|
new ExcelUtil<>(CcdiProjectRiskPeopleOverviewExcel.class);
|
||||||
util.exportExcel(response, rows, "风险人员总览");
|
util.exportExcel(response, rows, "风险人员总览");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出外部人员预警
|
|
||||||
*/
|
|
||||||
@PostMapping("/external-persons/export")
|
|
||||||
@Operation(summary = "导出外部人员预警")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public void exportExternalPersons(HttpServletResponse response, Long projectId) {
|
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
List<CcdiProjectExternalPersonWarningExcel> rows = overviewService.exportExternalPersonWarnings(projectId);
|
|
||||||
ExcelUtil<CcdiProjectExternalPersonWarningExcel> util =
|
|
||||||
new ExcelUtil<>(CcdiProjectExternalPersonWarningExcel.class);
|
|
||||||
util.exportExcel(response, rows, "外部人员预警");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出风险模型命中人员
|
|
||||||
*/
|
|
||||||
@PostMapping("/risk-models/people/export")
|
|
||||||
@Operation(summary = "导出风险模型命中人员")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public void exportRiskModelPeople(HttpServletResponse response, CcdiProjectRiskModelPeopleQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiProjectRiskModelPeopleExcel> rows = overviewService.exportRiskModelPeople(queryDTO);
|
|
||||||
ExcelUtil<CcdiProjectRiskModelPeopleExcel> util =
|
|
||||||
new ExcelUtil<>(CcdiProjectRiskModelPeopleExcel.class);
|
|
||||||
util.exportExcel(response, rows, "风险模型命中人员");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出外部人员风险模型命中人员
|
|
||||||
*/
|
|
||||||
@PostMapping("/external-risk-models/people/export")
|
|
||||||
@Operation(summary = "导出外部人员风险模型命中人员")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public void exportExternalRiskModelPeople(
|
|
||||||
HttpServletResponse response,
|
|
||||||
CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO
|
|
||||||
) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiProjectRiskModelPeopleExcel> rows = overviewService.exportExternalRiskModelPeople(queryDTO);
|
|
||||||
ExcelUtil<CcdiProjectRiskModelPeopleExcel> util =
|
|
||||||
new ExcelUtil<>(CcdiProjectRiskModelPeopleExcel.class);
|
|
||||||
util.exportExcel(response, rows, "外部人员风险模型命中人员");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出风险明细
|
* 导出风险明细
|
||||||
*/
|
*/
|
||||||
@@ -294,18 +180,16 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
@Operation(summary = "导出风险明细")
|
@Operation(summary = "导出风险明细")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public void exportRiskDetails(HttpServletResponse response, Long projectId) {
|
public void exportRiskDetails(HttpServletResponse response, Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
overviewService.exportRiskDetails(response, projectId);
|
overviewService.exportRiskDetails(response, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出结果总览报告
|
* 一键导出结果总览报告
|
||||||
*/
|
*/
|
||||||
@RequestMapping(value = "/report/export", method = { RequestMethod.GET, RequestMethod.POST })
|
@RequestMapping(value = "/report/export", method = { RequestMethod.GET, RequestMethod.POST })
|
||||||
@Operation(summary = "导出结果总览报告")
|
@Operation(summary = "一键导出结果总览报告")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public void exportOverviewReport(HttpServletResponse response, Long projectId) {
|
public void exportOverviewReport(HttpServletResponse response, Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
overviewService.exportOverviewReport(response, projectId);
|
overviewService.exportOverviewReport(response, projectId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferDetailVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferListVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferListVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiProjectSpecialCheckService;
|
import com.ruoyi.ccdi.project.service.ICcdiProjectSpecialCheckService;
|
||||||
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;
|
||||||
@@ -40,9 +39,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICcdiProjectSpecialCheckService specialCheckService;
|
private ICcdiProjectSpecialCheckService specialCheckService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询员工家庭资产负债列表
|
* 查询员工家庭资产负债列表
|
||||||
*/
|
*/
|
||||||
@@ -50,7 +46,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询员工家庭资产负债列表")
|
@Operation(summary = "查询员工家庭资产负债列表")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getFamilyAssetLiabilityList(@Validated CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO) {
|
public AjaxResult getFamilyAssetLiabilityList(@Validated CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectFamilyAssetLiabilityListVO result = specialCheckService.getFamilyAssetLiabilityList(queryDTO);
|
CcdiProjectFamilyAssetLiabilityListVO result = specialCheckService.getFamilyAssetLiabilityList(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -62,7 +57,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询员工家庭资产负债详情")
|
@Operation(summary = "查询员工家庭资产负债详情")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getFamilyAssetLiabilityDetail(@Validated CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO) {
|
public AjaxResult getFamilyAssetLiabilityDetail(@Validated CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectFamilyAssetLiabilityDetailVO result = specialCheckService.getFamilyAssetLiabilityDetail(queryDTO);
|
CcdiProjectFamilyAssetLiabilityDetailVO result = specialCheckService.getFamilyAssetLiabilityDetail(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -74,7 +68,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询采购拓展列表")
|
@Operation(summary = "查询采购拓展列表")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getExtendedPurchaseList(@Validated CcdiProjectExtendedPurchaseQueryDTO queryDTO) {
|
public AjaxResult getExtendedPurchaseList(@Validated CcdiProjectExtendedPurchaseQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExtendedPurchaseListVO result = specialCheckService.getExtendedPurchaseList(queryDTO);
|
CcdiProjectExtendedPurchaseListVO result = specialCheckService.getExtendedPurchaseList(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -86,7 +79,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询采购拓展详情")
|
@Operation(summary = "查询采购拓展详情")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getExtendedPurchaseDetail(@Validated CcdiProjectExtendedPurchaseDetailQueryDTO queryDTO) {
|
public AjaxResult getExtendedPurchaseDetail(@Validated CcdiProjectExtendedPurchaseDetailQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExtendedPurchaseDetailVO result = specialCheckService.getExtendedPurchaseDetail(queryDTO);
|
CcdiProjectExtendedPurchaseDetailVO result = specialCheckService.getExtendedPurchaseDetail(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -98,7 +90,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询招聘拓展列表")
|
@Operation(summary = "查询招聘拓展列表")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getExtendedRecruitmentList(@Validated CcdiProjectExtendedRecruitmentQueryDTO queryDTO) {
|
public AjaxResult getExtendedRecruitmentList(@Validated CcdiProjectExtendedRecruitmentQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExtendedRecruitmentListVO result = specialCheckService.getExtendedRecruitmentList(queryDTO);
|
CcdiProjectExtendedRecruitmentListVO result = specialCheckService.getExtendedRecruitmentList(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -110,7 +101,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询招聘拓展详情")
|
@Operation(summary = "查询招聘拓展详情")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getExtendedRecruitmentDetail(@Validated CcdiProjectExtendedRecruitmentDetailQueryDTO queryDTO) {
|
public AjaxResult getExtendedRecruitmentDetail(@Validated CcdiProjectExtendedRecruitmentDetailQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExtendedRecruitmentDetailVO result = specialCheckService.getExtendedRecruitmentDetail(queryDTO);
|
CcdiProjectExtendedRecruitmentDetailVO result = specialCheckService.getExtendedRecruitmentDetail(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -122,7 +112,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询调动拓展列表")
|
@Operation(summary = "查询调动拓展列表")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getExtendedTransferList(@Validated CcdiProjectExtendedTransferQueryDTO queryDTO) {
|
public AjaxResult getExtendedTransferList(@Validated CcdiProjectExtendedTransferQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExtendedTransferListVO result = specialCheckService.getExtendedTransferList(queryDTO);
|
CcdiProjectExtendedTransferListVO result = specialCheckService.getExtendedTransferList(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
@@ -134,7 +123,6 @@ public class CcdiProjectSpecialCheckController extends BaseController {
|
|||||||
@Operation(summary = "查询调动拓展详情")
|
@Operation(summary = "查询调动拓展详情")
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
public AjaxResult getExtendedTransferDetail(@Validated CcdiProjectExtendedTransferDetailQueryDTO queryDTO) {
|
public AjaxResult getExtendedTransferDetail(@Validated CcdiProjectExtendedTransferDetailQueryDTO queryDTO) {
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiProjectExtendedTransferDetailVO result = specialCheckService.getExtendedTransferDetail(queryDTO);
|
CcdiProjectExtendedTransferDetailVO result = specialCheckService.getExtendedTransferDetail(queryDTO);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.controller;
|
|
||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphSuspectedEnterpriseQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphVO;
|
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiRelationGraphService;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱Controller
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/ccdi/project/relation-graph")
|
|
||||||
@Tag(name = "关系图谱")
|
|
||||||
public class CcdiRelationGraphController extends BaseController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private ICcdiRelationGraphService relationGraphService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
@GetMapping("/search")
|
|
||||||
@Operation(summary = "查询关系图谱主体")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult searchSubjects(CcdiRelationGraphQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
List<CcdiRelationGraphNodeVO> subjects = relationGraphService.searchSubjects(queryDTO);
|
|
||||||
return AjaxResult.success(subjects);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/graph")
|
|
||||||
@Operation(summary = "查询一层关系图谱")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getGraph(CcdiRelationGraphQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiRelationGraphVO graph = relationGraphService.getRelationGraph(queryDTO);
|
|
||||||
return AjaxResult.success(graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/suspected-enterprises")
|
|
||||||
@Operation(summary = "查询关系图谱疑似同名企业")
|
|
||||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
|
||||||
public AjaxResult getSuspectedEnterprises(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO) {
|
|
||||||
projectAccessService.assertCanRead(queryDTO.getProjectId());
|
|
||||||
CcdiRelationGraphSuspectedEnterpriseVO result = relationGraphService.getSuspectedEnterprises(queryDTO);
|
|
||||||
return AjaxResult.success(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,7 @@ public class CcdiProject implements Serializable {
|
|||||||
/** 配置方式:default-全局默认,custom-自定义 */
|
/** 配置方式:default-全局默认,custom-自定义 */
|
||||||
private String configType;
|
private String configType;
|
||||||
|
|
||||||
/** 项目状态:0-进行中,1-已完成,2-已归档,3-打标中,4-打标失败 */
|
/** 项目状态:0-进行中,1-已完成,2-已归档,3-打标中 */
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
/** 是否归档:0-未归档,1-已归档 */
|
/** 是否归档:0-未归档,1-已归档 */
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前登录用户的项目访问范围。
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class ProjectAccessScope {
|
|
||||||
|
|
||||||
/** 当前用户名 */
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/** 是否可查看全部项目 */
|
|
||||||
private boolean viewAllProjects;
|
|
||||||
|
|
||||||
/** 是否超级管理员 */
|
|
||||||
private boolean superAdmin;
|
|
||||||
|
|
||||||
/** 是否项目管理员 */
|
|
||||||
private boolean projectManager;
|
|
||||||
}
|
|
||||||
@@ -40,9 +40,6 @@ public class CcdiBankStatementQueryDTO {
|
|||||||
/** 本方主体 */
|
/** 本方主体 */
|
||||||
private List<String> ourSubjects;
|
private List<String> ourSubjects;
|
||||||
|
|
||||||
/** 本方证件号 */
|
|
||||||
private List<String> ourCertNos;
|
|
||||||
|
|
||||||
/** 本方银行 */
|
/** 本方银行 */
|
||||||
private List<String> ourBanks;
|
private List<String> ourBanks;
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱边明细查询条件
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphEdgeDetailQueryDTO {
|
|
||||||
|
|
||||||
/** 项目ID:历史字段,资金流图谱不按项目过滤 */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 身份证号、员工姓名或本方户名 */
|
|
||||||
private String keyword;
|
|
||||||
|
|
||||||
/** 主体节点object_key;复用图谱公共SQL片段时兼容条件判断 */
|
|
||||||
private String objectKey;
|
|
||||||
|
|
||||||
/** 边起点 */
|
|
||||||
private String fromKey;
|
|
||||||
|
|
||||||
/** 边终点 */
|
|
||||||
private String toKey;
|
|
||||||
|
|
||||||
/** 方向:1支出,2收入 */
|
|
||||||
private String direction;
|
|
||||||
|
|
||||||
/** 交易开始时间 */
|
|
||||||
private String transactionStartTime;
|
|
||||||
|
|
||||||
/** 交易结束时间 */
|
|
||||||
private String transactionEndTime;
|
|
||||||
|
|
||||||
/** 最小金额 */
|
|
||||||
private BigDecimal amountMin;
|
|
||||||
|
|
||||||
/** 最大金额 */
|
|
||||||
private BigDecimal amountMax;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手工资金流向保存参数。
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphManualEdgeSaveDTO {
|
|
||||||
|
|
||||||
/** 当前项目ID,仅用于写权限校验,不参与手工资金流归属过滤 */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 起点主体object_key;为空时默认使用当前查询中心 */
|
|
||||||
private String fromObjectKey;
|
|
||||||
|
|
||||||
/** 起点主体名称 */
|
|
||||||
private String fromName;
|
|
||||||
|
|
||||||
/** 终点主体object_key;已有节点时传入 */
|
|
||||||
private String toObjectKey;
|
|
||||||
|
|
||||||
/** 终点主体名称;新建主体时必填 */
|
|
||||||
private String toName;
|
|
||||||
|
|
||||||
/** 终点主体身份证号/证件号;有值时按md5(trim(idNo))复用主体 */
|
|
||||||
private String toIdNo;
|
|
||||||
|
|
||||||
/** 手工录入汇总金额 */
|
|
||||||
private BigDecimal amount;
|
|
||||||
|
|
||||||
/** 手工录入笔数 */
|
|
||||||
private Integer transactionCount;
|
|
||||||
|
|
||||||
/** 方向:1支出,2收入 */
|
|
||||||
private String direction;
|
|
||||||
|
|
||||||
/** 资金流向关系说明 */
|
|
||||||
private String relationDesc;
|
|
||||||
|
|
||||||
/** 来源说明 */
|
|
||||||
private String sourceDesc;
|
|
||||||
|
|
||||||
/** 分析备注 */
|
|
||||||
private String remark;
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱查询条件
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphQueryDTO {
|
|
||||||
|
|
||||||
/** 项目ID:历史字段,资金流图谱不按项目过滤 */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 身份证号、员工姓名或本方户名 */
|
|
||||||
private String keyword;
|
|
||||||
|
|
||||||
/** 主体节点object_key;节点穿透时直接使用 */
|
|
||||||
private String objectKey;
|
|
||||||
|
|
||||||
/** 交易开始时间 */
|
|
||||||
private String transactionStartTime;
|
|
||||||
|
|
||||||
/** 交易结束时间 */
|
|
||||||
private String transactionEndTime;
|
|
||||||
|
|
||||||
/** 最小金额 */
|
|
||||||
private BigDecimal amountMin;
|
|
||||||
|
|
||||||
/** 最大金额 */
|
|
||||||
private BigDecimal amountMax;
|
|
||||||
|
|
||||||
/** 最小汇总金额,默认1000 */
|
|
||||||
private BigDecimal minTotalAmount;
|
|
||||||
|
|
||||||
/** 方向:1支出,2收入 */
|
|
||||||
private String direction;
|
|
||||||
|
|
||||||
/** 返回边数量上限 */
|
|
||||||
private Integer limit;
|
|
||||||
|
|
||||||
/** 预留追溯层级,一期固定按一层处理 */
|
|
||||||
private Integer depth;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员预警查询DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectExternalPersonQueryDTO {
|
|
||||||
|
|
||||||
/** 项目ID */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 页码 */
|
|
||||||
private Integer pageNum;
|
|
||||||
|
|
||||||
/** 每页数量 */
|
|
||||||
private Integer pageSize;
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员模型命中人员查询DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectExternalRiskModelPeopleQueryDTO {
|
|
||||||
|
|
||||||
/** 项目ID */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 模型编码 */
|
|
||||||
private List<String> modelCodes;
|
|
||||||
|
|
||||||
/** 匹配方式 */
|
|
||||||
private String matchMode;
|
|
||||||
|
|
||||||
/** 关键字 */
|
|
||||||
private String keyword;
|
|
||||||
|
|
||||||
/** 页码 */
|
|
||||||
private Integer pageNum;
|
|
||||||
|
|
||||||
/** 每页数量 */
|
|
||||||
private Integer pageSize;
|
|
||||||
|
|
||||||
public String getModelCodesCsv() {
|
|
||||||
if (modelCodes == null || modelCodes.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return modelCodes.stream()
|
|
||||||
.filter(item -> item != null && !item.isBlank())
|
|
||||||
.map(String::trim)
|
|
||||||
.distinct()
|
|
||||||
.collect(Collectors.joining(","));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱查询条件
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphQueryDTO {
|
|
||||||
|
|
||||||
/** 项目ID:历史字段,关系图谱不按项目过滤 */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 身份证号、姓名、统一社会信用代码或节点object_key */
|
|
||||||
private String keyword;
|
|
||||||
|
|
||||||
/** 节点object_key;节点穿透时直接使用 */
|
|
||||||
private String objectKey;
|
|
||||||
|
|
||||||
/** 返回边数量上限 */
|
|
||||||
private Integer limit;
|
|
||||||
|
|
||||||
/** 预留追溯层级,一期固定按一层处理 */
|
|
||||||
private Integer depth;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱疑似企业查询条件
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphSuspectedEnterpriseQueryDTO {
|
|
||||||
|
|
||||||
/** 项目ID */
|
|
||||||
private Long projectId;
|
|
||||||
|
|
||||||
/** 姓名 */
|
|
||||||
private String personName;
|
|
||||||
|
|
||||||
/** 证件号 */
|
|
||||||
private String certNo;
|
|
||||||
|
|
||||||
/** 出生日期,yyyy-MM-dd */
|
|
||||||
private String birthDate;
|
|
||||||
|
|
||||||
/** 返回数量上限 */
|
|
||||||
private Integer limit;
|
|
||||||
}
|
|
||||||
@@ -207,7 +207,6 @@ public class CcdiBankStatement implements Serializable {
|
|||||||
entity.setBatchSequence(item.getUploadSequnceNumber());
|
entity.setBatchSequence(item.getUploadSequnceNumber());
|
||||||
entity.setCustomerCertNo(item.getCustomerCertNo());
|
entity.setCustomerCertNo(item.getCustomerCertNo());
|
||||||
entity.setCustomerSocialCreditCode(item.getCustomerSocialCreditCode());
|
entity.setCustomerSocialCreditCode(item.getCustomerSocialCreditCode());
|
||||||
entity.setCretNo(normalizeCertNo(item.getCretNo()));
|
|
||||||
|
|
||||||
// 5. 特殊字段处理
|
// 5. 特殊字段处理
|
||||||
entity.setMetaJson(null); // 根据文档要求强制设为 null
|
entity.setMetaJson(null); // 根据文档要求强制设为 null
|
||||||
@@ -220,19 +219,4 @@ public class CcdiBankStatement implements Serializable {
|
|||||||
throw new RuntimeException("流水数据转换失败", e);
|
throw new RuntimeException("流水数据转换失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String normalizeCertNo(String value) {
|
|
||||||
if (value == null || value.isBlank()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String normalized = value.trim()
|
|
||||||
.replace('-', '-')
|
|
||||||
.replace('—', '-')
|
|
||||||
.replace('–', '-');
|
|
||||||
int separatorIndex = normalized.indexOf('-');
|
|
||||||
if (separatorIndex >= 0) {
|
|
||||||
normalized = normalized.substring(0, separatorIndex).trim();
|
|
||||||
}
|
|
||||||
return normalized.isEmpty() ? null : normalized;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.excel;
|
|
||||||
|
|
||||||
import com.ruoyi.common.annotation.Excel;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员预警导出对象
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectExternalPersonWarningExcel {
|
|
||||||
|
|
||||||
@Excel(name = "姓名")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Excel(name = "证件号")
|
|
||||||
private String idNo;
|
|
||||||
|
|
||||||
@Excel(name = "主体类型")
|
|
||||||
private String subjectType;
|
|
||||||
|
|
||||||
@Excel(name = "风险等级")
|
|
||||||
private String riskLevel;
|
|
||||||
|
|
||||||
@Excel(name = "命中模型数")
|
|
||||||
private Integer modelCount;
|
|
||||||
|
|
||||||
@Excel(name = "核心异常点")
|
|
||||||
private String riskPoint;
|
|
||||||
|
|
||||||
@Excel(name = "涉及对象")
|
|
||||||
private String relatedObject;
|
|
||||||
|
|
||||||
@Excel(name = "最近交易时间")
|
|
||||||
private String latestTradeTime;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.excel;
|
|
||||||
|
|
||||||
import com.ruoyi.common.annotation.Excel;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 风险模型命中人员导出对象
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectRiskModelPeopleExcel {
|
|
||||||
|
|
||||||
@Excel(name = "风险主体")
|
|
||||||
private String personName;
|
|
||||||
|
|
||||||
@Excel(name = "主体类型")
|
|
||||||
private String subjectType;
|
|
||||||
|
|
||||||
@Excel(name = "证件号")
|
|
||||||
private String idNo;
|
|
||||||
|
|
||||||
@Excel(name = "部门/涉及对象")
|
|
||||||
private String scopeName;
|
|
||||||
|
|
||||||
@Excel(name = "命中模型")
|
|
||||||
private String modelNames;
|
|
||||||
|
|
||||||
@Excel(name = "异常标签")
|
|
||||||
private String hitTags;
|
|
||||||
}
|
|
||||||
@@ -14,29 +14,20 @@ public class CcdiProjectSuspiciousTransactionExcel {
|
|||||||
@Excel(name = "交易时间")
|
@Excel(name = "交易时间")
|
||||||
private String trxDate;
|
private String trxDate;
|
||||||
|
|
||||||
@Excel(name = "本方账户")
|
@Excel(name = "可疑人员")
|
||||||
private String leAccountNo;
|
private String suspiciousPersonName;
|
||||||
|
|
||||||
@Excel(name = "本方主体")
|
@Excel(name = "关联人")
|
||||||
private String leAccountName;
|
private String relatedPersonName;
|
||||||
|
|
||||||
@Excel(name = "对方名称")
|
|
||||||
private String customerAccountName;
|
|
||||||
|
|
||||||
@Excel(name = "对方账户")
|
|
||||||
private String customerAccountNo;
|
|
||||||
|
|
||||||
@Excel(name = "关联员工")
|
@Excel(name = "关联员工")
|
||||||
private String relatedStaffDisplay;
|
private String relatedStaffDisplay;
|
||||||
|
|
||||||
@Excel(name = "摘要")
|
@Excel(name = "关系")
|
||||||
private String userMemo;
|
private String relationType;
|
||||||
|
|
||||||
@Excel(name = "交易类型")
|
@Excel(name = "摘要/交易类型")
|
||||||
private String cashType;
|
private String summaryAndCashType;
|
||||||
|
|
||||||
@Excel(name = "异常标签")
|
|
||||||
private String hitTags;
|
|
||||||
|
|
||||||
@Excel(name = "交易金额")
|
@Excel(name = "交易金额")
|
||||||
private BigDecimal displayAmount;
|
private BigDecimal displayAmount;
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱汇总边
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphEdgeVO {
|
|
||||||
|
|
||||||
private String edgeKey;
|
|
||||||
|
|
||||||
private String fromKey;
|
|
||||||
|
|
||||||
private String toKey;
|
|
||||||
|
|
||||||
private String fromObjectKey;
|
|
||||||
|
|
||||||
private String toObjectKey;
|
|
||||||
|
|
||||||
private String fromName;
|
|
||||||
|
|
||||||
private String toName;
|
|
||||||
|
|
||||||
private BigDecimal totalAmount;
|
|
||||||
|
|
||||||
private Long transactionCount;
|
|
||||||
|
|
||||||
private String firstTrxDate;
|
|
||||||
|
|
||||||
private String lastTrxDate;
|
|
||||||
|
|
||||||
private String direction;
|
|
||||||
|
|
||||||
private String familyRelationType;
|
|
||||||
|
|
||||||
private String sourceType;
|
|
||||||
|
|
||||||
private String relationDesc;
|
|
||||||
|
|
||||||
private String sourceDesc;
|
|
||||||
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
private Integer depth;
|
|
||||||
|
|
||||||
private Boolean canTrace;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱节点
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphNodeVO {
|
|
||||||
|
|
||||||
private String nodeKey;
|
|
||||||
|
|
||||||
private String objectKey;
|
|
||||||
|
|
||||||
private String nodeName;
|
|
||||||
|
|
||||||
private String idNo;
|
|
||||||
|
|
||||||
private String cinocsno;
|
|
||||||
|
|
||||||
private String idnoType;
|
|
||||||
|
|
||||||
private String staffId;
|
|
||||||
|
|
||||||
private String sourceType;
|
|
||||||
|
|
||||||
private String nodeType;
|
|
||||||
|
|
||||||
private String identityType;
|
|
||||||
|
|
||||||
private String relationType;
|
|
||||||
|
|
||||||
private Long accountCount;
|
|
||||||
|
|
||||||
private String createdTime;
|
|
||||||
|
|
||||||
private String updatedTime;
|
|
||||||
|
|
||||||
private Boolean canExpand;
|
|
||||||
|
|
||||||
private Integer depth;
|
|
||||||
|
|
||||||
private BigDecimal totalAmount;
|
|
||||||
|
|
||||||
private Long transactionCount;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱边对应流水明细
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphStatementVO {
|
|
||||||
|
|
||||||
private Long bankStatementId;
|
|
||||||
|
|
||||||
private String trxDate;
|
|
||||||
|
|
||||||
private String leAccountNo;
|
|
||||||
|
|
||||||
private String leAccountName;
|
|
||||||
|
|
||||||
private String customerAccountName;
|
|
||||||
|
|
||||||
private String customerAccountNo;
|
|
||||||
|
|
||||||
private String cashType;
|
|
||||||
|
|
||||||
private String userMemo;
|
|
||||||
|
|
||||||
private BigDecimal amount;
|
|
||||||
|
|
||||||
private String direction;
|
|
||||||
|
|
||||||
private String familyRelationType;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱结果
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiFundGraphVO {
|
|
||||||
|
|
||||||
private CcdiFundGraphNodeVO centerNode;
|
|
||||||
|
|
||||||
private List<CcdiFundGraphNodeVO> nodes = new ArrayList<>();
|
|
||||||
|
|
||||||
private List<CcdiFundGraphEdgeVO> edges = new ArrayList<>();
|
|
||||||
|
|
||||||
private BigDecimal totalAmount = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
private Long transactionCount = 0L;
|
|
||||||
|
|
||||||
private Integer maxDepth = 1;
|
|
||||||
|
|
||||||
private Boolean traceReserved = true;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员预警项
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectExternalPersonWarningItemVO {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private String idNo;
|
|
||||||
|
|
||||||
private String subjectType;
|
|
||||||
|
|
||||||
private String riskLevel;
|
|
||||||
|
|
||||||
private String riskLevelType;
|
|
||||||
|
|
||||||
private Integer riskCount;
|
|
||||||
|
|
||||||
private Integer modelCount;
|
|
||||||
|
|
||||||
private String riskPoint;
|
|
||||||
|
|
||||||
private String relatedObject;
|
|
||||||
|
|
||||||
private String latestTradeTime;
|
|
||||||
|
|
||||||
private List<CcdiProjectRiskHitTagVO> riskPointTagList;
|
|
||||||
|
|
||||||
private String actionLabel;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员预警分页
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectExternalPersonWarningVO {
|
|
||||||
|
|
||||||
private List<CcdiProjectExternalPersonWarningItemVO> rows;
|
|
||||||
|
|
||||||
private Long total;
|
|
||||||
|
|
||||||
private Long pageNum;
|
|
||||||
|
|
||||||
private Long pageSize;
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员风险等级汇总
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiProjectExternalRiskSummaryVO {
|
|
||||||
|
|
||||||
private Integer total;
|
|
||||||
|
|
||||||
private Integer high;
|
|
||||||
|
|
||||||
private Integer medium;
|
|
||||||
|
|
||||||
private Integer low;
|
|
||||||
|
|
||||||
private Integer noRisk;
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package com.ruoyi.ccdi.project.domain.vo;
|
|||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectExternalPersonWarningExcel;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -22,16 +21,10 @@ public class CcdiProjectOverviewReportVO {
|
|||||||
|
|
||||||
private CcdiProjectOverviewDashboardVO dashboard = new CcdiProjectOverviewDashboardVO();
|
private CcdiProjectOverviewDashboardVO dashboard = new CcdiProjectOverviewDashboardVO();
|
||||||
|
|
||||||
private CcdiProjectExternalRiskSummaryVO externalRiskSummary = new CcdiProjectExternalRiskSummaryVO();
|
|
||||||
|
|
||||||
private List<CcdiProjectOverviewReportModelSummaryVO> modelSummaries = new ArrayList<>();
|
private List<CcdiProjectOverviewReportModelSummaryVO> modelSummaries = new ArrayList<>();
|
||||||
|
|
||||||
private List<CcdiProjectOverviewReportModelSummaryVO> externalModelSummaries = new ArrayList<>();
|
|
||||||
|
|
||||||
private List<CcdiProjectRiskModelPeopleItemVO> riskPeople = new ArrayList<>();
|
private List<CcdiProjectRiskModelPeopleItemVO> riskPeople = new ArrayList<>();
|
||||||
|
|
||||||
private List<CcdiProjectExternalPersonWarningExcel> externalPersonWarnings = new ArrayList<>();
|
|
||||||
|
|
||||||
private List<CcdiProjectOverviewReportSuspiciousTransactionVO> suspiciousTransactions = new ArrayList<>();
|
private List<CcdiProjectOverviewReportSuspiciousTransactionVO> suspiciousTransactions = new ArrayList<>();
|
||||||
|
|
||||||
private List<CcdiProjectEmployeeCreditNegativeExcel> illegalPeople = new ArrayList<>();
|
private List<CcdiProjectEmployeeCreditNegativeExcel> illegalPeople = new ArrayList<>();
|
||||||
|
|||||||
@@ -13,8 +13,4 @@ public class CcdiProjectOverviewStatVO {
|
|||||||
private String label;
|
private String label;
|
||||||
|
|
||||||
private Integer value;
|
private Integer value;
|
||||||
|
|
||||||
private Integer employeeValue;
|
|
||||||
|
|
||||||
private Integer externalValue;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,4 @@ public class CcdiProjectRiskHitTagVO {
|
|||||||
private String ruleName;
|
private String ruleName;
|
||||||
|
|
||||||
private String riskLevel;
|
private String riskLevel;
|
||||||
|
|
||||||
private String reasonDetail;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,4 @@ public class CcdiProjectStatusCountsVO {
|
|||||||
|
|
||||||
/** 打标中项目数(状态3) */
|
/** 打标中项目数(状态3) */
|
||||||
private Long status3;
|
private Long status3;
|
||||||
|
|
||||||
/** 打标失败项目数(状态4) */
|
|
||||||
private Long status4;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,4 @@ public class CcdiProjectSuspiciousTransactionItemVO {
|
|||||||
private Boolean hasModelRuleHit;
|
private Boolean hasModelRuleHit;
|
||||||
|
|
||||||
private Boolean hasNameListHit;
|
private Boolean hasNameListHit;
|
||||||
|
|
||||||
private String nameListHitType;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,21 +50,9 @@ public class CcdiProjectVO {
|
|||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
private Date updateTime;
|
private Date updateTime;
|
||||||
|
|
||||||
/** 最近一次打标失败原因 */
|
|
||||||
private String latestTagTaskErrorMessage;
|
|
||||||
|
|
||||||
/** 最近一次打标失败结束时间 */
|
|
||||||
private Date latestTagTaskEndTime;
|
|
||||||
|
|
||||||
/** 创建者(用户名) */
|
/** 创建者(用户名) */
|
||||||
private String createBy;
|
private String createBy;
|
||||||
|
|
||||||
/** 创建者姓名(真实姓名) */
|
/** 创建者姓名(真实姓名) */
|
||||||
private String createByName;
|
private String createByName;
|
||||||
|
|
||||||
/** 是否当前用户创建 */
|
|
||||||
private Boolean ownedByCurrentUser;
|
|
||||||
|
|
||||||
/** 当前用户是否可操作 */
|
|
||||||
private Boolean canOperate;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱边
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphEdgeVO {
|
|
||||||
|
|
||||||
private String objectKey;
|
|
||||||
|
|
||||||
private String fromKey;
|
|
||||||
|
|
||||||
private String toKey;
|
|
||||||
|
|
||||||
private String fromObjectKey;
|
|
||||||
|
|
||||||
private String toObjectKey;
|
|
||||||
|
|
||||||
private String fromName;
|
|
||||||
|
|
||||||
private String toName;
|
|
||||||
|
|
||||||
private String edgeTable;
|
|
||||||
|
|
||||||
private String relationType;
|
|
||||||
|
|
||||||
private String companyName;
|
|
||||||
|
|
||||||
private String stockName;
|
|
||||||
|
|
||||||
private String stockType;
|
|
||||||
|
|
||||||
private String stockPercent;
|
|
||||||
|
|
||||||
private String shouldCapi;
|
|
||||||
|
|
||||||
private String shouldCapiValue;
|
|
||||||
|
|
||||||
private String shouldCapiUnit;
|
|
||||||
|
|
||||||
private String shoudDate;
|
|
||||||
|
|
||||||
private String pKeyNo;
|
|
||||||
|
|
||||||
private String operName;
|
|
||||||
|
|
||||||
private String operKeyNo;
|
|
||||||
|
|
||||||
private String personId;
|
|
||||||
|
|
||||||
private String relationName;
|
|
||||||
|
|
||||||
private String relationCertNo;
|
|
||||||
|
|
||||||
private String gender;
|
|
||||||
|
|
||||||
private String birthDate;
|
|
||||||
|
|
||||||
private String relationCertType;
|
|
||||||
|
|
||||||
private String mobilePhone1;
|
|
||||||
|
|
||||||
private String mobilePhone2;
|
|
||||||
|
|
||||||
private String wechatNo1;
|
|
||||||
|
|
||||||
private String wechatNo2;
|
|
||||||
|
|
||||||
private String wechatNo3;
|
|
||||||
|
|
||||||
private String contactAddress;
|
|
||||||
|
|
||||||
private BigDecimal annualIncome;
|
|
||||||
|
|
||||||
private String relationDesc;
|
|
||||||
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
private String effectiveDate;
|
|
||||||
|
|
||||||
private String invalidDate;
|
|
||||||
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
private String dataSource;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱节点
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphNodeVO {
|
|
||||||
|
|
||||||
private String objectKey;
|
|
||||||
|
|
||||||
private String nodeKey;
|
|
||||||
|
|
||||||
private String nodeName;
|
|
||||||
|
|
||||||
private String idNumber;
|
|
||||||
|
|
||||||
private String subjectType;
|
|
||||||
|
|
||||||
private String sourceType;
|
|
||||||
|
|
||||||
private String detailRefType;
|
|
||||||
|
|
||||||
private String detailRefKey;
|
|
||||||
|
|
||||||
private String createdTime;
|
|
||||||
|
|
||||||
private String updatedTime;
|
|
||||||
|
|
||||||
private Boolean canExpand;
|
|
||||||
|
|
||||||
private Integer depth;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱疑似企业明细
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphSuspectedEnterpriseItemVO {
|
|
||||||
|
|
||||||
private String candidateKeyNo;
|
|
||||||
|
|
||||||
private String personName;
|
|
||||||
|
|
||||||
private String companyId;
|
|
||||||
|
|
||||||
private String companyName;
|
|
||||||
|
|
||||||
private String creditCode;
|
|
||||||
|
|
||||||
private String enterpriseStatus;
|
|
||||||
|
|
||||||
private String industryName;
|
|
||||||
|
|
||||||
private String relationType;
|
|
||||||
|
|
||||||
private String stockPercent;
|
|
||||||
|
|
||||||
/** 企业成立日期或当前可用的工商关系日期 */
|
|
||||||
private String establishDate;
|
|
||||||
|
|
||||||
private Integer ageAtEstablish;
|
|
||||||
|
|
||||||
private String matchReason;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱疑似企业结果
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphSuspectedEnterpriseVO {
|
|
||||||
|
|
||||||
/** 是否因同名候选过多被拦截 */
|
|
||||||
private Boolean blocked = false;
|
|
||||||
|
|
||||||
/** 拦截或空结果说明 */
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
/** 同名工商keyno数量 */
|
|
||||||
private Integer sameNameKeyNoCount = 0;
|
|
||||||
|
|
||||||
/** 表格明细 */
|
|
||||||
private List<CcdiRelationGraphSuspectedEnterpriseItemVO> rows = new ArrayList<>();
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱结果
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CcdiRelationGraphVO {
|
|
||||||
|
|
||||||
private CcdiRelationGraphNodeVO centerNode;
|
|
||||||
|
|
||||||
private List<CcdiRelationGraphNodeVO> nodes = new ArrayList<>();
|
|
||||||
|
|
||||||
private List<CcdiRelationGraphEdgeVO> edges = new ArrayList<>();
|
|
||||||
|
|
||||||
private Long edgeCount = 0L;
|
|
||||||
|
|
||||||
private Integer maxDepth = 1;
|
|
||||||
}
|
|
||||||
@@ -90,36 +90,6 @@ public interface CcdiBankTagAnalysisMapper {
|
|||||||
List<BankTagStatementHitVO> selectLargeTransferStatements(@Param("projectId") Long projectId,
|
List<BankTagStatementHitVO> selectLargeTransferStatements(@Param("projectId") Long projectId,
|
||||||
@Param("threshold") BigDecimal threshold);
|
@Param("threshold") BigDecimal threshold);
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员单笔大额交易
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @param threshold 单笔大额阈值
|
|
||||||
* @return 流水命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagStatementHitVO> selectExternalSingleLargeAmountStatements(@Param("projectId") Long projectId,
|
|
||||||
@Param("threshold") BigDecimal threshold);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员累计交易超限
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @param threshold 累计交易阈值
|
|
||||||
* @return 对象命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagObjectHitVO> selectExternalCumulativeTransactionAmountObjects(@Param("projectId") Long projectId,
|
|
||||||
@Param("threshold") BigDecimal threshold);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员年流水超限
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @param threshold 年流水阈值
|
|
||||||
* @return 对象命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagObjectHitVO> selectExternalAnnualTurnoverObjects(@Param("projectId") Long projectId,
|
|
||||||
@Param("threshold") BigDecimal threshold);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 与客户之间非正常资金往来
|
* 与客户之间非正常资金往来
|
||||||
*
|
*
|
||||||
@@ -156,26 +126,6 @@ public interface CcdiBankTagAnalysisMapper {
|
|||||||
*/
|
*/
|
||||||
List<BankTagStatementHitVO> selectGamblingSensitiveKeywordStatements(@Param("projectId") Long projectId);
|
List<BankTagStatementHitVO> selectGamblingSensitiveKeywordStatements(@Param("projectId") Long projectId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员疑似赌博摘要
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 流水命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagStatementHitVO> selectExternalGamblingMemoStatements(@Param("projectId") Long projectId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员同日多对手方疑似赌博交易
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @param amountMinThreshold 可疑金额下限
|
|
||||||
* @param amountMaxThreshold 可疑金额上限
|
|
||||||
* @return 对象命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagObjectHitVO> selectExternalMultiPartyGamblingTransferObjects(@Param("projectId") Long projectId,
|
|
||||||
@Param("amountMinThreshold") BigDecimal amountMinThreshold,
|
|
||||||
@Param("amountMaxThreshold") BigDecimal amountMaxThreshold);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 特殊金额交易
|
* 特殊金额交易
|
||||||
*
|
*
|
||||||
@@ -184,22 +134,6 @@ public interface CcdiBankTagAnalysisMapper {
|
|||||||
*/
|
*/
|
||||||
List<BankTagStatementHitVO> selectSpecialAmountTransactionStatements(@Param("projectId") Long projectId);
|
List<BankTagStatementHitVO> selectSpecialAmountTransactionStatements(@Param("projectId") Long projectId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员与员工或员工亲属交易
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 流水命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagStatementHitVO> selectExternalToStaffOrFamilyTransactionStatements(@Param("projectId") Long projectId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部人员夜间交易
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 流水命中结果
|
|
||||||
*/
|
|
||||||
List<BankTagStatementHitVO> selectExternalNightTransactionStatements(@Param("projectId") Long projectId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 月度固定收入疑似兼职
|
* 月度固定收入疑似兼职
|
||||||
*
|
*
|
||||||
@@ -338,11 +272,9 @@ public interface CcdiBankTagAnalysisMapper {
|
|||||||
* 微信支付宝提现超额
|
* 微信支付宝提现超额
|
||||||
*
|
*
|
||||||
* @param projectId 项目ID
|
* @param projectId 项目ID
|
||||||
* @param amountThreshold 提现金额阈值
|
|
||||||
* @return 对象命中结果
|
* @return 对象命中结果
|
||||||
*/
|
*/
|
||||||
List<BankTagObjectHitVO> selectWithdrawAmtObjects(@Param("projectId") Long projectId,
|
List<BankTagObjectHitVO> selectWithdrawAmtObjects(@Param("projectId") Long projectId);
|
||||||
@Param("amountThreshold") BigDecimal amountThreshold);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工资快速转出
|
* 工资快速转出
|
||||||
|
|||||||
@@ -32,12 +32,4 @@ public interface CcdiBankTagTaskMapper extends BaseMapper<CcdiBankTagTask> {
|
|||||||
* @return 任务实体
|
* @return 任务实体
|
||||||
*/
|
*/
|
||||||
CcdiBankTagTask selectRunningTaskByProjectId(@Param("projectId") Long projectId);
|
CcdiBankTagTask selectRunningTaskByProjectId(@Param("projectId") Long projectId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询项目最近一次失败任务
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 任务实体
|
|
||||||
*/
|
|
||||||
CcdiBankTagTask selectLatestFailedTaskByProjectId(@Param("projectId") Long projectId);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.mapper;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphEdgeDetailQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphManualEdgeSaveDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphEdgeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphStatementVO;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱Mapper
|
|
||||||
*/
|
|
||||||
@Mapper
|
|
||||||
public interface CcdiFundGraphMapper {
|
|
||||||
|
|
||||||
List<CcdiFundGraphNodeVO> selectFundGraphSubjects(@Param("query") CcdiFundGraphQueryDTO query);
|
|
||||||
|
|
||||||
List<CcdiFundGraphNodeVO> selectFundGraphSubjectsByExactKeyword(@Param("query") CcdiFundGraphQueryDTO query);
|
|
||||||
|
|
||||||
List<CcdiFundGraphNodeVO> selectFundGraphSubjectsByName(@Param("query") CcdiFundGraphQueryDTO query);
|
|
||||||
|
|
||||||
List<CcdiFundGraphEdgeVO> selectFundGraphEdges(@Param("query") CcdiFundGraphQueryDTO query);
|
|
||||||
|
|
||||||
List<CcdiFundGraphEdgeVO> selectFundGraphManualEdges(@Param("query") CcdiFundGraphQueryDTO query);
|
|
||||||
|
|
||||||
int countSubjectByObjectKey(@Param("objectKey") String objectKey);
|
|
||||||
|
|
||||||
int insertManualSubject(
|
|
||||||
@Param("objectKey") String objectKey,
|
|
||||||
@Param("idNo") String idNo,
|
|
||||||
@Param("name") String name
|
|
||||||
);
|
|
||||||
|
|
||||||
int insertManualEdge(
|
|
||||||
@Param("objectKey") String objectKey,
|
|
||||||
@Param("dto") CcdiFundGraphManualEdgeSaveDTO dto,
|
|
||||||
@Param("operator") String operator
|
|
||||||
);
|
|
||||||
|
|
||||||
Page<CcdiFundGraphStatementVO> selectFundGraphEdgeDetails(
|
|
||||||
Page<CcdiFundGraphStatementVO> page,
|
|
||||||
@Param("query") CcdiFundGraphEdgeDetailQueryDTO query
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package com.ruoyi.ccdi.project.mapper;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.ProjectAccessScope;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
||||||
@@ -26,9 +25,7 @@ public interface CcdiProjectMapper extends BaseMapper<CcdiProject> {
|
|||||||
* @param queryDTO 查询条件
|
* @param queryDTO 查询条件
|
||||||
* @return 分页结果
|
* @return 分页结果
|
||||||
*/
|
*/
|
||||||
Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page,
|
Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page, @Param("queryDTO") CcdiProjectQueryDTO queryDTO);
|
||||||
@Param("queryDTO") CcdiProjectQueryDTO queryDTO,
|
|
||||||
@Param("scope") ProjectAccessScope scope);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询历史项目列表
|
* 查询历史项目列表
|
||||||
@@ -36,14 +33,12 @@ public interface CcdiProjectMapper extends BaseMapper<CcdiProject> {
|
|||||||
* @param queryDTO 查询条件
|
* @param queryDTO 查询条件
|
||||||
* @return 历史项目列表
|
* @return 历史项目列表
|
||||||
*/
|
*/
|
||||||
List<CcdiProjectHistoryListItemVO> selectHistoryProjects(@Param("queryDTO") CcdiProjectQueryDTO queryDTO,
|
List<CcdiProjectHistoryListItemVO> selectHistoryProjects(@Param("queryDTO") CcdiProjectQueryDTO queryDTO);
|
||||||
@Param("scope") ProjectAccessScope scope);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新项目总人数与风险人数
|
* 更新项目风险人数
|
||||||
*
|
*
|
||||||
* @param projectId 项目ID
|
* @param projectId 项目ID
|
||||||
* @param targetCount 总人数
|
|
||||||
* @param highRiskCount 高风险人数
|
* @param highRiskCount 高风险人数
|
||||||
* @param mediumRiskCount 中风险人数
|
* @param mediumRiskCount 中风险人数
|
||||||
* @param lowRiskCount 低风险人数
|
* @param lowRiskCount 低风险人数
|
||||||
@@ -51,7 +46,6 @@ public interface CcdiProjectMapper extends BaseMapper<CcdiProject> {
|
|||||||
* @return 更新行数
|
* @return 更新行数
|
||||||
*/
|
*/
|
||||||
int updateRiskCountsByProjectId(@Param("projectId") Long projectId,
|
int updateRiskCountsByProjectId(@Param("projectId") Long projectId,
|
||||||
@Param("targetCount") Integer targetCount,
|
|
||||||
@Param("highRiskCount") Integer highRiskCount,
|
@Param("highRiskCount") Integer highRiskCount,
|
||||||
@Param("mediumRiskCount") Integer mediumRiskCount,
|
@Param("mediumRiskCount") Integer mediumRiskCount,
|
||||||
@Param("lowRiskCount") Integer lowRiskCount,
|
@Param("lowRiskCount") Integer lowRiskCount,
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalPersonQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalRiskModelPeopleQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
@@ -13,8 +11,6 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountItemVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalRiskSummaryVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalPersonWarningItemVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
|
||||||
@@ -125,88 +121,6 @@ public interface CcdiProjectOverviewMapper {
|
|||||||
@Param("query") CcdiProjectRiskModelPeopleQueryDTO query
|
@Param("query") CcdiProjectRiskModelPeopleQueryDTO query
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询风险模型命中人员导出列表
|
|
||||||
*
|
|
||||||
* @param query 查询条件
|
|
||||||
* @return 命中人员列表
|
|
||||||
*/
|
|
||||||
List<CcdiProjectRiskModelPeopleItemVO> selectRiskModelPeopleList(
|
|
||||||
@Param("query") CcdiProjectRiskModelPeopleQueryDTO query
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询外部人员预警
|
|
||||||
*
|
|
||||||
* @param page 分页参数
|
|
||||||
* @param query 查询条件
|
|
||||||
* @return 外部人员预警分页
|
|
||||||
*/
|
|
||||||
Page<CcdiProjectExternalPersonWarningItemVO> selectExternalPersonWarningPage(
|
|
||||||
Page<CcdiProjectExternalPersonWarningItemVO> page,
|
|
||||||
@Param("query") CcdiProjectExternalPersonQueryDTO query
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员预警导出列表
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 外部人员预警列表
|
|
||||||
*/
|
|
||||||
List<CcdiProjectExternalPersonWarningItemVO> selectExternalPersonWarningList(@Param("projectId") Long projectId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险等级汇总
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 外部人员风险等级汇总
|
|
||||||
*/
|
|
||||||
CcdiProjectExternalRiskSummaryVO selectExternalRiskSummaryByProjectId(@Param("projectId") Long projectId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员预警模型卡片
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 模型卡片
|
|
||||||
*/
|
|
||||||
List<CcdiProjectRiskModelCardVO> selectExternalRiskModelCardsByProjectId(@Param("projectId") Long projectId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询外部人员模型命中人员
|
|
||||||
*
|
|
||||||
* @param page 分页参数
|
|
||||||
* @param query 查询条件
|
|
||||||
* @return 命中人员分页
|
|
||||||
*/
|
|
||||||
Page<CcdiProjectRiskModelPeopleItemVO> selectExternalRiskModelPeoplePage(
|
|
||||||
Page<CcdiProjectRiskModelPeopleItemVO> page,
|
|
||||||
@Param("query") CcdiProjectExternalRiskModelPeopleQueryDTO query
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员模型命中人员导出列表
|
|
||||||
*
|
|
||||||
* @param query 查询条件
|
|
||||||
* @return 命中人员列表
|
|
||||||
*/
|
|
||||||
List<CcdiProjectRiskModelPeopleItemVO> selectExternalRiskModelPeopleList(
|
|
||||||
@Param("query") CcdiProjectExternalRiskModelPeopleQueryDTO query
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员命中标签
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @param certNo 外部人员证件号
|
|
||||||
* @param selectedModelCodes 已选模型编码CSV,可为空
|
|
||||||
* @return 命中标签列表
|
|
||||||
*/
|
|
||||||
List<CcdiProjectRiskHitTagVO> selectExternalRiskHitTagsByScope(
|
|
||||||
@Param("projectId") Long projectId,
|
|
||||||
@Param("certNo") String certNo,
|
|
||||||
@Param("selectedModelCodes") String selectedModelCodes
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询涉疑交易明细
|
* 分页查询涉疑交易明细
|
||||||
*
|
*
|
||||||
@@ -326,5 +240,4 @@ public interface CcdiProjectOverviewMapper {
|
|||||||
* @return 风险人数汇总
|
* @return 风险人数汇总
|
||||||
*/
|
*/
|
||||||
Map<String, Object> selectRiskCountSummaryByProjectId(@Param("projectId") Long projectId);
|
Map<String, Object> selectRiskCountSummaryByProjectId(@Param("projectId") Long projectId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.mapper;
|
|
||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphEdgeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseItemVO;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱Mapper
|
|
||||||
*/
|
|
||||||
@Mapper
|
|
||||||
public interface CcdiRelationGraphMapper {
|
|
||||||
|
|
||||||
List<CcdiRelationGraphNodeVO> selectRelationGraphSubjects(@Param("query") CcdiRelationGraphQueryDTO query);
|
|
||||||
|
|
||||||
List<CcdiRelationGraphNodeVO> selectRelationGraphNodesByKeys(@Param("objectKeys") List<String> objectKeys);
|
|
||||||
|
|
||||||
List<CcdiRelationGraphEdgeVO> selectRelationGraphEdges(@Param("query") CcdiRelationGraphQueryDTO query);
|
|
||||||
|
|
||||||
int countSuspectedEnterpriseKeyNos(@Param("personName") String personName);
|
|
||||||
|
|
||||||
List<CcdiRelationGraphSuspectedEnterpriseItemVO> selectSuspectedEnterprises(@Param("personName") String personName,
|
|
||||||
@Param("limit") Integer limit);
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.service;
|
|
||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
|
||||||
import com.ruoyi.ccdi.project.domain.ProjectAccessScope;
|
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
|
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiEvidence;
|
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiEvidenceMapper;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
|
||||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
|
||||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 项目访问控制。
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class CcdiProjectAccessService {
|
|
||||||
|
|
||||||
private static final String ROLE_ADMIN = "admin";
|
|
||||||
|
|
||||||
private static final String ROLE_MANAGER = "manager";
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectMapper projectMapper;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiBankStatementMapper bankStatementMapper;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiFileUploadRecordMapper fileUploadRecordMapper;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiEvidenceMapper evidenceMapper;
|
|
||||||
|
|
||||||
public ProjectAccessScope buildCurrentScope() {
|
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
|
||||||
String username = SecurityUtils.getUsername();
|
|
||||||
boolean superAdmin = isSuperAdmin(loginUser);
|
|
||||||
boolean projectManager = hasRole(loginUser, ROLE_MANAGER);
|
|
||||||
return new ProjectAccessScope(username, superAdmin || projectManager, superAdmin, projectManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canOperate(CcdiProject project) {
|
|
||||||
if (project == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ProjectAccessScope scope = buildCurrentScope();
|
|
||||||
return scope.isSuperAdmin() || Objects.equals(scope.getUsername(), project.getCreateBy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertCanRead(Long projectId) {
|
|
||||||
CcdiProject project = getRequiredProject(projectId);
|
|
||||||
ProjectAccessScope scope = buildCurrentScope();
|
|
||||||
if (scope.isViewAllProjects() || Objects.equals(scope.getUsername(), project.getCreateBy())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new ServiceException("无权查看该项目");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertCanOperate(Long projectId) {
|
|
||||||
CcdiProject project = getRequiredProject(projectId);
|
|
||||||
if (canOperate(project)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new ServiceException("无权操作该项目");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertCanReadByBankStatementId(Long bankStatementId) {
|
|
||||||
CcdiBankStatement statement = getRequiredBankStatement(bankStatementId);
|
|
||||||
assertCanRead(statement.getProjectId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertCanReadByFileRecordId(Long fileRecordId) {
|
|
||||||
CcdiFileUploadRecord record = getRequiredFileRecord(fileRecordId);
|
|
||||||
assertCanRead(record.getProjectId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertCanOperateByFileRecordId(Long fileRecordId) {
|
|
||||||
CcdiFileUploadRecord record = getRequiredFileRecord(fileRecordId);
|
|
||||||
assertCanOperate(record.getProjectId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertCanReadByEvidenceId(Long evidenceId) {
|
|
||||||
CcdiEvidence evidence = getRequiredEvidence(evidenceId);
|
|
||||||
assertCanRead(evidence.getProjectId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertSourceProjectsReadable(List<Long> sourceProjectIds) {
|
|
||||||
if (CollectionUtils.isEmpty(sourceProjectIds)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (Long sourceProjectId : sourceProjectIds) {
|
|
||||||
assertCanRead(sourceProjectId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiProject getRequiredProject(Long projectId) {
|
|
||||||
if (projectId == null) {
|
|
||||||
throw new ServiceException("项目ID不能为空");
|
|
||||||
}
|
|
||||||
CcdiProject project = projectMapper.selectById(projectId);
|
|
||||||
if (project == null) {
|
|
||||||
throw new ServiceException("项目不存在");
|
|
||||||
}
|
|
||||||
return project;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiBankStatement getRequiredBankStatement(Long bankStatementId) {
|
|
||||||
if (bankStatementId == null) {
|
|
||||||
throw new ServiceException("流水ID不能为空");
|
|
||||||
}
|
|
||||||
CcdiBankStatement statement = bankStatementMapper.selectById(bankStatementId);
|
|
||||||
if (statement == null) {
|
|
||||||
throw new ServiceException("流水记录不存在");
|
|
||||||
}
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiFileUploadRecord getRequiredFileRecord(Long fileRecordId) {
|
|
||||||
if (fileRecordId == null) {
|
|
||||||
throw new ServiceException("文件记录ID不能为空");
|
|
||||||
}
|
|
||||||
CcdiFileUploadRecord record = fileUploadRecordMapper.selectById(fileRecordId);
|
|
||||||
if (record == null) {
|
|
||||||
throw new ServiceException("文件记录不存在");
|
|
||||||
}
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiEvidence getRequiredEvidence(Long evidenceId) {
|
|
||||||
if (evidenceId == null) {
|
|
||||||
throw new ServiceException("证据ID不能为空");
|
|
||||||
}
|
|
||||||
CcdiEvidence evidence = evidenceMapper.selectById(evidenceId);
|
|
||||||
if (evidence == null) {
|
|
||||||
throw new ServiceException("证据不存在");
|
|
||||||
}
|
|
||||||
return evidence;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuperAdmin(LoginUser loginUser) {
|
|
||||||
if (loginUser == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (SecurityUtils.isAdmin(loginUser.getUserId())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return hasRole(loginUser, ROLE_ADMIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasRole(LoginUser loginUser, String roleKey) {
|
|
||||||
if (loginUser == null || loginUser.getUser() == null
|
|
||||||
|| CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (SysRole role : loginUser.getUser().getRoles()) {
|
|
||||||
if (roleKey.equals(role.getRoleKey())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.service;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphEdgeDetailQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphManualEdgeSaveDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphEdgeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphStatementVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphVO;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱Service接口
|
|
||||||
*/
|
|
||||||
public interface ICcdiFundGraphService {
|
|
||||||
|
|
||||||
List<CcdiFundGraphNodeVO> searchSubjects(CcdiFundGraphQueryDTO queryDTO);
|
|
||||||
|
|
||||||
CcdiFundGraphVO getFundGraph(CcdiFundGraphQueryDTO queryDTO);
|
|
||||||
|
|
||||||
Page<CcdiFundGraphStatementVO> getEdgeDetails(
|
|
||||||
Page<CcdiFundGraphStatementVO> page,
|
|
||||||
CcdiFundGraphEdgeDetailQueryDTO queryDTO
|
|
||||||
);
|
|
||||||
|
|
||||||
CcdiFundGraphEdgeVO saveManualEdge(CcdiFundGraphManualEdgeSaveDTO saveDTO, String operator);
|
|
||||||
}
|
|
||||||
@@ -2,22 +2,16 @@ package com.ruoyi.ccdi.project.service;
|
|||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalPersonQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalRiskModelPeopleQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectExternalPersonWarningExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskModelPeopleExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountPageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountPageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalRiskSummaryVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalPersonWarningVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
||||||
@@ -88,80 +82,6 @@ public interface ICcdiProjectOverviewService {
|
|||||||
return new CcdiProjectRiskModelPeopleVO();
|
return new CcdiProjectRiskModelPeopleVO();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出风险模型命中人员
|
|
||||||
*
|
|
||||||
* @param queryDTO 查询条件
|
|
||||||
* @return 导出列表
|
|
||||||
*/
|
|
||||||
default List<CcdiProjectRiskModelPeopleExcel> exportRiskModelPeople(CcdiProjectRiskModelPeopleQueryDTO queryDTO) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员预警
|
|
||||||
*
|
|
||||||
* @param queryDTO 查询条件
|
|
||||||
* @return 外部人员预警
|
|
||||||
*/
|
|
||||||
default CcdiProjectExternalPersonWarningVO getExternalPersonWarnings(CcdiProjectExternalPersonQueryDTO queryDTO) {
|
|
||||||
return new CcdiProjectExternalPersonWarningVO();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险等级汇总
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 外部人员风险等级汇总
|
|
||||||
*/
|
|
||||||
default CcdiProjectExternalRiskSummaryVO getExternalRiskSummary(Long projectId) {
|
|
||||||
return new CcdiProjectExternalRiskSummaryVO();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出外部人员预警
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 导出列表
|
|
||||||
*/
|
|
||||||
default List<CcdiProjectExternalPersonWarningExcel> exportExternalPersonWarnings(Long projectId) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险模型卡片
|
|
||||||
*
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @return 风险模型卡片
|
|
||||||
*/
|
|
||||||
default CcdiProjectRiskModelCardsVO getExternalRiskModelCards(Long projectId) {
|
|
||||||
return new CcdiProjectRiskModelCardsVO();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询外部人员风险模型命中人员
|
|
||||||
*
|
|
||||||
* @param queryDTO 查询条件
|
|
||||||
* @return 命中人员
|
|
||||||
*/
|
|
||||||
default CcdiProjectRiskModelPeopleVO getExternalRiskModelPeople(
|
|
||||||
CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO
|
|
||||||
) {
|
|
||||||
return new CcdiProjectRiskModelPeopleVO();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出外部人员风险模型命中人员
|
|
||||||
*
|
|
||||||
* @param queryDTO 查询条件
|
|
||||||
* @return 导出列表
|
|
||||||
*/
|
|
||||||
default List<CcdiProjectRiskModelPeopleExcel> exportExternalRiskModelPeople(
|
|
||||||
CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO
|
|
||||||
) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询涉疑交易明细
|
* 查询涉疑交易明细
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.service;
|
|
||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphSuspectedEnterpriseQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphVO;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱Service接口
|
|
||||||
*/
|
|
||||||
public interface ICcdiRelationGraphService {
|
|
||||||
|
|
||||||
List<CcdiRelationGraphNodeVO> searchSubjects(CcdiRelationGraphQueryDTO queryDTO);
|
|
||||||
|
|
||||||
CcdiRelationGraphVO getRelationGraph(CcdiRelationGraphQueryDTO queryDTO);
|
|
||||||
|
|
||||||
CcdiRelationGraphSuspectedEnterpriseVO getSuspectedEnterprises(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO);
|
|
||||||
}
|
|
||||||
@@ -27,32 +27,20 @@ public class BankTagRuleConfigResolver {
|
|||||||
private static final Map<String, Set<String>> RULE_PARAM_MAPPING = Map.ofEntries(
|
private static final Map<String, Set<String>> RULE_PARAM_MAPPING = Map.ofEntries(
|
||||||
Map.entry("SINGLE_LARGE_INCOME", Set.of("SINGLE_TRANSACTION_AMOUNT")),
|
Map.entry("SINGLE_LARGE_INCOME", Set.of("SINGLE_TRANSACTION_AMOUNT")),
|
||||||
Map.entry("CUMULATIVE_INCOME", Set.of("CUMULATIVE_TRANSACTION_AMOUNT")),
|
Map.entry("CUMULATIVE_INCOME", Set.of("CUMULATIVE_TRANSACTION_AMOUNT")),
|
||||||
Map.entry("EXTERNAL_CUMULATIVE_TRANSACTION_AMOUNT", Set.of("CUMULATIVE_TRANSACTION_AMOUNT")),
|
|
||||||
Map.entry("ANNUAL_TURNOVER", Set.of("ANNUAL_TURNOVER")),
|
Map.entry("ANNUAL_TURNOVER", Set.of("ANNUAL_TURNOVER")),
|
||||||
Map.entry("EXTERNAL_ANNUAL_TURNOVER", Set.of("ANNUAL_TURNOVER")),
|
|
||||||
Map.entry("LARGE_CASH_DEPOSIT", Set.of("LARGE_CASH_DEPOSIT")),
|
Map.entry("LARGE_CASH_DEPOSIT", Set.of("LARGE_CASH_DEPOSIT")),
|
||||||
Map.entry("FREQUENT_CASH_DEPOSIT", Set.of("LARGE_CASH_DEPOSIT", "FREQUENT_CASH_DEPOSIT")),
|
Map.entry("FREQUENT_CASH_DEPOSIT", Set.of("LARGE_CASH_DEPOSIT", "FREQUENT_CASH_DEPOSIT")),
|
||||||
Map.entry("LARGE_TRANSFER", Set.of("FREQUENT_TRANSFER")),
|
Map.entry("LARGE_TRANSFER", Set.of("FREQUENT_TRANSFER")),
|
||||||
Map.entry("EXTERNAL_SINGLE_LARGE_AMOUNT", Set.of("FREQUENT_TRANSFER")),
|
|
||||||
Map.entry("FOREX_BUY_AMT", Set.of("SINGLE_PURCHASE_AMOUNT")),
|
Map.entry("FOREX_BUY_AMT", Set.of("SINGLE_PURCHASE_AMOUNT")),
|
||||||
Map.entry("FOREX_SELL_AMT", Set.of("SINGLE_SETTLEMENT_AMOUNT")),
|
Map.entry("FOREX_SELL_AMT", Set.of("SINGLE_SETTLEMENT_AMOUNT")),
|
||||||
Map.entry("WITHDRAW_CNT", Set.of("WITHDRAW_CNT")),
|
Map.entry("WITHDRAW_CNT", Set.of("WITHDRAW_CNT")),
|
||||||
Map.entry("WITHDRAW_AMT", Set.of("WITHDRAW_AMT")),
|
|
||||||
Map.entry("STOCK_TFR_LARGE", Set.of("STOCK_TFR_LARGE")),
|
Map.entry("STOCK_TFR_LARGE", Set.of("STOCK_TFR_LARGE")),
|
||||||
Map.entry("LARGE_STOCK_TRADING", Set.of("STOCK_TFR_LARGE")),
|
Map.entry("LARGE_STOCK_TRADING", Set.of("STOCK_TFR_LARGE")),
|
||||||
Map.entry("MULTI_PARTY_GAMBLING_TRANSFER", Set.of("MULTI_PARTY_AMT_MIN", "MULTI_PARTY_AMT_MAX")),
|
Map.entry("MULTI_PARTY_GAMBLING_TRANSFER", Set.of("MULTI_PARTY_AMT_MIN", "MULTI_PARTY_AMT_MAX")),
|
||||||
Map.entry("EXTERNAL_MULTI_PARTY_GAMBLING_TRANSFER", Set.of("MULTI_PARTY_AMT_MIN", "MULTI_PARTY_AMT_MAX")),
|
|
||||||
Map.entry("MONTHLY_FIXED_INCOME", Set.of("MONTHLY_FIXED_INCOME")),
|
Map.entry("MONTHLY_FIXED_INCOME", Set.of("MONTHLY_FIXED_INCOME")),
|
||||||
Map.entry("FIXED_COUNTERPARTY_TRANSFER", Set.of("FIXED_COUNTERPARTY_TRANSFER_MIN", "FIXED_COUNTERPARTY_TRANSFER_MAX"))
|
Map.entry("FIXED_COUNTERPARTY_TRANSFER", Set.of("FIXED_COUNTERPARTY_TRANSFER_MIN", "FIXED_COUNTERPARTY_TRANSFER_MAX"))
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final Map<String, String> RULE_PARAM_MODEL_MAPPING = Map.of(
|
|
||||||
"EXTERNAL_SINGLE_LARGE_AMOUNT", "LARGE_TRANSACTION",
|
|
||||||
"EXTERNAL_CUMULATIVE_TRANSACTION_AMOUNT", "LARGE_TRANSACTION",
|
|
||||||
"EXTERNAL_ANNUAL_TURNOVER", "LARGE_TRANSACTION",
|
|
||||||
"EXTERNAL_MULTI_PARTY_GAMBLING_TRANSFER", "SUSPICIOUS_GAMBLING"
|
|
||||||
);
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CcdiProjectMapper projectMapper;
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
@@ -80,13 +68,12 @@ public class BankTagRuleConfigResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Long effectiveProjectId = "default".equals(project.getConfigType()) ? 0L : projectId;
|
Long effectiveProjectId = "default".equals(project.getConfigType()) ? 0L : projectId;
|
||||||
String paramModelCode = RULE_PARAM_MODEL_MAPPING.getOrDefault(ruleMeta.getRuleCode(), ruleMeta.getModelCode());
|
List<CcdiModelParam> params = modelParamMapper.selectByProjectAndModel(effectiveProjectId, ruleMeta.getModelCode());
|
||||||
List<CcdiModelParam> params = modelParamMapper.selectByProjectAndModel(effectiveProjectId, paramModelCode);
|
|
||||||
|
|
||||||
Map<String, String> thresholdValues = new LinkedHashMap<>();
|
Map<String, String> thresholdValues = new LinkedHashMap<>();
|
||||||
Set<String> requiredParamCodes = RULE_PARAM_MAPPING.getOrDefault(ruleMeta.getRuleCode(), Set.of());
|
Set<String> requiredParamCodes = RULE_PARAM_MAPPING.getOrDefault(ruleMeta.getRuleCode(), Set.of());
|
||||||
log.info("【流水标签】解析规则参数: projectId={}, effectiveProjectId={}, ruleCode={}, paramModelCode={}, requiredParams={}",
|
log.info("【流水标签】解析规则参数: projectId={}, effectiveProjectId={}, ruleCode={}, requiredParams={}",
|
||||||
projectId, effectiveProjectId, ruleMeta.getRuleCode(), paramModelCode, requiredParamCodes);
|
projectId, effectiveProjectId, ruleMeta.getRuleCode(), requiredParamCodes);
|
||||||
for (CcdiModelParam param : params) {
|
for (CcdiModelParam param : params) {
|
||||||
if (requiredParamCodes.contains(param.getParamCode())) {
|
if (requiredParamCodes.contains(param.getParamCode())) {
|
||||||
thresholdValues.put(param.getParamCode(), param.getParamValue());
|
thresholdValues.put(param.getParamCode(), param.getParamValue());
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
task.setUpdateBy(operator);
|
task.setUpdateBy(operator);
|
||||||
task.setUpdateTime(new Date());
|
task.setUpdateTime(new Date());
|
||||||
updateFailedTaskSafely(task, ex);
|
updateFailedTaskSafely(task, ex);
|
||||||
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.TAG_FAILED, operator);
|
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.PROCESSING, operator);
|
||||||
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
|
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
|
||||||
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
|
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -225,15 +225,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
case "LARGE_TRANSFER" -> analysisMapper.selectLargeTransferStatements(
|
case "LARGE_TRANSFER" -> analysisMapper.selectLargeTransferStatements(
|
||||||
projectId, toBigDecimal(config.getThresholdValue("FREQUENT_TRANSFER"))
|
projectId, toBigDecimal(config.getThresholdValue("FREQUENT_TRANSFER"))
|
||||||
);
|
);
|
||||||
case "EXTERNAL_SINGLE_LARGE_AMOUNT" -> analysisMapper.selectExternalSingleLargeAmountStatements(
|
|
||||||
projectId, toBigDecimal(config.getThresholdValue("FREQUENT_TRANSFER"))
|
|
||||||
);
|
|
||||||
case "ABNORMAL_CUSTOMER_TRANSACTION" -> analysisMapper.selectAbnormalCustomerTransactionStatements(projectId);
|
case "ABNORMAL_CUSTOMER_TRANSACTION" -> analysisMapper.selectAbnormalCustomerTransactionStatements(projectId);
|
||||||
case "EXTERNAL_NIGHT_TRANSACTION" -> analysisMapper.selectExternalNightTransactionStatements(projectId);
|
|
||||||
case "GAMBLING_SENSITIVE_KEYWORD" -> analysisMapper.selectGamblingSensitiveKeywordStatements(projectId);
|
case "GAMBLING_SENSITIVE_KEYWORD" -> analysisMapper.selectGamblingSensitiveKeywordStatements(projectId);
|
||||||
case "EXTERNAL_GAMBLING_MEMO" -> analysisMapper.selectExternalGamblingMemoStatements(projectId);
|
|
||||||
case "SPECIAL_AMOUNT_TRANSACTION" -> analysisMapper.selectSpecialAmountTransactionStatements(projectId);
|
case "SPECIAL_AMOUNT_TRANSACTION" -> analysisMapper.selectSpecialAmountTransactionStatements(projectId);
|
||||||
case "EXTERNAL_TO_STAFF_FAMILY_TRANSACTION" -> analysisMapper.selectExternalToStaffOrFamilyTransactionStatements(projectId);
|
|
||||||
case "SUSPICIOUS_INCOME_KEYWORD" -> analysisMapper.selectSuspiciousIncomeKeywordStatements(projectId);
|
case "SUSPICIOUS_INCOME_KEYWORD" -> analysisMapper.selectSuspiciousIncomeKeywordStatements(projectId);
|
||||||
case "HOUSE_REGISTRATION_MISMATCH" -> analysisMapper.selectHouseRegistrationMismatchStatements(projectId);
|
case "HOUSE_REGISTRATION_MISMATCH" -> analysisMapper.selectHouseRegistrationMismatchStatements(projectId);
|
||||||
case "PROPERTY_FEE_REGISTRATION_MISMATCH" -> analysisMapper.selectPropertyFeeRegistrationMismatchStatements(projectId);
|
case "PROPERTY_FEE_REGISTRATION_MISMATCH" -> analysisMapper.selectPropertyFeeRegistrationMismatchStatements(projectId);
|
||||||
@@ -264,15 +258,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
case "CUMULATIVE_INCOME" -> analysisMapper.selectCumulativeIncomeObjects(
|
case "CUMULATIVE_INCOME" -> analysisMapper.selectCumulativeIncomeObjects(
|
||||||
projectId, toBigDecimal(config.getThresholdValue("CUMULATIVE_TRANSACTION_AMOUNT"))
|
projectId, toBigDecimal(config.getThresholdValue("CUMULATIVE_TRANSACTION_AMOUNT"))
|
||||||
);
|
);
|
||||||
case "EXTERNAL_CUMULATIVE_TRANSACTION_AMOUNT" -> analysisMapper.selectExternalCumulativeTransactionAmountObjects(
|
|
||||||
projectId, toBigDecimal(config.getThresholdValue("CUMULATIVE_TRANSACTION_AMOUNT"))
|
|
||||||
);
|
|
||||||
case "ANNUAL_TURNOVER" -> analysisMapper.selectAnnualTurnoverObjects(
|
case "ANNUAL_TURNOVER" -> analysisMapper.selectAnnualTurnoverObjects(
|
||||||
projectId, toBigDecimal(config.getThresholdValue("ANNUAL_TURNOVER"))
|
projectId, toBigDecimal(config.getThresholdValue("ANNUAL_TURNOVER"))
|
||||||
);
|
);
|
||||||
case "EXTERNAL_ANNUAL_TURNOVER" -> analysisMapper.selectExternalAnnualTurnoverObjects(
|
|
||||||
projectId, toBigDecimal(config.getThresholdValue("ANNUAL_TURNOVER"))
|
|
||||||
);
|
|
||||||
case "FREQUENT_CASH_DEPOSIT" -> analysisMapper.selectFrequentCashDepositObjects(
|
case "FREQUENT_CASH_DEPOSIT" -> analysisMapper.selectFrequentCashDepositObjects(
|
||||||
projectId,
|
projectId,
|
||||||
toBigDecimal(config.getThresholdValue("LARGE_CASH_DEPOSIT")),
|
toBigDecimal(config.getThresholdValue("LARGE_CASH_DEPOSIT")),
|
||||||
@@ -284,11 +272,6 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
toBigDecimal(config.getThresholdValue("MULTI_PARTY_AMT_MIN")),
|
toBigDecimal(config.getThresholdValue("MULTI_PARTY_AMT_MIN")),
|
||||||
toBigDecimal(config.getThresholdValue("MULTI_PARTY_AMT_MAX"))
|
toBigDecimal(config.getThresholdValue("MULTI_PARTY_AMT_MAX"))
|
||||||
);
|
);
|
||||||
case "EXTERNAL_MULTI_PARTY_GAMBLING_TRANSFER" -> analysisMapper.selectExternalMultiPartyGamblingTransferObjects(
|
|
||||||
projectId,
|
|
||||||
toBigDecimal(config.getThresholdValue("MULTI_PARTY_AMT_MIN")),
|
|
||||||
toBigDecimal(config.getThresholdValue("MULTI_PARTY_AMT_MAX"))
|
|
||||||
);
|
|
||||||
case "MONTHLY_FIXED_INCOME" -> analysisMapper.selectMonthlyFixedIncomeObjects(
|
case "MONTHLY_FIXED_INCOME" -> analysisMapper.selectMonthlyFixedIncomeObjects(
|
||||||
projectId, toBigDecimal(config.getThresholdValue("MONTHLY_FIXED_INCOME"))
|
projectId, toBigDecimal(config.getThresholdValue("MONTHLY_FIXED_INCOME"))
|
||||||
);
|
);
|
||||||
@@ -302,9 +285,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
|||||||
case "WITHDRAW_CNT" -> analysisMapper.selectWithdrawCntObjects(
|
case "WITHDRAW_CNT" -> analysisMapper.selectWithdrawCntObjects(
|
||||||
projectId, toInteger(config.getThresholdValue("WITHDRAW_CNT"))
|
projectId, toInteger(config.getThresholdValue("WITHDRAW_CNT"))
|
||||||
);
|
);
|
||||||
case "WITHDRAW_AMT" -> analysisMapper.selectWithdrawAmtObjects(
|
case "WITHDRAW_AMT" -> analysisMapper.selectWithdrawAmtObjects(projectId);
|
||||||
projectId, toBigDecimal(config.getThresholdValue("WITHDRAW_AMT"))
|
|
||||||
);
|
|
||||||
case "SALARY_QUICK_TRANSFER" -> analysisMapper.selectSalaryQuickTransferObjects(projectId);
|
case "SALARY_QUICK_TRANSFER" -> analysisMapper.selectSalaryQuickTransferObjects(projectId);
|
||||||
case "SALARY_UNUSED" -> analysisMapper.selectSalaryUnusedObjects(projectId);
|
case "SALARY_UNUSED" -> analysisMapper.selectSalaryUnusedObjects(projectId);
|
||||||
case "SUDDEN_ACCOUNT_CLOSURE" -> analysisMapper.selectSuddenAccountClosureObjects(projectId);
|
case "SUDDEN_ACCOUNT_CLOSURE" -> analysisMapper.selectSuddenAccountClosureObjects(projectId);
|
||||||
|
|||||||
@@ -226,18 +226,15 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
CcdiFileUploadRecord record = recordMapper.selectById(id);
|
CcdiFileUploadRecord record = recordMapper.selectById(id);
|
||||||
validateDeleteRecord(record);
|
validateDeleteRecord(record);
|
||||||
|
|
||||||
/*
|
DeleteFilesRequest request = new DeleteFilesRequest();
|
||||||
* 按当前要求,项目管理-上传数据页面删除时先不调用流水分析平台删除接口。
|
request.setGroupId(record.getLsfxProjectId());
|
||||||
* DeleteFilesRequest request = new DeleteFilesRequest();
|
request.setLogIds(new Integer[]{record.getLogId()});
|
||||||
* request.setGroupId(record.getLsfxProjectId());
|
request.setUserId(toUploadUserId(operatorUserId));
|
||||||
* request.setLogIds(new Integer[]{record.getLogId()});
|
|
||||||
* request.setUserId(toUploadUserId(operatorUserId));
|
DeleteFilesResponse response = lsfxClient.deleteFiles(request);
|
||||||
*
|
if (response == null || Boolean.FALSE.equals(response.getSuccessResponse())) {
|
||||||
* DeleteFilesResponse response = lsfxClient.deleteFiles(request);
|
throw new RuntimeException("流水分析平台删除文件失败");
|
||||||
* if (response == null || Boolean.FALSE.equals(response.getSuccessResponse())) {
|
}
|
||||||
* throw new RuntimeException("流水分析平台删除文件失败");
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
bankStatementMapper.deleteByProjectIdAndBatchId(record.getProjectId(), record.getLogId());
|
bankStatementMapper.deleteByProjectIdAndBatchId(record.getProjectId(), record.getLogId());
|
||||||
refreshProjectTargetCount(record.getProjectId());
|
refreshProjectTargetCount(record.getProjectId());
|
||||||
|
|||||||
@@ -1,383 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.service.impl;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphEdgeDetailQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphManualEdgeSaveDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiFundGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphEdgeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphStatementVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphVO;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiFundGraphMapper;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiFundGraphService;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.DigestUtils;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资金流图谱Service实现
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class CcdiFundGraphServiceImpl implements ICcdiFundGraphService {
|
|
||||||
|
|
||||||
private static final int DEFAULT_LIMIT = 20;
|
|
||||||
private static final int MAX_LIMIT = 100;
|
|
||||||
private static final BigDecimal DEFAULT_MIN_TOTAL_AMOUNT = new BigDecimal("1000");
|
|
||||||
private static final Comparator<CcdiFundGraphEdgeVO> EDGE_COMPARATOR = Comparator
|
|
||||||
.comparing(CcdiFundGraphServiceImpl::safeAmount, Comparator.reverseOrder())
|
|
||||||
.thenComparing(CcdiFundGraphServiceImpl::safeTransactionCount, Comparator.reverseOrder())
|
|
||||||
.thenComparing(CcdiFundGraphServiceImpl::safeDateText, Comparator.reverseOrder())
|
|
||||||
.thenComparing(edge -> normalizeSortText(edge == null ? null : edge.getEdgeKey()));
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiFundGraphMapper fundGraphMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<CcdiFundGraphNodeVO> searchSubjects(CcdiFundGraphQueryDTO queryDTO) {
|
|
||||||
CcdiFundGraphQueryDTO query = normalizeGraphQuery(queryDTO);
|
|
||||||
if (isBlank(query.getKeyword()) && isBlank(query.getObjectKey())) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return selectSubjects(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiFundGraphVO getFundGraph(CcdiFundGraphQueryDTO queryDTO) {
|
|
||||||
CcdiFundGraphQueryDTO query = normalizeGraphQuery(queryDTO);
|
|
||||||
CcdiFundGraphNodeVO centerNode = resolveCenterNode(query);
|
|
||||||
if (centerNode == null || isBlank(centerNode.getObjectKey())) {
|
|
||||||
return new CcdiFundGraphVO();
|
|
||||||
}
|
|
||||||
|
|
||||||
query.setObjectKey(centerNode.getObjectKey());
|
|
||||||
List<CcdiFundGraphEdgeVO> edges = new ArrayList<>();
|
|
||||||
List<CcdiFundGraphEdgeVO> realEdges = fundGraphMapper.selectFundGraphEdges(query);
|
|
||||||
if (realEdges != null) {
|
|
||||||
edges.addAll(realEdges);
|
|
||||||
}
|
|
||||||
List<CcdiFundGraphEdgeVO> manualEdges = fundGraphMapper.selectFundGraphManualEdges(query);
|
|
||||||
if (manualEdges != null) {
|
|
||||||
edges.addAll(manualEdges);
|
|
||||||
}
|
|
||||||
edges = sortAndLimitEdges(edges, query.getLimit());
|
|
||||||
CcdiFundGraphVO graph = new CcdiFundGraphVO();
|
|
||||||
graph.setCenterNode(centerNode);
|
|
||||||
graph.setEdges(edges);
|
|
||||||
graph.setNodes(buildNodes(centerNode, edges));
|
|
||||||
graph.setTransactionCount(edges.stream()
|
|
||||||
.map(CcdiFundGraphEdgeVO::getTransactionCount)
|
|
||||||
.filter(item -> item != null)
|
|
||||||
.reduce(0L, Long::sum));
|
|
||||||
graph.setTotalAmount(edges.stream()
|
|
||||||
.map(CcdiFundGraphEdgeVO::getTotalAmount)
|
|
||||||
.filter(item -> item != null)
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add));
|
|
||||||
graph.setMaxDepth(1);
|
|
||||||
graph.setTraceReserved(true);
|
|
||||||
return graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Page<CcdiFundGraphStatementVO> getEdgeDetails(
|
|
||||||
Page<CcdiFundGraphStatementVO> page,
|
|
||||||
CcdiFundGraphEdgeDetailQueryDTO queryDTO
|
|
||||||
) {
|
|
||||||
CcdiFundGraphEdgeDetailQueryDTO query = normalizeDetailQuery(queryDTO);
|
|
||||||
if (isBlank(query.getFromKey()) || isBlank(query.getToKey())) {
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
return fundGraphMapper.selectFundGraphEdgeDetails(page, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiFundGraphEdgeVO saveManualEdge(CcdiFundGraphManualEdgeSaveDTO saveDTO, String operator) {
|
|
||||||
CcdiFundGraphManualEdgeSaveDTO dto = normalizeManualEdge(saveDTO);
|
|
||||||
String fromObjectKey = dto.getFromObjectKey();
|
|
||||||
String toObjectKey = resolveManualToObjectKey(dto);
|
|
||||||
dto.setToObjectKey(toObjectKey);
|
|
||||||
|
|
||||||
String edgeObjectKey = md5("MANUAL_EDGE|" + fromObjectKey + "|" + toObjectKey + "|" + dto.getDirection()
|
|
||||||
+ "|" + UUID.randomUUID());
|
|
||||||
fundGraphMapper.insertManualEdge(edgeObjectKey, dto, normalizeText(operator));
|
|
||||||
|
|
||||||
CcdiFundGraphEdgeVO edge = new CcdiFundGraphEdgeVO();
|
|
||||||
edge.setEdgeKey(edgeObjectKey);
|
|
||||||
edge.setFromKey(toSubjectKey(fromObjectKey));
|
|
||||||
edge.setToKey(toSubjectKey(toObjectKey));
|
|
||||||
edge.setFromObjectKey(fromObjectKey);
|
|
||||||
edge.setToObjectKey(toObjectKey);
|
|
||||||
edge.setFromName(dto.getFromName());
|
|
||||||
edge.setToName(dto.getToName());
|
|
||||||
edge.setTotalAmount(dto.getAmount());
|
|
||||||
edge.setTransactionCount(dto.getTransactionCount() == null ? 1L : dto.getTransactionCount().longValue());
|
|
||||||
edge.setDirection(dto.getDirection());
|
|
||||||
edge.setRelationDesc(dto.getRelationDesc());
|
|
||||||
edge.setSourceDesc(dto.getSourceDesc());
|
|
||||||
edge.setRemark(dto.getRemark());
|
|
||||||
edge.setSourceType("MANUAL");
|
|
||||||
edge.setDepth(1);
|
|
||||||
edge.setCanTrace(false);
|
|
||||||
return edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiFundGraphNodeVO resolveCenterNode(CcdiFundGraphQueryDTO query) {
|
|
||||||
if (isBlank(query.getObjectKey()) && isBlank(query.getKeyword())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
List<CcdiFundGraphNodeVO> subjects = selectSubjects(query);
|
|
||||||
if (subjects == null || subjects.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return subjects.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CcdiFundGraphNodeVO> selectSubjects(CcdiFundGraphQueryDTO query) {
|
|
||||||
if (query == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
if (!isBlank(query.getObjectKey())) {
|
|
||||||
return fundGraphMapper.selectFundGraphSubjects(query);
|
|
||||||
}
|
|
||||||
if (isBlank(query.getKeyword())) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
List<CcdiFundGraphNodeVO> exactSubjects = fundGraphMapper.selectFundGraphSubjectsByExactKeyword(query);
|
|
||||||
if (exactSubjects != null && !exactSubjects.isEmpty()) {
|
|
||||||
return exactSubjects;
|
|
||||||
}
|
|
||||||
return fundGraphMapper.selectFundGraphSubjectsByName(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CcdiFundGraphNodeVO> buildNodes(CcdiFundGraphNodeVO centerNode, List<CcdiFundGraphEdgeVO> edges) {
|
|
||||||
Map<String, CcdiFundGraphNodeVO> nodeMap = new LinkedHashMap<>();
|
|
||||||
Map<String, CcdiFundGraphNodeVO> subjectCache = new LinkedHashMap<>();
|
|
||||||
subjectCache.put(centerNode.getObjectKey(), centerNode);
|
|
||||||
addNode(nodeMap, centerNode, centerNode.getNodeKey(), centerNode.getObjectKey(), centerNode.getNodeName(),
|
|
||||||
centerNode.getRelationType(), centerNode.getCanExpand(), BigDecimal.ZERO, 0L);
|
|
||||||
|
|
||||||
for (CcdiFundGraphEdgeVO edge : edges) {
|
|
||||||
String centerObjectKey = centerNode.getObjectKey();
|
|
||||||
String fromRelationType = centerObjectKey != null && centerObjectKey.equals(edge.getFromObjectKey())
|
|
||||||
? null
|
|
||||||
: edge.getFamilyRelationType();
|
|
||||||
String toRelationType = centerObjectKey != null && centerObjectKey.equals(edge.getToObjectKey())
|
|
||||||
? null
|
|
||||||
: edge.getFamilyRelationType();
|
|
||||||
addNode(nodeMap, lookupSubject(edge.getFromObjectKey(), subjectCache), edge.getFromKey(),
|
|
||||||
edge.getFromObjectKey(), edge.getFromName(), fromRelationType, true, edge.getTotalAmount(),
|
|
||||||
edge.getTransactionCount());
|
|
||||||
addNode(nodeMap, lookupSubject(edge.getToObjectKey(), subjectCache), edge.getToKey(),
|
|
||||||
edge.getToObjectKey(), edge.getToName(), toRelationType, edge.getCanTrace(),
|
|
||||||
edge.getTotalAmount(), edge.getTransactionCount());
|
|
||||||
}
|
|
||||||
return List.copyOf(nodeMap.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiFundGraphNodeVO lookupSubject(String objectKey, Map<String, CcdiFundGraphNodeVO> subjectCache) {
|
|
||||||
if (isBlank(objectKey)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (subjectCache.containsKey(objectKey)) {
|
|
||||||
return subjectCache.get(objectKey);
|
|
||||||
}
|
|
||||||
CcdiFundGraphQueryDTO query = new CcdiFundGraphQueryDTO();
|
|
||||||
query.setObjectKey(objectKey);
|
|
||||||
query.setLimit(DEFAULT_LIMIT);
|
|
||||||
List<CcdiFundGraphNodeVO> subjects = fundGraphMapper.selectFundGraphSubjects(query);
|
|
||||||
CcdiFundGraphNodeVO subject = subjects == null || subjects.isEmpty() ? null : subjects.get(0);
|
|
||||||
subjectCache.put(objectKey, subject);
|
|
||||||
return subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveManualToObjectKey(CcdiFundGraphManualEdgeSaveDTO dto) {
|
|
||||||
if (!isBlank(dto.getToObjectKey())) {
|
|
||||||
ensureManualSubject(dto.getToObjectKey(), dto.getToIdNo(), dto.getToName());
|
|
||||||
return dto.getToObjectKey();
|
|
||||||
}
|
|
||||||
String objectKey = !isBlank(dto.getToIdNo())
|
|
||||||
? md5(dto.getToIdNo())
|
|
||||||
: md5("MANUAL_NODE|" + dto.getToName() + "|" + UUID.randomUUID());
|
|
||||||
dto.setToObjectKey(objectKey);
|
|
||||||
ensureManualSubject(objectKey, dto.getToIdNo(), dto.getToName());
|
|
||||||
return objectKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureManualSubject(String objectKey, String idNo, String name) {
|
|
||||||
if (fundGraphMapper.countSubjectByObjectKey(objectKey) > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fundGraphMapper.insertManualSubject(objectKey, normalizeText(idNo), normalizeText(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiFundGraphManualEdgeSaveDTO normalizeManualEdge(CcdiFundGraphManualEdgeSaveDTO saveDTO) {
|
|
||||||
CcdiFundGraphManualEdgeSaveDTO dto = saveDTO == null ? new CcdiFundGraphManualEdgeSaveDTO() : saveDTO;
|
|
||||||
dto.setFromObjectKey(normalizeText(dto.getFromObjectKey()));
|
|
||||||
dto.setFromName(normalizeText(dto.getFromName()));
|
|
||||||
dto.setToObjectKey(normalizeText(dto.getToObjectKey()));
|
|
||||||
dto.setToName(normalizeText(dto.getToName()));
|
|
||||||
dto.setToIdNo(normalizeText(dto.getToIdNo()));
|
|
||||||
dto.setDirection(normalizeText(dto.getDirection()));
|
|
||||||
dto.setRelationDesc(normalizeText(dto.getRelationDesc()));
|
|
||||||
dto.setSourceDesc(normalizeText(dto.getSourceDesc()));
|
|
||||||
dto.setRemark(normalizeText(dto.getRemark()));
|
|
||||||
if (isBlank(dto.getFromObjectKey())) {
|
|
||||||
throw new IllegalArgumentException("起点主体不能为空");
|
|
||||||
}
|
|
||||||
if (isBlank(dto.getToObjectKey()) && isBlank(dto.getToName())) {
|
|
||||||
throw new IllegalArgumentException("终点主体不能为空");
|
|
||||||
}
|
|
||||||
if (isBlank(dto.getDirection())) {
|
|
||||||
dto.setDirection("1");
|
|
||||||
}
|
|
||||||
if (dto.getAmount() == null) {
|
|
||||||
dto.setAmount(BigDecimal.ZERO);
|
|
||||||
}
|
|
||||||
if (dto.getTransactionCount() == null || dto.getTransactionCount() <= 0) {
|
|
||||||
dto.setTransactionCount(1);
|
|
||||||
}
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNode(
|
|
||||||
Map<String, CcdiFundGraphNodeVO> nodeMap,
|
|
||||||
CcdiFundGraphNodeVO subject,
|
|
||||||
String nodeKey,
|
|
||||||
String objectKey,
|
|
||||||
String nodeName,
|
|
||||||
String relationType,
|
|
||||||
Boolean canExpand,
|
|
||||||
BigDecimal edgeAmount,
|
|
||||||
Long edgeCount
|
|
||||||
) {
|
|
||||||
if (isBlank(nodeKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CcdiFundGraphNodeVO node = nodeMap.computeIfAbsent(nodeKey, key -> {
|
|
||||||
CcdiFundGraphNodeVO item = new CcdiFundGraphNodeVO();
|
|
||||||
item.setNodeKey(key);
|
|
||||||
item.setObjectKey(objectKey);
|
|
||||||
item.setNodeName(subject != null && !isBlank(subject.getNodeName())
|
|
||||||
? subject.getNodeName()
|
|
||||||
: (isBlank(nodeName) ? "未知主体" : nodeName));
|
|
||||||
item.setIdNo(subject == null ? null : subject.getIdNo());
|
|
||||||
item.setCinocsno(subject == null ? null : subject.getCinocsno());
|
|
||||||
item.setIdnoType(subject == null ? null : subject.getIdnoType());
|
|
||||||
item.setStaffId(subject == null ? null : subject.getStaffId());
|
|
||||||
item.setSourceType(subject == null ? null : subject.getSourceType());
|
|
||||||
item.setNodeType(subject != null && !isBlank(subject.getNodeType()) ? subject.getNodeType() : "PERSON");
|
|
||||||
item.setIdentityType(subject != null && !isBlank(subject.getIdentityType()) ? subject.getIdentityType() : "IDNO");
|
|
||||||
item.setRelationType(relationType);
|
|
||||||
item.setAccountCount(subject == null ? 0L : subject.getAccountCount());
|
|
||||||
item.setCreatedTime(subject == null ? null : subject.getCreatedTime());
|
|
||||||
item.setUpdatedTime(subject == null ? null : subject.getUpdatedTime());
|
|
||||||
item.setCanExpand(Boolean.TRUE.equals(canExpand));
|
|
||||||
item.setDepth(1);
|
|
||||||
item.setTotalAmount(BigDecimal.ZERO);
|
|
||||||
item.setTransactionCount(0L);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
if (isBlank(node.getObjectKey())) {
|
|
||||||
node.setObjectKey(objectKey);
|
|
||||||
}
|
|
||||||
if (isBlank(node.getRelationType())) {
|
|
||||||
node.setRelationType(relationType);
|
|
||||||
}
|
|
||||||
if (Boolean.TRUE.equals(canExpand)) {
|
|
||||||
node.setCanExpand(true);
|
|
||||||
}
|
|
||||||
node.setTotalAmount(node.getTotalAmount().add(edgeAmount == null ? BigDecimal.ZERO : edgeAmount));
|
|
||||||
node.setTransactionCount(node.getTransactionCount() + (edgeCount == null ? 0L : edgeCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiFundGraphQueryDTO normalizeGraphQuery(CcdiFundGraphQueryDTO queryDTO) {
|
|
||||||
CcdiFundGraphQueryDTO query = queryDTO == null ? new CcdiFundGraphQueryDTO() : queryDTO;
|
|
||||||
query.setKeyword(normalizeText(query.getKeyword()));
|
|
||||||
query.setObjectKey(normalizeText(query.getObjectKey()));
|
|
||||||
query.setTransactionStartTime(normalizeText(query.getTransactionStartTime()));
|
|
||||||
query.setTransactionEndTime(normalizeText(query.getTransactionEndTime()));
|
|
||||||
query.setDirection(normalizeText(query.getDirection()));
|
|
||||||
if (query.getMinTotalAmount() == null) {
|
|
||||||
query.setMinTotalAmount(DEFAULT_MIN_TOTAL_AMOUNT);
|
|
||||||
}
|
|
||||||
query.setLimit(normalizeLimit(query.getLimit()));
|
|
||||||
query.setDepth(1);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiFundGraphEdgeDetailQueryDTO normalizeDetailQuery(CcdiFundGraphEdgeDetailQueryDTO queryDTO) {
|
|
||||||
CcdiFundGraphEdgeDetailQueryDTO query = queryDTO == null ? new CcdiFundGraphEdgeDetailQueryDTO() : queryDTO;
|
|
||||||
query.setKeyword(normalizeText(query.getKeyword()));
|
|
||||||
query.setFromKey(normalizeText(query.getFromKey()));
|
|
||||||
query.setToKey(normalizeText(query.getToKey()));
|
|
||||||
query.setDirection(normalizeText(query.getDirection()));
|
|
||||||
query.setTransactionStartTime(normalizeText(query.getTransactionStartTime()));
|
|
||||||
query.setTransactionEndTime(normalizeText(query.getTransactionEndTime()));
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer normalizeLimit(Integer limit) {
|
|
||||||
if (limit == null || limit <= 0) {
|
|
||||||
return DEFAULT_LIMIT;
|
|
||||||
}
|
|
||||||
return Math.min(limit, MAX_LIMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CcdiFundGraphEdgeVO> sortAndLimitEdges(List<CcdiFundGraphEdgeVO> edges, Integer limit) {
|
|
||||||
if (edges == null || edges.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
List<CcdiFundGraphEdgeVO> sorted = new ArrayList<>(edges);
|
|
||||||
sorted.sort(EDGE_COMPARATOR);
|
|
||||||
int finalLimit = normalizeLimit(limit);
|
|
||||||
if (sorted.size() > finalLimit) {
|
|
||||||
return List.copyOf(sorted.subList(0, finalLimit));
|
|
||||||
}
|
|
||||||
return List.copyOf(sorted);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String normalizeText(String value) {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String trimmed = value.trim();
|
|
||||||
return trimmed.isEmpty() ? null : trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBlank(String value) {
|
|
||||||
return value == null || value.trim().isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toSubjectKey(String objectKey) {
|
|
||||||
return "idno_node/" + objectKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String md5(String value) {
|
|
||||||
return DigestUtils.md5DigestAsHex(value.trim().getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BigDecimal safeAmount(CcdiFundGraphEdgeVO edge) {
|
|
||||||
return edge == null || edge.getTotalAmount() == null ? BigDecimal.ZERO : edge.getTotalAmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Long safeTransactionCount(CcdiFundGraphEdgeVO edge) {
|
|
||||||
return edge == null || edge.getTransactionCount() == null ? 0L : edge.getTransactionCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String safeDateText(CcdiFundGraphEdgeVO edge) {
|
|
||||||
return normalizeSortText(edge == null ? null : edge.getLastTrxDate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String normalizeSortText(String value) {
|
|
||||||
return value == null ? "" : value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,6 @@ import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -54,7 +53,6 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
|
|||||||
private ICcdiProjectService projectService;
|
private ICcdiProjectService projectService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy
|
|
||||||
private ICcdiBankTagService bankTagService;
|
private ICcdiBankTagService bankTagService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.ruoyi.ccdi.project.service.impl;
|
|||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectExternalPersonWarningExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportParamVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportParamVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportSuspiciousTransactionVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportSuspiciousTransactionVO;
|
||||||
@@ -66,7 +65,7 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
writeCover(writer, report);
|
writeCover(writer, report);
|
||||||
writeUploadSubjects(writer, report.getUploadSubjects());
|
writeUploadSubjects(writer, report.getUploadSubjects());
|
||||||
writeParams(writer, report.getParams());
|
writeParams(writer, report.getParams());
|
||||||
writeRiskOverview(writer, report);
|
writeRiskModels(writer, report);
|
||||||
writeRiskDetails(writer, report);
|
writeRiskDetails(writer, report);
|
||||||
writer.close();
|
writer.close();
|
||||||
document.save(response.getOutputStream());
|
document.save(response.getOutputStream());
|
||||||
@@ -119,9 +118,9 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeRiskOverview(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
|
private void writeRiskModels(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
|
||||||
writer.section("三、风险总览");
|
writer.section("三、风险模型");
|
||||||
writer.metrics(buildOverallRiskMetrics(report));
|
writer.metrics(report.getDashboard().getStats());
|
||||||
writer.subsection("风险模型汇总");
|
writer.subsection("风险模型汇总");
|
||||||
writer.table(
|
writer.table(
|
||||||
List.of("模型名称", "预警数量", "涉及人员"),
|
List.of("模型名称", "预警数量", "涉及人员"),
|
||||||
@@ -152,40 +151,6 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
new float[] { 0.1F, 0.11F, 0.16F, 0.14F, 0.24F, 0.25F },
|
new float[] { 0.1F, 0.11F, 0.16F, 0.14F, 0.24F, 0.25F },
|
||||||
"暂无风险人员与异常点数据"
|
"暂无风险人员与异常点数据"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasExternalRisk(report)) {
|
|
||||||
writer.subsection("外部人员预警");
|
|
||||||
writer.metrics(buildExternalMetrics(report));
|
|
||||||
writer.table(
|
|
||||||
List.of("外部模型", "预警数量", "涉及人数"),
|
|
||||||
report.getExternalModelSummaries().stream()
|
|
||||||
.map(item -> List.of(
|
|
||||||
safeText(item.getModelName()),
|
|
||||||
String.valueOf(defaultZero(item.getWarningCount())),
|
|
||||||
formatCount(item.getPeopleCount(), "人")
|
|
||||||
))
|
|
||||||
.collect(Collectors.toList()),
|
|
||||||
new float[] { 0.5F, 0.2F, 0.3F },
|
|
||||||
"暂无外部人员模型汇总数据"
|
|
||||||
);
|
|
||||||
writer.table(
|
|
||||||
List.of("姓名", "证件号", "主体类型", "风险等级", "命中模型数", "核心异常点", "涉及对象", "最近交易时间"),
|
|
||||||
report.getExternalPersonWarnings().stream()
|
|
||||||
.map(item -> List.of(
|
|
||||||
safeText(item.getName()),
|
|
||||||
maskIdCard(item.getIdNo()),
|
|
||||||
safeText(item.getSubjectType()),
|
|
||||||
safeText(item.getRiskLevel()),
|
|
||||||
String.valueOf(defaultZero(item.getModelCount())),
|
|
||||||
safeText(item.getRiskPoint()),
|
|
||||||
safeText(item.getRelatedObject()),
|
|
||||||
safeText(item.getLatestTradeTime())
|
|
||||||
))
|
|
||||||
.collect(Collectors.toList()),
|
|
||||||
new float[] { 0.09F, 0.15F, 0.1F, 0.09F, 0.1F, 0.25F, 0.12F, 0.1F },
|
|
||||||
"暂无外部人员预警数据"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeRiskDetails(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
|
private void writeRiskDetails(PdfPageWriter writer, CcdiProjectOverviewReportVO report) throws IOException {
|
||||||
@@ -363,69 +328,6 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<CcdiProjectOverviewStatVO> buildOverallRiskMetrics(CcdiProjectOverviewReportVO report) {
|
|
||||||
List<CcdiProjectOverviewStatVO> employeeStats = report.getDashboard().getStats();
|
|
||||||
if (employeeStats == null || employeeStats.isEmpty()) {
|
|
||||||
return List.of(
|
|
||||||
buildMetric("总人数", report.getExternalRiskSummary().getTotal()),
|
|
||||||
buildMetric("高风险", report.getExternalRiskSummary().getHigh()),
|
|
||||||
buildMetric("中风险", report.getExternalRiskSummary().getMedium()),
|
|
||||||
buildMetric("低风险", report.getExternalRiskSummary().getLow()),
|
|
||||||
buildMetric("无风险", report.getExternalRiskSummary().getNoRisk())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
int employeeTotal = metricValue(employeeStats, "people");
|
|
||||||
int high = metricValue(employeeStats, "riskPeople");
|
|
||||||
int medium = metricValue(employeeStats, "medium");
|
|
||||||
int low = metricValue(employeeStats, "low");
|
|
||||||
int noRisk = metricValue(employeeStats, "count");
|
|
||||||
int externalTotal = defaultZero(report.getExternalRiskSummary().getTotal());
|
|
||||||
int externalHigh = defaultZero(report.getExternalRiskSummary().getHigh());
|
|
||||||
int externalMedium = defaultZero(report.getExternalRiskSummary().getMedium());
|
|
||||||
int externalLow = defaultZero(report.getExternalRiskSummary().getLow());
|
|
||||||
int externalNoRisk = defaultZero(report.getExternalRiskSummary().getNoRisk());
|
|
||||||
return List.of(
|
|
||||||
buildMetric("总人数", employeeTotal + externalTotal),
|
|
||||||
buildMetric("高风险", high + externalHigh),
|
|
||||||
buildMetric("中风险", medium + externalMedium),
|
|
||||||
buildMetric("低风险", low + externalLow),
|
|
||||||
buildMetric("无风险", noRisk + externalNoRisk)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer metricValue(List<CcdiProjectOverviewStatVO> stats, String key) {
|
|
||||||
return defaultZero(stats.stream()
|
|
||||||
.filter(stat -> key.equals(stat.getKey()))
|
|
||||||
.findFirst()
|
|
||||||
.map(CcdiProjectOverviewStatVO::getValue)
|
|
||||||
.orElse(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasExternalRisk(CcdiProjectOverviewReportVO report) {
|
|
||||||
return defaultZero(report.getExternalRiskSummary().getHigh()) > 0
|
|
||||||
|| defaultZero(report.getExternalRiskSummary().getMedium()) > 0
|
|
||||||
|| defaultZero(report.getExternalRiskSummary().getLow()) > 0
|
|
||||||
|| !report.getExternalModelSummaries().isEmpty()
|
|
||||||
|| !report.getExternalPersonWarnings().isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CcdiProjectOverviewStatVO> buildExternalMetrics(CcdiProjectOverviewReportVO report) {
|
|
||||||
return List.of(
|
|
||||||
buildMetric("外部人员", report.getExternalRiskSummary().getTotal()),
|
|
||||||
buildMetric("高风险", report.getExternalRiskSummary().getHigh()),
|
|
||||||
buildMetric("中风险", report.getExternalRiskSummary().getMedium()),
|
|
||||||
buildMetric("低风险", report.getExternalRiskSummary().getLow()),
|
|
||||||
buildMetric("无风险人员", report.getExternalRiskSummary().getNoRisk())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiProjectOverviewStatVO buildMetric(String label, Integer value) {
|
|
||||||
CcdiProjectOverviewStatVO stat = new CcdiProjectOverviewStatVO();
|
|
||||||
stat.setLabel(label);
|
|
||||||
stat.setValue(defaultZero(value));
|
|
||||||
return stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String formatPeopleSummary(CcdiProjectOverviewReportModelSummaryVO item) {
|
private String formatPeopleSummary(CcdiProjectOverviewReportModelSummaryVO item) {
|
||||||
String names = safeText(item.getPeopleNames());
|
String names = safeText(item.getPeopleNames());
|
||||||
if ("-".equals(names)) {
|
if ("-".equals(names)) {
|
||||||
@@ -567,7 +469,6 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
private static final float SUBSECTION_FONT_SIZE = 12F;
|
private static final float SUBSECTION_FONT_SIZE = 12F;
|
||||||
private static final float LINE_HEIGHT = 12F;
|
private static final float LINE_HEIGHT = 12F;
|
||||||
private static final float CELL_PADDING = 5F;
|
private static final float CELL_PADDING = 5F;
|
||||||
private static final float TABLE_AFTER_GAP = 32F;
|
|
||||||
|
|
||||||
private final PDDocument document;
|
private final PDDocument document;
|
||||||
private final PDType0Font font;
|
private final PDType0Font font;
|
||||||
@@ -595,7 +496,7 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void title(String text) throws IOException {
|
void title(String text) throws IOException {
|
||||||
writeLine(text, TITLE_FONT_SIZE, new Color(18, 56, 93), 0F, 28F, true);
|
writeLine(text, TITLE_FONT_SIZE, new Color(18, 56, 93), 0F, 28F);
|
||||||
}
|
}
|
||||||
|
|
||||||
void text(String text, float fontSize, Color color) throws IOException {
|
void text(String text, float fontSize, Color color) throws IOException {
|
||||||
@@ -604,12 +505,12 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
|
|
||||||
void section(String text) throws IOException {
|
void section(String text) throws IOException {
|
||||||
ensureSpace(32F);
|
ensureSpace(32F);
|
||||||
writeLine(text, SECTION_FONT_SIZE, new Color(18, 56, 93), 0F, 26F, true);
|
writeLine(text, SECTION_FONT_SIZE, new Color(18, 56, 93), 0F, 26F);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subsection(String text) throws IOException {
|
void subsection(String text) throws IOException {
|
||||||
ensureSpace(26F);
|
ensureSpace(26F);
|
||||||
writeLine(text, SUBSECTION_FONT_SIZE, new Color(51, 65, 85), 0F, 22F, true);
|
writeLine(text, SUBSECTION_FONT_SIZE, new Color(51, 65, 85), 0F, 22F);
|
||||||
}
|
}
|
||||||
|
|
||||||
void separator() throws IOException {
|
void separator() throws IOException {
|
||||||
@@ -656,7 +557,7 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
for (List<String> row : safeRows) {
|
for (List<String> row : safeRows) {
|
||||||
drawRow(row, widths, false);
|
drawRow(row, widths, false);
|
||||||
}
|
}
|
||||||
y -= TABLE_AFTER_GAP;
|
y -= 8F;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float[] calculateWidths(float[] ratios) {
|
private float[] calculateWidths(float[] ratios) {
|
||||||
@@ -731,17 +632,6 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeLine(String text, float fontSize, Color color, float indent, float advance) throws IOException {
|
private void writeLine(String text, float fontSize, Color color, float indent, float advance) throws IOException {
|
||||||
writeLine(text, fontSize, color, indent, advance, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLine(
|
|
||||||
String text,
|
|
||||||
float fontSize,
|
|
||||||
Color color,
|
|
||||||
float indent,
|
|
||||||
float advance,
|
|
||||||
boolean bold
|
|
||||||
) throws IOException {
|
|
||||||
ensureSpace(advance);
|
ensureSpace(advance);
|
||||||
content.beginText();
|
content.beginText();
|
||||||
content.setNonStrokingColor(color);
|
content.setNonStrokingColor(color);
|
||||||
@@ -749,14 +639,6 @@ public class CcdiProjectOverviewReportPdfExporter {
|
|||||||
content.newLineAtOffset(MARGIN + indent, y);
|
content.newLineAtOffset(MARGIN + indent, y);
|
||||||
content.showText(text);
|
content.showText(text);
|
||||||
content.endText();
|
content.endText();
|
||||||
if (bold) {
|
|
||||||
content.beginText();
|
|
||||||
content.setNonStrokingColor(color);
|
|
||||||
content.setFont(font, fontSize);
|
|
||||||
content.newLineAtOffset(MARGIN + indent + 0.25F, y);
|
|
||||||
content.showText(text);
|
|
||||||
content.endText();
|
|
||||||
}
|
|
||||||
y -= advance;
|
y -= advance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import com.ruoyi.ccdi.project.domain.CcdiModelParam;
|
|||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalPersonQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectExternalRiskModelPeopleQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectAbnormalAccountExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectExternalPersonWarningExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskModelPeopleExcel;
|
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
||||||
@@ -30,21 +26,14 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisAbnormalGroupVO
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalPersonWarningItemVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalPersonWarningVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalRiskSummaryVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportParamVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportParamVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportSuspiciousTransactionVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewEmployeeHitRowVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewEmployeeHitRowVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportModelSummaryVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewStatVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewStatVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO;
|
||||||
@@ -52,7 +41,6 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewEmployeeResultMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewEmployeeResultMapper;
|
||||||
@@ -86,9 +74,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
@Resource
|
@Resource
|
||||||
private CcdiProjectMapper projectMapper;
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiBankStatementMapper bankStatementMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CcdiModelParamMapper modelParamMapper;
|
private CcdiModelParamMapper modelParamMapper;
|
||||||
|
|
||||||
@@ -227,108 +212,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return people;
|
return people;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<CcdiProjectRiskModelPeopleExcel> exportRiskModelPeople(CcdiProjectRiskModelPeopleQueryDTO queryDTO) {
|
|
||||||
ensureProjectExists(queryDTO.getProjectId());
|
|
||||||
normalizeRiskModelPeopleQuery(queryDTO);
|
|
||||||
|
|
||||||
return defaultList(overviewMapper.selectRiskModelPeopleList(queryDTO)).stream()
|
|
||||||
.map(item -> buildRiskModelPeopleExcelRow(item, "员工"))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiProjectExternalPersonWarningVO getExternalPersonWarnings(CcdiProjectExternalPersonQueryDTO queryDTO) {
|
|
||||||
ensureProjectExists(queryDTO.getProjectId());
|
|
||||||
|
|
||||||
Page<CcdiProjectExternalPersonWarningItemVO> page = new Page<>(
|
|
||||||
defaultRiskPeoplePageNum(queryDTO.getPageNum()),
|
|
||||||
defaultRiskPeoplePageSize(queryDTO.getPageSize())
|
|
||||||
);
|
|
||||||
Page<CcdiProjectExternalPersonWarningItemVO> resultPage =
|
|
||||||
overviewMapper.selectExternalPersonWarningPage(page, queryDTO);
|
|
||||||
|
|
||||||
List<CcdiProjectExternalPersonWarningItemVO> rows =
|
|
||||||
defaultList(resultPage == null ? null : resultPage.getRecords()).stream()
|
|
||||||
.peek(item -> item.setActionLabel(ACTION_LABEL))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
CcdiProjectExternalPersonWarningVO warnings = new CcdiProjectExternalPersonWarningVO();
|
|
||||||
warnings.setRows(rows);
|
|
||||||
warnings.setTotal(resultPage == null ? 0L : resultPage.getTotal());
|
|
||||||
warnings.setPageNum(page.getCurrent());
|
|
||||||
warnings.setPageSize(page.getSize());
|
|
||||||
return warnings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiProjectExternalRiskSummaryVO getExternalRiskSummary(Long projectId) {
|
|
||||||
ensureProjectExists(projectId);
|
|
||||||
CcdiProjectExternalRiskSummaryVO summary = overviewMapper.selectExternalRiskSummaryByProjectId(projectId);
|
|
||||||
if (summary == null) {
|
|
||||||
return new CcdiProjectExternalRiskSummaryVO();
|
|
||||||
}
|
|
||||||
summary.setTotal(defaultZero(summary.getTotal()));
|
|
||||||
summary.setHigh(defaultZero(summary.getHigh()));
|
|
||||||
summary.setMedium(defaultZero(summary.getMedium()));
|
|
||||||
summary.setLow(defaultZero(summary.getLow()));
|
|
||||||
summary.setNoRisk(defaultZero(summary.getNoRisk()));
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<CcdiProjectExternalPersonWarningExcel> exportExternalPersonWarnings(Long projectId) {
|
|
||||||
ensureProjectExists(projectId);
|
|
||||||
|
|
||||||
return defaultList(overviewMapper.selectExternalPersonWarningList(projectId)).stream()
|
|
||||||
.map(this::buildExternalPersonWarningExcelRow)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiProjectRiskModelCardsVO getExternalRiskModelCards(Long projectId) {
|
|
||||||
ensureProjectExists(projectId);
|
|
||||||
|
|
||||||
CcdiProjectRiskModelCardsVO cards = new CcdiProjectRiskModelCardsVO();
|
|
||||||
cards.setCardList(defaultList(overviewMapper.selectExternalRiskModelCardsByProjectId(projectId)));
|
|
||||||
return cards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiProjectRiskModelPeopleVO getExternalRiskModelPeople(CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO) {
|
|
||||||
ensureProjectExists(queryDTO.getProjectId());
|
|
||||||
normalizeExternalRiskModelPeopleQuery(queryDTO);
|
|
||||||
|
|
||||||
Page<CcdiProjectRiskModelPeopleItemVO> page = new Page<>(
|
|
||||||
defaultPageNum(queryDTO.getPageNum()),
|
|
||||||
defaultPageSize(queryDTO.getPageSize())
|
|
||||||
);
|
|
||||||
Page<CcdiProjectRiskModelPeopleItemVO> resultPage =
|
|
||||||
overviewMapper.selectExternalRiskModelPeoplePage(page, queryDTO);
|
|
||||||
|
|
||||||
List<CcdiProjectRiskModelPeopleItemVO> rows = defaultList(resultPage == null ? null : resultPage.getRecords())
|
|
||||||
.stream()
|
|
||||||
.peek(item -> item.setActionLabel(ACTION_LABEL))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
CcdiProjectRiskModelPeopleVO people = new CcdiProjectRiskModelPeopleVO();
|
|
||||||
people.setRows(rows);
|
|
||||||
people.setTotal(resultPage == null ? 0L : resultPage.getTotal());
|
|
||||||
return people;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<CcdiProjectRiskModelPeopleExcel> exportExternalRiskModelPeople(
|
|
||||||
CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO
|
|
||||||
) {
|
|
||||||
ensureProjectExists(queryDTO.getProjectId());
|
|
||||||
normalizeExternalRiskModelPeopleQuery(queryDTO);
|
|
||||||
|
|
||||||
return defaultList(overviewMapper.selectExternalRiskModelPeopleList(queryDTO)).stream()
|
|
||||||
.map(item -> buildRiskModelPeopleExcelRow(item, item.getStaffCode()))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CcdiProjectSuspiciousTransactionPageVO getSuspiciousTransactions(
|
public CcdiProjectSuspiciousTransactionPageVO getSuspiciousTransactions(
|
||||||
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
@@ -356,7 +239,7 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
ensureProjectExists(queryDTO.getProjectId());
|
ensureProjectExists(queryDTO.getProjectId());
|
||||||
normalizeSuspiciousTransactionQuery(queryDTO);
|
normalizeSuspiciousTransactionQuery(queryDTO);
|
||||||
|
|
||||||
return defaultList(overviewMapper.selectReportSuspiciousTransactionList(queryDTO)).stream()
|
return defaultList(overviewMapper.selectSuspiciousTransactionList(queryDTO)).stream()
|
||||||
.map(this::buildSuspiciousTransactionExcelRow)
|
.map(this::buildSuspiciousTransactionExcelRow)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
@@ -447,12 +330,9 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
.toList());
|
.toList());
|
||||||
report.setParams(buildReportParams(project));
|
report.setParams(buildReportParams(project));
|
||||||
report.setModelSummaries(defaultList(overviewMapper.selectReportRiskModelSummaries(projectId)));
|
report.setModelSummaries(defaultList(overviewMapper.selectReportRiskModelSummaries(projectId)));
|
||||||
report.setExternalRiskSummary(getExternalRiskSummary(projectId));
|
|
||||||
report.setExternalModelSummaries(buildExternalReportModelSummaries(projectId));
|
|
||||||
report.setRiskPeople(defaultList(overviewMapper.selectReportRiskPeople(projectId)).stream()
|
report.setRiskPeople(defaultList(overviewMapper.selectReportRiskPeople(projectId)).stream()
|
||||||
.peek(item -> item.setActionLabel(ACTION_LABEL))
|
.peek(item -> item.setActionLabel(ACTION_LABEL))
|
||||||
.toList());
|
.toList());
|
||||||
report.setExternalPersonWarnings(exportExternalPersonWarnings(projectId));
|
|
||||||
report.setSuspiciousTransactions(defaultList(
|
report.setSuspiciousTransactions(defaultList(
|
||||||
overviewMapper.selectReportSuspiciousTransactionList(suspiciousQuery)
|
overviewMapper.selectReportSuspiciousTransactionList(suspiciousQuery)
|
||||||
));
|
));
|
||||||
@@ -491,7 +371,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
|
|
||||||
projectMapper.updateRiskCountsByProjectId(
|
projectMapper.updateRiskCountsByProjectId(
|
||||||
projectId,
|
projectId,
|
||||||
countProjectScopeStaff(projectId),
|
|
||||||
countRiskLevel(results, "HIGH"),
|
countRiskLevel(results, "HIGH"),
|
||||||
countRiskLevel(results, "MEDIUM"),
|
countRiskLevel(results, "MEDIUM"),
|
||||||
countRiskLevel(results, "LOW"),
|
countRiskLevel(results, "LOW"),
|
||||||
@@ -506,7 +385,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
Map<String, Object> summary = overviewMapper.selectRiskCountSummaryByProjectId(projectId);
|
Map<String, Object> summary = overviewMapper.selectRiskCountSummaryByProjectId(projectId);
|
||||||
projectMapper.updateRiskCountsByProjectId(
|
projectMapper.updateRiskCountsByProjectId(
|
||||||
projectId,
|
projectId,
|
||||||
countProjectScopeStaff(projectId),
|
|
||||||
readCount(summary, "highRiskCount"),
|
readCount(summary, "highRiskCount"),
|
||||||
readCount(summary, "mediumRiskCount"),
|
readCount(summary, "mediumRiskCount"),
|
||||||
readCount(summary, "lowRiskCount"),
|
readCount(summary, "lowRiskCount"),
|
||||||
@@ -514,10 +392,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int countProjectScopeStaff(Long projectId) {
|
|
||||||
return defaultZero(bankStatementMapper.countMatchedStaffCountByProjectId(projectId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiProjectRiskPeopleOverviewItemVO buildRiskPeopleItem(Long projectId, CcdiProjectEmployeeRiskAggregateVO aggregate) {
|
private CcdiProjectRiskPeopleOverviewItemVO buildRiskPeopleItem(Long projectId, CcdiProjectEmployeeRiskAggregateVO aggregate) {
|
||||||
CcdiProjectRiskPeopleOverviewItemVO item = new CcdiProjectRiskPeopleOverviewItemVO();
|
CcdiProjectRiskPeopleOverviewItemVO item = new CcdiProjectRiskPeopleOverviewItemVO();
|
||||||
item.setName(aggregate.getStaffName());
|
item.setName(aggregate.getStaffName());
|
||||||
@@ -559,51 +433,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CcdiProjectExternalPersonWarningExcel buildExternalPersonWarningExcelRow(
|
|
||||||
CcdiProjectExternalPersonWarningItemVO item
|
|
||||||
) {
|
|
||||||
CcdiProjectExternalPersonWarningExcel row = new CcdiProjectExternalPersonWarningExcel();
|
|
||||||
row.setName(item.getName());
|
|
||||||
row.setIdNo(item.getIdNo());
|
|
||||||
row.setSubjectType(item.getSubjectType());
|
|
||||||
row.setRiskLevel(item.getRiskLevel());
|
|
||||||
row.setModelCount(item.getModelCount());
|
|
||||||
row.setRiskPoint(item.getRiskPoint());
|
|
||||||
row.setRelatedObject(item.getRelatedObject());
|
|
||||||
row.setLatestTradeTime(item.getLatestTradeTime());
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CcdiProjectOverviewReportModelSummaryVO> buildExternalReportModelSummaries(Long projectId) {
|
|
||||||
return defaultList(overviewMapper.selectExternalRiskModelCardsByProjectId(projectId)).stream()
|
|
||||||
.map(this::buildExternalReportModelSummary)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiProjectOverviewReportModelSummaryVO buildExternalReportModelSummary(CcdiProjectRiskModelCardVO card) {
|
|
||||||
CcdiProjectOverviewReportModelSummaryVO row = new CcdiProjectOverviewReportModelSummaryVO();
|
|
||||||
row.setModelCode(card.getModelCode());
|
|
||||||
row.setModelName(card.getModelName());
|
|
||||||
row.setWarningCount(card.getWarningCount());
|
|
||||||
row.setPeopleCount(card.getPeopleCount());
|
|
||||||
row.setPeopleNames("-");
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiProjectRiskModelPeopleExcel buildRiskModelPeopleExcelRow(
|
|
||||||
CcdiProjectRiskModelPeopleItemVO item,
|
|
||||||
String subjectType
|
|
||||||
) {
|
|
||||||
CcdiProjectRiskModelPeopleExcel row = new CcdiProjectRiskModelPeopleExcel();
|
|
||||||
row.setPersonName(item.getStaffName());
|
|
||||||
row.setSubjectType(subjectType);
|
|
||||||
row.setIdNo(item.getIdNo());
|
|
||||||
row.setScopeName(item.getDepartment());
|
|
||||||
row.setModelNames(joinModelNames(item.getModelNames()));
|
|
||||||
row.setHitTags(joinHitTagNames(item.getHitTagList()));
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureProjectExists(Long projectId) {
|
private void ensureProjectExists(Long projectId) {
|
||||||
getRequiredProject(projectId);
|
getRequiredProject(projectId);
|
||||||
}
|
}
|
||||||
@@ -616,14 +445,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
queryDTO.setMatchMode(queryDTO.getMatchMode().trim().toUpperCase());
|
queryDTO.setMatchMode(queryDTO.getMatchMode().trim().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void normalizeExternalRiskModelPeopleQuery(CcdiProjectExternalRiskModelPeopleQueryDTO queryDTO) {
|
|
||||||
if (queryDTO.getMatchMode() == null || queryDTO.getMatchMode().isBlank()) {
|
|
||||||
queryDTO.setMatchMode("ANY");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
queryDTO.setMatchMode(queryDTO.getMatchMode().trim().toUpperCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void normalizeSuspiciousTransactionQuery(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) {
|
private void normalizeSuspiciousTransactionQuery(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) {
|
||||||
if (queryDTO.getSuspiciousType() == null || queryDTO.getSuspiciousType().isBlank()) {
|
if (queryDTO.getSuspiciousType() == null || queryDTO.getSuspiciousType().isBlank()) {
|
||||||
queryDTO.setSuspiciousType("ALL");
|
queryDTO.setSuspiciousType("ALL");
|
||||||
@@ -693,18 +514,15 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CcdiProjectSuspiciousTransactionExcel buildSuspiciousTransactionExcelRow(
|
private CcdiProjectSuspiciousTransactionExcel buildSuspiciousTransactionExcelRow(
|
||||||
CcdiProjectOverviewReportSuspiciousTransactionVO item
|
CcdiProjectSuspiciousTransactionItemVO item
|
||||||
) {
|
) {
|
||||||
CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
|
CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
|
||||||
row.setTrxDate(item.getTrxDate());
|
row.setTrxDate(item.getTrxDate());
|
||||||
row.setLeAccountNo(item.getLeAccountNo());
|
row.setSuspiciousPersonName(item.getSuspiciousPersonName());
|
||||||
row.setLeAccountName(item.getLeAccountName());
|
row.setRelatedPersonName(item.getRelatedPersonName());
|
||||||
row.setCustomerAccountName(item.getCustomerAccountName());
|
|
||||||
row.setCustomerAccountNo(item.getCustomerAccountNo());
|
|
||||||
row.setRelatedStaffDisplay(formatRelatedStaff(item.getRelatedStaffName(), item.getRelatedStaffCode()));
|
row.setRelatedStaffDisplay(formatRelatedStaff(item.getRelatedStaffName(), item.getRelatedStaffCode()));
|
||||||
row.setUserMemo(item.getUserMemo());
|
row.setRelationType(item.getRelationType());
|
||||||
row.setCashType(item.getCashType());
|
row.setSummaryAndCashType(formatSummaryAndCashType(item.getUserMemo(), item.getCashType()));
|
||||||
row.setHitTags(item.getHitTags());
|
|
||||||
row.setDisplayAmount(item.getDisplayAmount());
|
row.setDisplayAmount(item.getDisplayAmount());
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
@@ -779,21 +597,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return safeMemo + "/" + safeCashType;
|
return safeMemo + "/" + safeCashType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String joinModelNames(List<String> modelNames) {
|
|
||||||
return defaultList(modelNames).stream()
|
|
||||||
.filter(item -> item != null && !item.isBlank())
|
|
||||||
.distinct()
|
|
||||||
.collect(Collectors.joining("、"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String joinHitTagNames(List<CcdiProjectRiskHitTagVO> hitTags) {
|
|
||||||
return defaultList(hitTags).stream()
|
|
||||||
.map(CcdiProjectRiskHitTagVO::getRuleName)
|
|
||||||
.filter(item -> item != null && !item.isBlank())
|
|
||||||
.distinct()
|
|
||||||
.collect(Collectors.joining("、"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiProjectPersonAnalysisAbnormalDetailVO buildAbnormalDetail(
|
private CcdiProjectPersonAnalysisAbnormalDetailVO buildAbnormalDetail(
|
||||||
List<CcdiBankStatementListVO> statementRows,
|
List<CcdiBankStatementListVO> statementRows,
|
||||||
List<CcdiProjectPersonAnalysisObjectRecordVO> objectRows
|
List<CcdiProjectPersonAnalysisObjectRecordVO> objectRows
|
||||||
|
|||||||
@@ -44,33 +44,19 @@ public class CcdiProjectRiskDetailWorkbookExporter {
|
|||||||
|
|
||||||
private void writeSuspiciousSheet(Sheet sheet, List<CcdiProjectSuspiciousTransactionExcel> rows) {
|
private void writeSuspiciousSheet(Sheet sheet, List<CcdiProjectSuspiciousTransactionExcel> rows) {
|
||||||
Row header = sheet.createRow(0);
|
Row header = sheet.createRow(0);
|
||||||
String[] headers = {
|
String[] headers = { "交易时间", "可疑人员", "关联人", "关联员工", "关系", "摘要/交易类型", "交易金额" };
|
||||||
"交易时间",
|
|
||||||
"本方账户",
|
|
||||||
"本方主体",
|
|
||||||
"对方名称",
|
|
||||||
"对方账户",
|
|
||||||
"关联员工",
|
|
||||||
"摘要",
|
|
||||||
"交易类型",
|
|
||||||
"异常标签",
|
|
||||||
"交易金额"
|
|
||||||
};
|
|
||||||
writeHeader(header, headers);
|
writeHeader(header, headers);
|
||||||
|
|
||||||
for (int i = 0; i < rows.size(); i++) {
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
CcdiProjectSuspiciousTransactionExcel item = rows.get(i);
|
CcdiProjectSuspiciousTransactionExcel item = rows.get(i);
|
||||||
Row row = sheet.createRow(i + 1);
|
Row row = sheet.createRow(i + 1);
|
||||||
row.createCell(0).setCellValue(safeText(item.getTrxDate()));
|
row.createCell(0).setCellValue(safeText(item.getTrxDate()));
|
||||||
row.createCell(1).setCellValue(safeText(item.getLeAccountNo()));
|
row.createCell(1).setCellValue(safeText(item.getSuspiciousPersonName()));
|
||||||
row.createCell(2).setCellValue(safeText(item.getLeAccountName()));
|
row.createCell(2).setCellValue(safeText(item.getRelatedPersonName()));
|
||||||
row.createCell(3).setCellValue(safeText(item.getCustomerAccountName()));
|
row.createCell(3).setCellValue(safeText(item.getRelatedStaffDisplay()));
|
||||||
row.createCell(4).setCellValue(safeText(item.getCustomerAccountNo()));
|
row.createCell(4).setCellValue(safeText(item.getRelationType()));
|
||||||
row.createCell(5).setCellValue(safeText(item.getRelatedStaffDisplay()));
|
row.createCell(5).setCellValue(safeText(item.getSummaryAndCashType()));
|
||||||
row.createCell(6).setCellValue(safeText(item.getUserMemo()));
|
row.createCell(6).setCellValue(safeNumber(item.getDisplayAmount()));
|
||||||
row.createCell(7).setCellValue(safeText(item.getCashType()));
|
|
||||||
row.createCell(8).setCellValue(safeText(item.getHitTags()));
|
|
||||||
row.createCell(9).setCellValue(safeNumber(item.getDisplayAmount()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
|
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.ProjectAccessScope;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
|
||||||
import com.ruoyi.ccdi.project.domain.event.CcdiProjectHistoryImportSubmittedEvent;
|
import com.ruoyi.ccdi.project.domain.event.CcdiProjectHistoryImportSubmittedEvent;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||||
@@ -47,18 +43,12 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
@Resource
|
@Resource
|
||||||
private CcdiProjectMapper projectMapper;
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiBankTagTaskMapper bankTagTaskMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private LsfxAnalysisClient lsfxAnalysisClient;
|
private LsfxAnalysisClient lsfxAnalysisClient;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ApplicationEventPublisher applicationEventPublisher;
|
private ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiProjectAccessService projectAccessService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
|
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
|
||||||
@@ -87,7 +77,6 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
// 5. 返回VO
|
// 5. 返回VO
|
||||||
CcdiProjectVO vo = new CcdiProjectVO();
|
CcdiProjectVO vo = new CcdiProjectVO();
|
||||||
BeanUtils.copyProperties(project, vo);
|
BeanUtils.copyProperties(project, vo);
|
||||||
fillProjectExtraFields(project, vo);
|
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +90,6 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
if (existingProject == null) {
|
if (existingProject == null) {
|
||||||
throw new ServiceException("项目不存在");
|
throw new ServiceException("项目不存在");
|
||||||
}
|
}
|
||||||
projectAccessService.assertCanOperate(dto.getProjectId());
|
|
||||||
|
|
||||||
// 只更新允许修改的字段
|
// 只更新允许修改的字段
|
||||||
existingProject.setProjectName(dto.getProjectName());
|
existingProject.setProjectName(dto.getProjectName());
|
||||||
@@ -112,46 +100,38 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
|
|
||||||
CcdiProjectVO vo = new CcdiProjectVO();
|
CcdiProjectVO vo = new CcdiProjectVO();
|
||||||
BeanUtils.copyProperties(existingProject, vo);
|
BeanUtils.copyProperties(existingProject, vo);
|
||||||
fillProjectExtraFields(existingProject, vo);
|
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteProject(Long projectId) {
|
public boolean deleteProject(Long projectId) {
|
||||||
projectAccessService.assertCanOperate(projectId);
|
|
||||||
return projectMapper.deleteById(projectId) > 0;
|
return projectMapper.deleteById(projectId) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CcdiProjectVO getProjectById(Long projectId) {
|
public CcdiProjectVO getProjectById(Long projectId) {
|
||||||
projectAccessService.assertCanRead(projectId);
|
|
||||||
CcdiProject project = projectMapper.selectById(projectId);
|
CcdiProject project = projectMapper.selectById(projectId);
|
||||||
if (project == null) {
|
if (project == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
CcdiProjectVO vo = new CcdiProjectVO();
|
CcdiProjectVO vo = new CcdiProjectVO();
|
||||||
BeanUtils.copyProperties(project, vo);
|
BeanUtils.copyProperties(project, vo);
|
||||||
fillProjectExtraFields(project, vo);
|
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page, CcdiProjectQueryDTO queryDTO) {
|
public Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page, CcdiProjectQueryDTO queryDTO) {
|
||||||
ProjectAccessScope scope = projectAccessService.buildCurrentScope();
|
return projectMapper.selectProjectPage(page, queryDTO);
|
||||||
Page<CcdiProjectVO> result = projectMapper.selectProjectPage(page, queryDTO, scope);
|
|
||||||
fillProjectExtraFields(result.getRecords());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CcdiProjectHistoryListItemVO> listHistoryProjects(CcdiProjectQueryDTO queryDTO) {
|
public List<CcdiProjectHistoryListItemVO> listHistoryProjects(CcdiProjectQueryDTO queryDTO) {
|
||||||
return projectMapper.selectHistoryProjects(queryDTO, projectAccessService.buildCurrentScope());
|
return projectMapper.selectHistoryProjects(queryDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public CcdiProjectVO importFromHistory(CcdiProjectImportHistoryDTO dto, String operator) {
|
public CcdiProjectVO importFromHistory(CcdiProjectImportHistoryDTO dto, String operator) {
|
||||||
projectAccessService.assertSourceProjectsReadable(dto.getSourceProjectIds());
|
|
||||||
CcdiProjectSaveDTO saveDTO = new CcdiProjectSaveDTO();
|
CcdiProjectSaveDTO saveDTO = new CcdiProjectSaveDTO();
|
||||||
saveDTO.setProjectName(dto.getProjectName());
|
saveDTO.setProjectName(dto.getProjectName());
|
||||||
saveDTO.setDescription(dto.getDescription());
|
saveDTO.setDescription(dto.getDescription());
|
||||||
@@ -171,43 +151,44 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
@Override
|
@Override
|
||||||
public CcdiProjectStatusCountsVO getStatusCounts() {
|
public CcdiProjectStatusCountsVO getStatusCounts() {
|
||||||
CcdiProjectStatusCountsVO vo = new CcdiProjectStatusCountsVO();
|
CcdiProjectStatusCountsVO vo = new CcdiProjectStatusCountsVO();
|
||||||
ProjectAccessScope scope = projectAccessService.buildCurrentScope();
|
|
||||||
|
|
||||||
// 统计全部项目
|
// 统计全部项目
|
||||||
LambdaQueryWrapper<CcdiProject> baseWrapper = buildScopeWrapper(scope);
|
Long totalCount = projectMapper.selectCount(null);
|
||||||
Long totalCount = projectMapper.selectCount(baseWrapper);
|
|
||||||
vo.setAll(totalCount);
|
vo.setAll(totalCount);
|
||||||
|
|
||||||
// 统计进行中项目(状态0)
|
// 统计进行中项目(状态0)
|
||||||
Long status0Count = projectMapper.selectCount(buildScopeWrapper(scope)
|
Long status0Count = projectMapper.selectCount(
|
||||||
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING));
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING)
|
||||||
|
);
|
||||||
vo.setStatus0(status0Count);
|
vo.setStatus0(status0Count);
|
||||||
|
|
||||||
// 统计已完成项目(状态1)
|
// 统计已完成项目(状态1)
|
||||||
Long status1Count = projectMapper.selectCount(buildScopeWrapper(scope)
|
Long status1Count = projectMapper.selectCount(
|
||||||
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED));
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED)
|
||||||
|
);
|
||||||
vo.setStatus1(status1Count);
|
vo.setStatus1(status1Count);
|
||||||
|
|
||||||
// 统计已归档项目(状态2)
|
// 统计已归档项目(状态2)
|
||||||
Long status2Count = projectMapper.selectCount(buildScopeWrapper(scope)
|
Long status2Count = projectMapper.selectCount(
|
||||||
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED));
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED)
|
||||||
|
);
|
||||||
vo.setStatus2(status2Count);
|
vo.setStatus2(status2Count);
|
||||||
|
|
||||||
Long status3Count = projectMapper.selectCount(buildScopeWrapper(scope)
|
Long status3Count = projectMapper.selectCount(
|
||||||
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING));
|
new LambdaQueryWrapper<CcdiProject>()
|
||||||
|
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING)
|
||||||
|
);
|
||||||
vo.setStatus3(status3Count);
|
vo.setStatus3(status3Count);
|
||||||
|
|
||||||
Long status4Count = projectMapper.selectCount(buildScopeWrapper(scope)
|
|
||||||
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAG_FAILED));
|
|
||||||
vo.setStatus4(status4Count);
|
|
||||||
|
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void archiveProject(Long projectId, String operator) {
|
public void archiveProject(Long projectId, String operator) {
|
||||||
CcdiProject project = getRequiredProject(projectId);
|
CcdiProject project = getRequiredProject(projectId);
|
||||||
projectAccessService.assertCanOperate(projectId);
|
|
||||||
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
|
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
|
||||||
throw new ServiceException("项目已归档,无需重复操作");
|
throw new ServiceException("项目已归档,无需重复操作");
|
||||||
}
|
}
|
||||||
@@ -282,55 +263,10 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
|||||||
case CcdiProjectStatusConstants.COMPLETED -> "已完成";
|
case CcdiProjectStatusConstants.COMPLETED -> "已完成";
|
||||||
case CcdiProjectStatusConstants.ARCHIVED -> "已归档";
|
case CcdiProjectStatusConstants.ARCHIVED -> "已归档";
|
||||||
case CcdiProjectStatusConstants.TAGGING -> "打标中";
|
case CcdiProjectStatusConstants.TAGGING -> "打标中";
|
||||||
case CcdiProjectStatusConstants.TAG_FAILED -> "打标失败";
|
|
||||||
default -> "未知";
|
default -> "未知";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillLatestTagFailure(CcdiProject project, CcdiProjectVO vo) {
|
|
||||||
if (!CcdiProjectStatusConstants.TAG_FAILED.equals(project.getStatus())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CcdiBankTagTask latestFailedTask = bankTagTaskMapper.selectLatestFailedTaskByProjectId(project.getProjectId());
|
|
||||||
if (latestFailedTask == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
vo.setLatestTagTaskErrorMessage(latestFailedTask.getErrorMessage());
|
|
||||||
vo.setLatestTagTaskEndTime(latestFailedTask.getEndTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
private LambdaQueryWrapper<CcdiProject> buildScopeWrapper(ProjectAccessScope scope) {
|
|
||||||
LambdaQueryWrapper<CcdiProject> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
if (scope != null && !scope.isViewAllProjects()) {
|
|
||||||
wrapper.eq(CcdiProject::getCreateBy, scope.getUsername());
|
|
||||||
}
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillProjectExtraFields(List<CcdiProjectVO> records) {
|
|
||||||
if (records == null || records.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ProjectAccessScope scope = projectAccessService.buildCurrentScope();
|
|
||||||
for (CcdiProjectVO vo : records) {
|
|
||||||
fillProjectAccessFields(vo, scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillProjectExtraFields(CcdiProject project, CcdiProjectVO vo) {
|
|
||||||
fillLatestTagFailure(project, vo);
|
|
||||||
fillProjectAccessFields(vo, projectAccessService.buildCurrentScope());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillProjectAccessFields(CcdiProjectVO vo, ProjectAccessScope scope) {
|
|
||||||
if (vo == null || scope == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean ownedByCurrentUser = Objects.equals(scope.getUsername(), vo.getCreateBy());
|
|
||||||
vo.setOwnedByCurrentUser(ownedByCurrentUser);
|
|
||||||
vo.setCanOperate(scope.isSuperAdmin() || ownedByCurrentUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveOperator(String operator) {
|
private String resolveOperator(String operator) {
|
||||||
return StringUtils.hasText(operator) ? operator : "system";
|
return StringUtils.hasText(operator) ? operator : "system";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.project.service.impl;
|
|
||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphSuspectedEnterpriseQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphEdgeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseItemVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseVO;
|
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphVO;
|
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiRelationGraphMapper;
|
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiRelationGraphService;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.Period;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关系图谱Service实现
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class CcdiRelationGraphServiceImpl implements ICcdiRelationGraphService {
|
|
||||||
|
|
||||||
private static final int DEFAULT_LIMIT = 80;
|
|
||||||
private static final int MAX_LIMIT = 200;
|
|
||||||
private static final int DEFAULT_SUSPECTED_LIMIT = 10;
|
|
||||||
private static final int MAX_SUSPECTED_LIMIT = 20;
|
|
||||||
private static final int SAME_NAME_BLOCK_THRESHOLD = 20;
|
|
||||||
private static final String NODE_PREFIX = "rel_node/";
|
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CcdiRelationGraphMapper relationGraphMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<CcdiRelationGraphNodeVO> searchSubjects(CcdiRelationGraphQueryDTO queryDTO) {
|
|
||||||
CcdiRelationGraphQueryDTO query = normalizeGraphQuery(queryDTO);
|
|
||||||
if (isBlank(query.getKeyword()) && isBlank(query.getObjectKey())) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return relationGraphMapper.selectRelationGraphSubjects(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiRelationGraphVO getRelationGraph(CcdiRelationGraphQueryDTO queryDTO) {
|
|
||||||
CcdiRelationGraphQueryDTO query = normalizeGraphQuery(queryDTO);
|
|
||||||
CcdiRelationGraphNodeVO centerNode = resolveCenterNode(query);
|
|
||||||
if (centerNode == null || isBlank(centerNode.getObjectKey())) {
|
|
||||||
return new CcdiRelationGraphVO();
|
|
||||||
}
|
|
||||||
|
|
||||||
query.setObjectKey(centerNode.getObjectKey());
|
|
||||||
List<CcdiRelationGraphEdgeVO> edges = relationGraphMapper.selectRelationGraphEdges(query);
|
|
||||||
if (edges == null) {
|
|
||||||
edges = Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CcdiRelationGraphNodeVO> nodes = buildNodes(centerNode, edges);
|
|
||||||
CcdiRelationGraphVO graph = new CcdiRelationGraphVO();
|
|
||||||
graph.setCenterNode(centerNode);
|
|
||||||
graph.setNodes(nodes);
|
|
||||||
graph.setEdges(edges);
|
|
||||||
graph.setEdgeCount((long) edges.size());
|
|
||||||
graph.setMaxDepth(1);
|
|
||||||
return graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CcdiRelationGraphSuspectedEnterpriseVO getSuspectedEnterprises(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO) {
|
|
||||||
CcdiRelationGraphSuspectedEnterpriseVO result = new CcdiRelationGraphSuspectedEnterpriseVO();
|
|
||||||
CcdiRelationGraphSuspectedEnterpriseQueryDTO query = normalizeSuspectedEnterpriseQuery(queryDTO);
|
|
||||||
if (isBlank(query.getPersonName())) {
|
|
||||||
result.setMessage("缺少人员姓名,无法按工商同名主体召回");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sameNameKeyNoCount = relationGraphMapper.countSuspectedEnterpriseKeyNos(query.getPersonName());
|
|
||||||
result.setSameNameKeyNoCount(sameNameKeyNoCount);
|
|
||||||
if (sameNameKeyNoCount > SAME_NAME_BLOCK_THRESHOLD) {
|
|
||||||
result.setBlocked(true);
|
|
||||||
result.setMessage("同名工商主体过多,请结合交易对手企业名称或其他线索进一步筛选");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate birthDate = resolveBirthDate(query);
|
|
||||||
List<CcdiRelationGraphSuspectedEnterpriseItemVO> rows =
|
|
||||||
relationGraphMapper.selectSuspectedEnterprises(query.getPersonName(), MAX_SUSPECTED_LIMIT);
|
|
||||||
List<CcdiRelationGraphSuspectedEnterpriseItemVO> filteredRows = new ArrayList<>();
|
|
||||||
if (rows != null) {
|
|
||||||
for (CcdiRelationGraphSuspectedEnterpriseItemVO row : rows) {
|
|
||||||
if (row == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
row.setPersonName(query.getPersonName());
|
|
||||||
if (applyAgeRule(row, birthDate)) {
|
|
||||||
filteredRows.add(row);
|
|
||||||
if (filteredRows.size() >= query.getLimit()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.setRows(filteredRows);
|
|
||||||
if (filteredRows.isEmpty()) {
|
|
||||||
result.setMessage("未发现可展示的疑似同名企业");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiRelationGraphNodeVO resolveCenterNode(CcdiRelationGraphQueryDTO query) {
|
|
||||||
List<CcdiRelationGraphNodeVO> subjects = relationGraphMapper.selectRelationGraphSubjects(query);
|
|
||||||
if (subjects == null || subjects.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return subjects.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CcdiRelationGraphNodeVO> buildNodes(CcdiRelationGraphNodeVO centerNode, List<CcdiRelationGraphEdgeVO> edges) {
|
|
||||||
Set<String> objectKeys = new LinkedHashSet<>();
|
|
||||||
objectKeys.add(centerNode.getObjectKey());
|
|
||||||
for (CcdiRelationGraphEdgeVO edge : edges) {
|
|
||||||
edge.setFromObjectKey(toObjectKey(edge.getFromKey()));
|
|
||||||
edge.setToObjectKey(toObjectKey(edge.getToKey()));
|
|
||||||
if (!isBlank(edge.getFromObjectKey())) {
|
|
||||||
objectKeys.add(edge.getFromObjectKey());
|
|
||||||
}
|
|
||||||
if (!isBlank(edge.getToObjectKey())) {
|
|
||||||
objectKeys.add(edge.getToObjectKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CcdiRelationGraphNodeVO> rawNodes = relationGraphMapper.selectRelationGraphNodesByKeys(new ArrayList<>(objectKeys));
|
|
||||||
Map<String, CcdiRelationGraphNodeVO> nodeMap = new LinkedHashMap<>();
|
|
||||||
if (rawNodes != null) {
|
|
||||||
for (CcdiRelationGraphNodeVO node : rawNodes) {
|
|
||||||
enrichNode(node, centerNode);
|
|
||||||
nodeMap.put(node.getObjectKey(), node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!nodeMap.containsKey(centerNode.getObjectKey())) {
|
|
||||||
enrichNode(centerNode, centerNode);
|
|
||||||
nodeMap.put(centerNode.getObjectKey(), centerNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (CcdiRelationGraphEdgeVO edge : edges) {
|
|
||||||
CcdiRelationGraphNodeVO fromNode = nodeMap.get(edge.getFromObjectKey());
|
|
||||||
CcdiRelationGraphNodeVO toNode = nodeMap.get(edge.getToObjectKey());
|
|
||||||
if (fromNode != null) {
|
|
||||||
edge.setFromName(fromNode.getNodeName());
|
|
||||||
}
|
|
||||||
if (toNode != null) {
|
|
||||||
edge.setToName(toNode.getNodeName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return List.copyOf(nodeMap.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enrichNode(CcdiRelationGraphNodeVO node, CcdiRelationGraphNodeVO centerNode) {
|
|
||||||
if (node == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
node.setNodeKey(NODE_PREFIX + node.getObjectKey());
|
|
||||||
node.setCanExpand(true);
|
|
||||||
node.setDepth(node.getObjectKey() != null && node.getObjectKey().equals(centerNode.getObjectKey()) ? 0 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiRelationGraphQueryDTO normalizeGraphQuery(CcdiRelationGraphQueryDTO queryDTO) {
|
|
||||||
CcdiRelationGraphQueryDTO query = queryDTO == null ? new CcdiRelationGraphQueryDTO() : queryDTO;
|
|
||||||
query.setKeyword(normalizeText(query.getKeyword()));
|
|
||||||
query.setObjectKey(normalizeText(query.getObjectKey()));
|
|
||||||
query.setLimit(normalizeLimit(query.getLimit()));
|
|
||||||
query.setDepth(1);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CcdiRelationGraphSuspectedEnterpriseQueryDTO normalizeSuspectedEnterpriseQuery(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO) {
|
|
||||||
CcdiRelationGraphSuspectedEnterpriseQueryDTO query =
|
|
||||||
queryDTO == null ? new CcdiRelationGraphSuspectedEnterpriseQueryDTO() : queryDTO;
|
|
||||||
query.setPersonName(normalizeText(query.getPersonName()));
|
|
||||||
query.setCertNo(normalizeText(query.getCertNo()));
|
|
||||||
query.setBirthDate(normalizeText(query.getBirthDate()));
|
|
||||||
query.setLimit(normalizeSuspectedLimit(query.getLimit()));
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer normalizeSuspectedLimit(Integer limit) {
|
|
||||||
if (limit == null || limit <= 0) {
|
|
||||||
return DEFAULT_SUSPECTED_LIMIT;
|
|
||||||
}
|
|
||||||
return Math.min(limit, MAX_SUSPECTED_LIMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalDate resolveBirthDate(CcdiRelationGraphSuspectedEnterpriseQueryDTO query) {
|
|
||||||
LocalDate explicitBirthDate = parseDate(query.getBirthDate());
|
|
||||||
if (explicitBirthDate != null) {
|
|
||||||
return explicitBirthDate;
|
|
||||||
}
|
|
||||||
return parseBirthDateFromCertNo(query.getCertNo());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean applyAgeRule(CcdiRelationGraphSuspectedEnterpriseItemVO row, LocalDate birthDate) {
|
|
||||||
LocalDate establishDate = parseDate(row.getEstablishDate());
|
|
||||||
if (birthDate == null || establishDate == null) {
|
|
||||||
row.setMatchReason("姓名一致;企业成立日期或出生日期缺失,年龄无法判断");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int age = Period.between(birthDate, establishDate).getYears();
|
|
||||||
row.setAgeAtEstablish(age);
|
|
||||||
if (age < 18) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
row.setMatchReason("姓名一致;成立时年龄" + age + "岁");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalDate parseBirthDateFromCertNo(String certNo) {
|
|
||||||
if (isBlank(certNo)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String value = certNo.trim();
|
|
||||||
if (value.matches("^\\d{17}[0-9Xx]$")) {
|
|
||||||
return parseCompactDate(value.substring(6, 14));
|
|
||||||
}
|
|
||||||
if (value.matches("^\\d{15}$")) {
|
|
||||||
return parseCompactDate("19" + value.substring(6, 12));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalDate parseCompactDate(String value) {
|
|
||||||
try {
|
|
||||||
return LocalDate.parse(value, DateTimeFormatter.BASIC_ISO_DATE);
|
|
||||||
} catch (DateTimeParseException ignored) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalDate parseDate(String value) {
|
|
||||||
if (isBlank(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return LocalDate.parse(value.trim(), DATE_FORMATTER);
|
|
||||||
} catch (DateTimeParseException ignored) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer normalizeLimit(Integer limit) {
|
|
||||||
if (limit == null || limit <= 0) {
|
|
||||||
return DEFAULT_LIMIT;
|
|
||||||
}
|
|
||||||
return Math.min(limit, MAX_LIMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toObjectKey(String nodeKey) {
|
|
||||||
if (isBlank(nodeKey)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return nodeKey.startsWith(NODE_PREFIX) ? nodeKey.substring(NODE_PREFIX.length()) : nodeKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String normalizeText(String value) {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String trimmed = value.trim();
|
|
||||||
return trimmed.isEmpty() ? null : trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBlank(String value) {
|
|
||||||
return value == null || value.trim().isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -119,7 +119,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<select id="countMatchedStaffCountByProjectId" resultType="java.lang.Integer">
|
<select id="countMatchedStaffCountByProjectId" resultType="java.lang.Integer">
|
||||||
select count(distinct trim(bs.cret_no))
|
select count(distinct trim(bs.cret_no))
|
||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement bs
|
||||||
inner join ccdi_base_staff staff on staff.id_card = trim(bs.cret_no)
|
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and bs.cret_no is not null
|
and bs.cret_no is not null
|
||||||
and trim(bs.cret_no) != ''
|
and trim(bs.cret_no) != ''
|
||||||
@@ -215,12 +215,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
#{item}
|
#{item}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
<if test="query.ourCertNos != null and query.ourCertNos.size() > 0">
|
|
||||||
AND bs.cret_no IN
|
|
||||||
<foreach collection="query.ourCertNos" item="item" open="(" separator="," close=")">
|
|
||||||
#{item}
|
|
||||||
</foreach>
|
|
||||||
</if>
|
|
||||||
<if test="query.ourBanks != null and query.ourBanks.size() > 0">
|
<if test="query.ourBanks != null and query.ourBanks.size() > 0">
|
||||||
AND bs.BANK IN
|
AND bs.BANK IN
|
||||||
<foreach collection="query.ourBanks" item="item" open="(" separator="," close=")">
|
<foreach collection="query.ourBanks" item="item" open="(" separator="," close=")">
|
||||||
|
|||||||
@@ -41,23 +41,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and trim(bs.cret_no) != ''
|
and trim(bs.cret_no) != ''
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="externalPersonPredicateSql">
|
|
||||||
bs.cret_no is not null
|
|
||||||
and trim(bs.cret_no) != ''
|
|
||||||
and not exists (
|
|
||||||
select 1
|
|
||||||
from ccdi_base_staff staff
|
|
||||||
where staff.id_card = bs.cret_no
|
|
||||||
)
|
|
||||||
and not exists (
|
|
||||||
select 1
|
|
||||||
from ccdi_staff_fmy_relation relation
|
|
||||||
where relation.status = 1
|
|
||||||
and relation.relation_cert_no = bs.cret_no
|
|
||||||
)
|
|
||||||
and trim(IFNULL(bs.LE_ACCOUNT_NAME, '')) <> trim(IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''))
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="cashDepositPredicate">
|
<sql id="cashDepositPredicate">
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
@@ -122,7 +105,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
|
|
||||||
<sql id="salaryExclusionPredicate">
|
<sql id="salaryExclusionPredicate">
|
||||||
not (
|
not (
|
||||||
(
|
|
||||||
bs.CUSTOMER_ACCOUNT_NAME = '浙江兰溪农村商业银行股份有限公司'
|
bs.CUSTOMER_ACCOUNT_NAME = '浙江兰溪农村商业银行股份有限公司'
|
||||||
and (
|
and (
|
||||||
IFNULL(bs.USER_MEMO, '') LIKE '%代发%'
|
IFNULL(bs.USER_MEMO, '') LIKE '%代发%'
|
||||||
@@ -151,92 +133,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
or IFNULL(bs.CASH_TYPE, '') LIKE '%劳务费%'
|
or IFNULL(bs.CASH_TYPE, '') LIKE '%劳务费%'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or (
|
|
||||||
IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%公积金中心%'
|
|
||||||
and (
|
|
||||||
IFNULL(bs.USER_MEMO, '') LIKE '%公积金%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%批量代付%'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') LIKE '%公积金%'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') LIKE '%批量代付%'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="financialProductExclusionPredicate">
|
|
||||||
not (
|
|
||||||
(
|
|
||||||
(
|
|
||||||
IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '理财|理财产品|结构.*存款|结构性存款|理财.*托管|余额宝|朝朝宝|朝朝盈|现金宝|金添利|定存宝'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') REGEXP '理财|理财产品|结构.*存款|结构性存款|本金划出|本金返还|余额宝|朝朝宝|朝朝盈|现金宝|金添利|定存宝|整存整取|智能存款|通知存款'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '受托理财|表内理财|购买理财|理财购买|理财扣款|理财申购|理财认购|结构性存款|存款产品|朝朝宝'
|
|
||||||
or (
|
|
||||||
IFNULL(bs.USER_MEMO, '') REGEXP '申购|认购|赎回'
|
|
||||||
and IFNULL(bs.USER_MEMO, '') REGEXP '理财|产品|存款|本金|余额宝|朝朝宝|朝朝盈'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
and IFNULL(bs.USER_MEMO, '') NOT REGEXP '财务|经理|代理财税'
|
|
||||||
and IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') NOT LIKE '%代理财政%'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="thirdPartyWithdrawIncomePredicate">
|
|
||||||
(
|
|
||||||
(
|
|
||||||
bs.BANK in ('ALIPAY', 'WECHAT')
|
|
||||||
and (
|
|
||||||
IFNULL(bs.CASH_TYPE, '') LIKE '%提现%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%提现%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%转出到%银行%'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%提现%'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%转出到%银行%'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
(
|
|
||||||
bs.BANK is null
|
|
||||||
or bs.BANK = ''
|
|
||||||
or bs.BANK not in ('ALIPAY', 'WECHAT')
|
|
||||||
)
|
|
||||||
and (
|
|
||||||
IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '支付宝|Alipay|财付通|Tenpay|微信|wechat|WeChat|微信零钱'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') REGEXP '支付宝|Alipay|财付通|Tenpay|微信|wechat|WeChat|微信零钱'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '支付宝|Alipay|财付通|Tenpay|微信|wechat|WeChat|微信零钱'
|
|
||||||
)
|
|
||||||
and (
|
|
||||||
IFNULL(bs.CASH_TYPE, '') LIKE '%提现%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%提现%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%转出到%银行%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%提现到账%'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%提现%'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%转出到%银行%'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%提现到账%'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="abnormalCustomerTransactionSubjectSql">
|
|
||||||
select
|
|
||||||
staff.id_card as subjectCertNo,
|
|
||||||
staff.name as subjectName,
|
|
||||||
'本人' as subjectType
|
|
||||||
from ccdi_base_staff staff
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
relation.relation_cert_no as subjectCertNo,
|
|
||||||
relation.relation_name as subjectName,
|
|
||||||
case
|
|
||||||
when relation.relation_type is not null and trim(relation.relation_type) != '' then relation.relation_type
|
|
||||||
else '关系人'
|
|
||||||
end as subjectType
|
|
||||||
from ccdi_staff_fmy_relation relation
|
|
||||||
where relation.status = 1
|
|
||||||
and relation.relation_cert_no is not null
|
|
||||||
and trim(relation.relation_cert_no) != ''
|
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="salaryIncomePredicate">
|
<sql id="salaryIncomePredicate">
|
||||||
@@ -271,7 +167,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
IFNULL(bs.USER_MEMO, '') REGEXP '(购|买).*房|(购|买).*车|车款|房款|首付|(房|车).*贷'
|
IFNULL(bs.USER_MEMO, '') REGEXP '(购|买).*房|(购|买).*车|车款|房款|首付|(房|车).*贷'
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '汽车销售|汽车金融|4S店|汽贸|车行|房地产|置业|置地|地产|房产|不动产|链家|贝壳|我爱我家|房管局'
|
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '汽车销售|汽车金融|4S店|汽贸|车行|房地产|置业|置地|地产|房产|不动产|链家|贝壳|我爱我家|房管局'
|
||||||
)
|
)
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
and (
|
and (
|
||||||
exists (
|
exists (
|
||||||
select 1
|
select 1
|
||||||
@@ -303,7 +198,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
IFNULL(bs.USER_MEMO, '') REGEXP '税务|缴税|税款'
|
IFNULL(bs.USER_MEMO, '') REGEXP '税务|缴税|税款'
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '税务|税务局|国库|国家金库|财政'
|
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '税务|税务局|国库|国家金库|财政'
|
||||||
)
|
)
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
and (
|
and (
|
||||||
exists (
|
exists (
|
||||||
select 1
|
select 1
|
||||||
@@ -341,7 +235,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
||||||
and relation.person_id is null
|
and relation.person_id is null
|
||||||
and <include refid="salaryExclusionPredicate"/>
|
and <include refid="salaryExclusionPredicate"/>
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectCumulativeIncomeObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectCumulativeIncomeObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
@@ -369,7 +262,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
||||||
and relation.person_id is null
|
and relation.person_id is null
|
||||||
and <include refid="salaryExclusionPredicate"/>
|
and <include refid="salaryExclusionPredicate"/>
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
group by staff.id_card, bs.CUSTOMER_ACCOUNT_NAME
|
group by staff.id_card, bs.CUSTOMER_ACCOUNT_NAME
|
||||||
having SUM(IFNULL(bs.AMOUNT_CR, 0)) > #{threshold}
|
having SUM(IFNULL(bs.AMOUNT_CR, 0)) > #{threshold}
|
||||||
) t
|
) t
|
||||||
@@ -391,7 +283,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
and STR_TO_DATE(LEFT(TRIM(bs.TRX_DATE), 10), '%Y-%m-%d') >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
and STR_TO_DATE(LEFT(TRIM(bs.TRX_DATE), 10), '%Y-%m-%d') >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
||||||
group by staff.id_card
|
group by staff.id_card
|
||||||
having SUM(IFNULL(bs.AMOUNT_DR, 0) + IFNULL(bs.AMOUNT_CR, 0)) > #{threshold}
|
having SUM(IFNULL(bs.AMOUNT_DR, 0) + IFNULL(bs.AMOUNT_CR, 0)) > #{threshold}
|
||||||
@@ -411,12 +302,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.AMOUNT_CR, 0) > #{threshold}
|
and IFNULL(bs.AMOUNT_CR, 0) > #{threshold}
|
||||||
and <include refid="cashDepositPredicate"/>
|
and <include refid="cashDepositPredicate"/>
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
and (
|
||||||
and exists (
|
exists (
|
||||||
select 1
|
select 1
|
||||||
from ccdi_base_staff staff
|
from ccdi_base_staff staff
|
||||||
where staff.id_card = bs.cret_no
|
where staff.id_card = bs.cret_no
|
||||||
)
|
)
|
||||||
|
or exists (
|
||||||
|
select 1
|
||||||
|
from ccdi_staff_fmy_relation relation
|
||||||
|
where relation.relation_cert_no = bs.cret_no
|
||||||
|
and relation.status = 1
|
||||||
|
)
|
||||||
|
)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectFrequentCashDepositObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectFrequentCashDepositObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
@@ -442,7 +340,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.AMOUNT_CR, 0) > #{amountThreshold}
|
and IFNULL(bs.AMOUNT_CR, 0) > #{amountThreshold}
|
||||||
and <include refid="cashDepositPredicate"/>
|
and <include refid="cashDepositPredicate"/>
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
union all
|
||||||
|
select
|
||||||
|
relation.person_id AS object_key,
|
||||||
|
LEFT(TRIM(bs.TRX_DATE), 10) AS cash_date
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_staff_fmy_relation relation on relation.relation_cert_no = bs.cret_no
|
||||||
|
where bs.project_id = #{projectId}
|
||||||
|
and relation.status = 1
|
||||||
|
and IFNULL(bs.AMOUNT_CR, 0) > #{amountThreshold}
|
||||||
|
and <include refid="cashDepositPredicate"/>
|
||||||
) source
|
) source
|
||||||
group by source.object_key, source.cash_date
|
group by source.object_key, source.cash_date
|
||||||
having COUNT(1) > #{frequencyThreshold}
|
having COUNT(1) > #{frequencyThreshold}
|
||||||
@@ -466,8 +373,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
or IFNULL(bs.USER_MEMO, '') REGEXP '转帐|转账|汇入|转存|红包|汇款|网转|转入'
|
or IFNULL(bs.USER_MEMO, '') REGEXP '转帐|转账|汇入|转存|红包|汇款|网转|转入'
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '转帐|转账|汇入|转存|红包|汇款|网转|转入'
|
or IFNULL(bs.CASH_TYPE, '') REGEXP '转帐|转账|汇入|转存|红包|汇款|网转|转入'
|
||||||
)
|
)
|
||||||
|
and IFNULL(bs.USER_MEMO, '') NOT LIKE '%款%'
|
||||||
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
and IFNULL(bs.LE_ACCOUNT_NAME, '') <> IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
and (
|
and (
|
||||||
exists (
|
exists (
|
||||||
select 1
|
select 1
|
||||||
@@ -483,200 +390,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
)
|
)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectExternalSingleLargeAmountStatements" resultMap="BankTagStatementHitResultMap">
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(bs.LE_ACCOUNT_NAME, ''),
|
|
||||||
'”单笔交易金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR),
|
|
||||||
' 元,超过阈值 ', CAST(#{threshold} AS CHAR),
|
|
||||||
' 元,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''), '”'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > #{threshold}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 0
|
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalCumulativeTransactionAmountObjects" resultMap="BankTagObjectHitResultMap">
|
|
||||||
select
|
|
||||||
'EXTERNAL_CERT_NO' AS objectType,
|
|
||||||
t.certNo AS objectKey,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(t.personName, ''),
|
|
||||||
'”累计交易金额 ', CAST(t.totalAmount AS CHAR),
|
|
||||||
' 元,超过阈值 ', CAST(#{threshold} AS CHAR), ' 元'
|
|
||||||
) AS reasonDetail
|
|
||||||
from (
|
|
||||||
select
|
|
||||||
bs.cret_no AS certNo,
|
|
||||||
max(IFNULL(bs.LE_ACCOUNT_NAME, '')) AS personName,
|
|
||||||
ROUND(SUM(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0))), 2) AS totalAmount
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 0
|
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
group by bs.cret_no
|
|
||||||
having ROUND(SUM(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0))), 2) > #{threshold}
|
|
||||||
) t
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalAnnualTurnoverObjects" resultMap="BankTagObjectHitResultMap">
|
|
||||||
select
|
|
||||||
'EXTERNAL_CERT_NO' AS objectType,
|
|
||||||
t.certNo AS objectKey,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(t.personName, ''),
|
|
||||||
'”近一年流水交易额 ', CAST(t.annualAmount AS CHAR),
|
|
||||||
' 元,超过阈值 ', CAST(#{threshold} AS CHAR), ' 元'
|
|
||||||
) AS reasonDetail
|
|
||||||
from (
|
|
||||||
select
|
|
||||||
bs.cret_no AS certNo,
|
|
||||||
max(IFNULL(bs.LE_ACCOUNT_NAME, '')) AS personName,
|
|
||||||
ROUND(SUM(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0))), 2) AS annualAmount
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 0
|
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
and STR_TO_DATE(LEFT(TRIM(bs.TRX_DATE), 10), '%Y-%m-%d') >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
|
||||||
group by bs.cret_no
|
|
||||||
having ROUND(SUM(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0))), 2) > #{threshold}
|
|
||||||
) t
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectAbnormalCustomerTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
<select id="selectAbnormalCustomerTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
||||||
select
|
|
||||||
hit.bankStatementId AS bankStatementId,
|
|
||||||
max(hit.groupId) AS groupId,
|
|
||||||
max(hit.logId) AS logId,
|
|
||||||
substring_index(
|
|
||||||
min(concat(lpad(hit.matchPriority, 2, '0'), '|', hit.reasonDetail)),
|
|
||||||
'|',
|
|
||||||
-1
|
|
||||||
) AS reasonDetail
|
|
||||||
from (
|
|
||||||
select
|
select
|
||||||
bs.bank_statement_id AS bankStatementId,
|
bs.bank_statement_id AS bankStatementId,
|
||||||
bs.group_id AS groupId,
|
bs.group_id AS groupId,
|
||||||
bs.batch_id AS logId,
|
bs.batch_id AS logId,
|
||||||
1 AS matchPriority,
|
'占位SQL,待补充真实规则' AS reasonDetail
|
||||||
CONCAT(
|
|
||||||
subject.subjectType, '“', IFNULL(subject.subjectName, ''), '”与信贷客户账号发生交易,',
|
|
||||||
'金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR),
|
|
||||||
' 元,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''), '”'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement bs
|
||||||
inner join (
|
where 1 = 0
|
||||||
<include refid="abnormalCustomerTransactionSubjectSql"/>
|
|
||||||
) subject on subject.subjectCertNo = bs.cret_no
|
|
||||||
inner join ccdi_account_info account
|
|
||||||
on trim(IFNULL(bs.customer_account_no, '')) != ''
|
|
||||||
and account.owner_type = 'CREDIT_CUSTOMER'
|
|
||||||
and account.account_no = bs.customer_account_no
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
2 AS matchPriority,
|
|
||||||
CONCAT(
|
|
||||||
subject.subjectType, '“', IFNULL(subject.subjectName, ''), '”与中介账号发生交易,',
|
|
||||||
'金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR),
|
|
||||||
' 元,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''), '”'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
inner join (
|
|
||||||
<include refid="abnormalCustomerTransactionSubjectSql"/>
|
|
||||||
) subject on subject.subjectCertNo = bs.cret_no
|
|
||||||
inner join ccdi_account_info account
|
|
||||||
on trim(IFNULL(bs.customer_account_no, '')) != ''
|
|
||||||
and account.owner_type = 'INTERMEDIARY'
|
|
||||||
and account.account_no = bs.customer_account_no
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
3 AS matchPriority,
|
|
||||||
CONCAT(
|
|
||||||
subject.subjectType, '“', IFNULL(subject.subjectName, ''), '”与中介关联企业发生交易,',
|
|
||||||
'金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR),
|
|
||||||
' 元,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''), '”'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
inner join (
|
|
||||||
<include refid="abnormalCustomerTransactionSubjectSql"/>
|
|
||||||
) subject on subject.subjectCertNo = bs.cret_no
|
|
||||||
inner join ccdi_enterprise_base_info enterprise
|
|
||||||
on trim(IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')) != ''
|
|
||||||
and enterprise.enterprise_name = bs.CUSTOMER_ACCOUNT_NAME
|
|
||||||
and enterprise.ent_source = 'INTERMEDIARY'
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
4 AS matchPriority,
|
|
||||||
CONCAT(
|
|
||||||
subject.subjectType, '“', IFNULL(subject.subjectName, ''), '”与中介库人员发生微信/支付宝交易,',
|
|
||||||
'金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR),
|
|
||||||
' 元,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''), '”'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
inner join (
|
|
||||||
<include refid="abnormalCustomerTransactionSubjectSql"/>
|
|
||||||
) subject on subject.subjectCertNo = bs.cret_no
|
|
||||||
inner join ccdi_biz_intermediary intermediary
|
|
||||||
on trim(IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')) != ''
|
|
||||||
and trim(IFNULL(intermediary.name, '')) != ''
|
|
||||||
and bs.CUSTOMER_ACCOUNT_NAME like concat('%', intermediary.name, '%')
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
and bs.bank in ('ALIPAY', 'WECHAT')
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
5 AS matchPriority,
|
|
||||||
CONCAT(
|
|
||||||
subject.subjectType, '“', IFNULL(subject.subjectName, ''), '”与中介库人员发生名称精确匹配交易,',
|
|
||||||
'金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR),
|
|
||||||
' 元,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''), '”'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
inner join (
|
|
||||||
<include refid="abnormalCustomerTransactionSubjectSql"/>
|
|
||||||
) subject on subject.subjectCertNo = bs.cret_no
|
|
||||||
inner join ccdi_biz_intermediary intermediary
|
|
||||||
on trim(IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '')) != ''
|
|
||||||
and intermediary.name = bs.CUSTOMER_ACCOUNT_NAME
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
) hit
|
|
||||||
group by hit.bankStatementId
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectLowIncomeRelativeLargeTransactionObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectLowIncomeRelativeLargeTransactionObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
@@ -695,9 +416,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
from ccdi_staff_fmy_relation relation
|
from ccdi_staff_fmy_relation relation
|
||||||
inner join ccdi_bank_statement bs on relation.relation_cert_no = bs.cret_no
|
inner join ccdi_bank_statement bs on relation.relation_cert_no = bs.cret_no
|
||||||
where relation.status = 1
|
where relation.status = 1
|
||||||
and relation.annual_income is not null
|
|
||||||
and (
|
and (
|
||||||
relation.annual_income = 0
|
relation.annual_income is null
|
||||||
|
or relation.annual_income = 0
|
||||||
or relation.annual_income / 12 < 3000
|
or relation.annual_income / 12 < 3000
|
||||||
)
|
)
|
||||||
and bs.project_id = #{projectId}
|
and bs.project_id = #{projectId}
|
||||||
@@ -782,77 +503,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.AMOUNT_DR, 0) > 0
|
and IFNULL(bs.AMOUNT_DR, 0) > 0
|
||||||
and (
|
and (
|
||||||
IFNULL(bs.USER_MEMO, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌博|赌球|下注|投注|球赛投注|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|捕鱼|电子游艺|VIP666|USDT下注'
|
IFNULL(bs.USER_MEMO, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌|球|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|捕鱼|电子游艺|投注'
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌博|赌球|下注|投注|球赛投注|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|捕鱼|电子游艺|VIP666|USDT下注'
|
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌|球|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|捕鱼|电子游艺|投注'
|
||||||
)
|
)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectExternalGamblingMemoStatements" resultMap="BankTagStatementHitResultMap">
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(bs.LE_ACCOUNT_NAME, ''),
|
|
||||||
'”摘要/对手方命中疑似赌博关键词,摘要“', IFNULL(bs.USER_MEMO, ''),
|
|
||||||
'”,对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''),
|
|
||||||
'”,交易金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR), ' 元'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 0
|
|
||||||
and (
|
|
||||||
IFNULL(bs.USER_MEMO, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌博|赌球|下注|投注|球赛投注|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|牌局|捕鱼|电子游艺|VIP666|USDT下注'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌博|赌球|下注|投注|球赛投注|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|牌局|捕鱼|电子游艺|VIP666|USDT下注'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '游戏|抖币|体彩|福彩|彩票|赌博|赌球|下注|投注|球赛投注|外围|博彩|六合|时时彩|赛车|赌场|筹码|盘口|返水|洗码|庄家|闲家|百家乐|斗牛|炸金花|牌九|麻将|牌局|捕鱼|电子游艺|VIP666|USDT下注'
|
|
||||||
)
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalMultiPartyGamblingTransferObjects" resultMap="BankTagObjectHitResultMap">
|
|
||||||
select
|
|
||||||
'EXTERNAL_CERT_NO' AS objectType,
|
|
||||||
t.certNo AS objectKey,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(MAX(t.personName), ''),
|
|
||||||
'”交易日 ', MAX(t.tradeDate),
|
|
||||||
' 发生 ', CAST(MAX(t.hitCount) AS CHAR),
|
|
||||||
' 笔疑似赌博交易,涉及 ', CAST(MAX(t.partyCount) AS CHAR),
|
|
||||||
' 个对手方,金额合计 ', CAST(MAX(t.totalAmount) AS CHAR), ' 元'
|
|
||||||
) AS reasonDetail
|
|
||||||
from (
|
|
||||||
select
|
|
||||||
source.certNo AS certNo,
|
|
||||||
max(source.personName) AS personName,
|
|
||||||
source.tradeDate AS tradeDate,
|
|
||||||
COUNT(1) AS hitCount,
|
|
||||||
COUNT(DISTINCT source.customerAccountName) AS partyCount,
|
|
||||||
ROUND(SUM(source.tradeAmount), 2) AS totalAmount
|
|
||||||
from (
|
|
||||||
select
|
|
||||||
bs.cret_no AS certNo,
|
|
||||||
IFNULL(bs.LE_ACCOUNT_NAME, '') AS personName,
|
|
||||||
LEFT(TRIM(bs.TRX_DATE), 10) AS tradeDate,
|
|
||||||
bs.CUSTOMER_ACCOUNT_NAME AS customerAccountName,
|
|
||||||
GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS tradeAmount
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) between #{amountMinThreshold} and #{amountMaxThreshold}
|
|
||||||
and IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') <> ''
|
|
||||||
and (
|
|
||||||
IFNULL(bs.USER_MEMO, '') REGEXP '微信|wechat|WeChat|财付通|Tenpay|支付宝|Alipay|转账|红包|牌局|赌'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '微信|wechat|WeChat|财付通|Tenpay|支付宝|Alipay|转账|红包|牌局|赌'
|
|
||||||
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '微信|wechat|WeChat|财付通|Tenpay|支付宝|Alipay'
|
|
||||||
)
|
|
||||||
) source
|
|
||||||
group by source.certNo, source.tradeDate
|
|
||||||
having COUNT(1) > 2
|
|
||||||
and COUNT(DISTINCT source.customerAccountName) >= 2
|
|
||||||
) t
|
|
||||||
group by t.certNo
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectSpecialAmountTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
<select id="selectSpecialAmountTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
||||||
select
|
select
|
||||||
bs.bank_statement_id AS bankStatementId,
|
bs.bank_statement_id AS bankStatementId,
|
||||||
@@ -879,74 +534,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
)
|
)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectExternalToStaffOrFamilyTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
|
||||||
select distinct
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(bs.LE_ACCOUNT_NAME, ''),
|
|
||||||
'”与', CASE
|
|
||||||
WHEN counter_account.owner_type = 'EMPLOYEE' THEN '员工'
|
|
||||||
WHEN counter_account.owner_type = 'RELATION' THEN '员工亲属'
|
|
||||||
WHEN counter_staff.id_card is not null THEN '员工'
|
|
||||||
WHEN counter_relation.relation_cert_no is not null THEN '员工亲属'
|
|
||||||
ELSE '员工/员工亲属'
|
|
||||||
END,
|
|
||||||
'“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''),
|
|
||||||
'”发生资金往来,交易金额 ',
|
|
||||||
CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR), ' 元'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
left join ccdi_account_info counter_account
|
|
||||||
on trim(bs.CUSTOMER_ACCOUNT_NO) != ''
|
|
||||||
and counter_account.account_no = trim(bs.CUSTOMER_ACCOUNT_NO)
|
|
||||||
and counter_account.owner_type in ('EMPLOYEE', 'RELATION', 'INTERMEDIARY', 'CREDIT_CUSTOMER')
|
|
||||||
left join ccdi_base_staff counter_staff
|
|
||||||
on counter_account.account_no is null
|
|
||||||
and trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
|
||||||
and counter_staff.name = trim(bs.CUSTOMER_ACCOUNT_NAME)
|
|
||||||
left join ccdi_staff_fmy_relation counter_relation
|
|
||||||
on counter_account.account_no is null
|
|
||||||
and counter_relation.status = 1
|
|
||||||
and trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
|
||||||
and counter_relation.relation_name = trim(bs.CUSTOMER_ACCOUNT_NAME)
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 0
|
|
||||||
and (
|
|
||||||
counter_account.owner_type in ('EMPLOYEE', 'RELATION')
|
|
||||||
or (
|
|
||||||
counter_account.account_no is null
|
|
||||||
and (
|
|
||||||
counter_staff.id_card is not null
|
|
||||||
or counter_relation.relation_cert_no is not null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalNightTransactionStatements" resultMap="BankTagStatementHitResultMap">
|
|
||||||
select
|
|
||||||
bs.bank_statement_id AS bankStatementId,
|
|
||||||
bs.group_id AS groupId,
|
|
||||||
bs.batch_id AS logId,
|
|
||||||
CONCAT(
|
|
||||||
'外部人员“', IFNULL(bs.LE_ACCOUNT_NAME, ''),
|
|
||||||
'”夜间交易,交易时间 ', IFNULL(bs.TRX_DATE, ''),
|
|
||||||
',对手方“', IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''),
|
|
||||||
'”,交易金额 ', CAST(GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) AS CHAR), ' 元'
|
|
||||||
) AS reasonDetail
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and <include refid="externalPersonPredicateSql"/>
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 0
|
|
||||||
and (
|
|
||||||
HOUR(STR_TO_DATE(LEFT(TRIM(bs.TRX_DATE), 19), '%Y-%m-%d %H:%i:%s')) >= 22
|
|
||||||
or HOUR(STR_TO_DATE(LEFT(TRIM(bs.TRX_DATE), 19), '%Y-%m-%d %H:%i:%s')) < 6
|
|
||||||
)
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectMonthlyFixedIncomeObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectMonthlyFixedIncomeObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
select
|
select
|
||||||
'STAFF_ID_CARD' AS objectType,
|
'STAFF_ID_CARD' AS objectType,
|
||||||
@@ -1071,15 +658,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.AMOUNT_CR, 0) > 0
|
and IFNULL(bs.AMOUNT_CR, 0) > 0
|
||||||
and IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') <> '浙江兰溪农村商业银行股份有限公司'
|
and IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') <> '浙江兰溪农村商业银行股份有限公司'
|
||||||
and not (
|
|
||||||
IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') LIKE '%公积金中心%'
|
|
||||||
and (
|
|
||||||
IFNULL(bs.USER_MEMO, '') LIKE '%公积金%'
|
|
||||||
or IFNULL(bs.USER_MEMO, '') LIKE '%批量代付%'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') LIKE '%公积金%'
|
|
||||||
or IFNULL(bs.CASH_TYPE, '') LIKE '%批量代付%'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
and (
|
and (
|
||||||
IFNULL(bs.USER_MEMO, '') REGEXP '代发|工资|分红|红利|奖金|薪酬|薪金|补贴|薪|年终奖|年金|加班费|劳务费|劳务外包|提成|劳务派遣|绩效|酬劳|批量代付|PAYROLL|SALA|CPF|directors.*fees'
|
IFNULL(bs.USER_MEMO, '') REGEXP '代发|工资|分红|红利|奖金|薪酬|薪金|补贴|薪|年终奖|年金|加班费|劳务费|劳务外包|提成|劳务派遣|绩效|酬劳|批量代付|PAYROLL|SALA|CPF|directors.*fees'
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '代发|工资|劳务费'
|
or IFNULL(bs.CASH_TYPE, '') REGEXP '代发|工资|劳务费'
|
||||||
@@ -1477,7 +1055,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
or IFNULL(bs.USER_MEMO, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管|银证转账|银证|证转银|银转证'
|
or IFNULL(bs.USER_MEMO, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管|银证转账|银证|证转银|银转证'
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管|银证转账|银证|证转银|银转证'
|
or IFNULL(bs.CASH_TYPE, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管|银证转账|银证|证转银|银转证'
|
||||||
)
|
)
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectWithdrawCntObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectWithdrawCntObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
@@ -1497,8 +1074,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement bs
|
||||||
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.AMOUNT_CR, 0) > 0
|
and IFNULL(bs.AMOUNT_CR, 0) >= 0
|
||||||
and <include refid="thirdPartyWithdrawIncomePredicate"/>
|
and (
|
||||||
|
IFNULL(bs.USER_MEMO, '') REGEXP '财付通|微信零钱|微信|wechat|WeChat|Tenpay|支付宝|Alipay|提现'
|
||||||
|
or IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '财付通|微信零钱|微信|wechat|WeChat|Tenpay|支付宝|Alipay|提现'
|
||||||
|
)
|
||||||
group by staff.id_card, LEFT(TRIM(bs.TRX_DATE), 10)
|
group by staff.id_card, LEFT(TRIM(bs.TRX_DATE), 10)
|
||||||
having COUNT(1) > #{frequencyThreshold}
|
having COUNT(1) > #{frequencyThreshold}
|
||||||
) t
|
) t
|
||||||
@@ -1507,25 +1087,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<select id="selectWithdrawAmtObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectWithdrawAmtObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
select
|
select
|
||||||
'STAFF_ID_CARD' AS objectType,
|
'STAFF_ID_CARD' AS objectType,
|
||||||
t.objectKey AS objectKey,
|
'' AS objectKey,
|
||||||
CONCAT(
|
'占位SQL,待补充真实规则' AS reasonDetail
|
||||||
'单日微信/支付宝提现到账金额 ', CAST(t.withdrawAmount AS CHAR),
|
|
||||||
' 元,超过阈值 ', CAST(#{amountThreshold} AS CHAR),
|
|
||||||
' 元,交易日:', t.transDate
|
|
||||||
) AS reasonDetail
|
|
||||||
from (
|
|
||||||
select
|
|
||||||
staff.id_card AS objectKey,
|
|
||||||
LEFT(TRIM(bs.TRX_DATE), 10) AS transDate,
|
|
||||||
ROUND(SUM(IFNULL(bs.AMOUNT_CR, 0)), 2) AS withdrawAmount
|
|
||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement bs
|
||||||
inner join ccdi_base_staff staff on staff.id_card = bs.cret_no
|
where 1 = 0
|
||||||
where bs.project_id = #{projectId}
|
|
||||||
and IFNULL(bs.AMOUNT_CR, 0) > 0
|
|
||||||
and <include refid="thirdPartyWithdrawIncomePredicate"/>
|
|
||||||
group by staff.id_card, LEFT(TRIM(bs.TRX_DATE), 10)
|
|
||||||
having SUM(IFNULL(bs.AMOUNT_CR, 0)) > #{amountThreshold}
|
|
||||||
) t
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectSalaryQuickTransferObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectSalaryQuickTransferObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
@@ -1757,11 +1322,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where bs.project_id = #{projectId}
|
where bs.project_id = #{projectId}
|
||||||
and IFNULL(bs.AMOUNT_DR, 0) > #{threshold}
|
and IFNULL(bs.AMOUNT_DR, 0) > #{threshold}
|
||||||
and (
|
and (
|
||||||
IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管'
|
IFNULL(bs.CUSTOMER_ACCOUNT_NAME, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|理财|资金存管|第三方存管'
|
||||||
or IFNULL(bs.USER_MEMO, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管|银证转账|银证|证转银|银转证'
|
or IFNULL(bs.USER_MEMO, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|理财|资金存管|第三方存管'
|
||||||
or IFNULL(bs.CASH_TYPE, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|资金存管|第三方存管|银证转账|银证|证转银|银转证'
|
or IFNULL(bs.CASH_TYPE, '') REGEXP '证券|国泰君安|中信建投|中金|基金|期货|信托|同花顺|理财|资金存管|第三方存管'
|
||||||
)
|
)
|
||||||
and <include refid="financialProductExclusionPredicate"/>
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectProxyAccountOperationObjects" resultMap="BankTagObjectHitResultMap">
|
<select id="selectProxyAccountOperationObjects" resultMap="BankTagObjectHitResultMap">
|
||||||
|
|||||||
@@ -65,15 +65,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
limit 1
|
limit 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectLatestFailedTaskByProjectId" resultMap="CcdiBankTagTaskResultMap">
|
|
||||||
select id, project_id, trigger_type, model_code, status, need_rerun, success_rule_count,
|
|
||||||
failed_rule_count, hit_count, error_message, start_time, end_time,
|
|
||||||
create_by, create_time, update_by, update_time
|
|
||||||
from ccdi_bank_tag_task
|
|
||||||
where project_id = #{projectId}
|
|
||||||
and status = 'FAILED'
|
|
||||||
order by id desc
|
|
||||||
limit 1
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -1,475 +0,0 @@
|
|||||||
<?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.ccdi.project.mapper.CcdiFundGraphMapper">
|
|
||||||
|
|
||||||
<resultMap id="FundGraphNodeResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphNodeVO">
|
|
||||||
<id property="objectKey" column="objectKey"/>
|
|
||||||
<result property="nodeKey" column="nodeKey"/>
|
|
||||||
<result property="nodeName" column="nodeName"/>
|
|
||||||
<result property="idNo" column="idNo"/>
|
|
||||||
<result property="cinocsno" column="cinocsno"/>
|
|
||||||
<result property="idnoType" column="idnoType"/>
|
|
||||||
<result property="staffId" column="staffId"/>
|
|
||||||
<result property="sourceType" column="sourceType"/>
|
|
||||||
<result property="nodeType" column="nodeType"/>
|
|
||||||
<result property="identityType" column="identityType"/>
|
|
||||||
<result property="relationType" column="relationType"/>
|
|
||||||
<result property="accountCount" column="accountCount"/>
|
|
||||||
<result property="createdTime" column="createdTime"/>
|
|
||||||
<result property="updatedTime" column="updatedTime"/>
|
|
||||||
<result property="canExpand" column="canExpand"/>
|
|
||||||
<result property="depth" column="depth"/>
|
|
||||||
<result property="totalAmount" column="totalAmount"/>
|
|
||||||
<result property="transactionCount" column="transactionCount"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<resultMap id="FundGraphEdgeResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphEdgeVO">
|
|
||||||
<id property="edgeKey" column="edgeKey"/>
|
|
||||||
<result property="fromKey" column="fromKey"/>
|
|
||||||
<result property="toKey" column="toKey"/>
|
|
||||||
<result property="fromObjectKey" column="fromObjectKey"/>
|
|
||||||
<result property="toObjectKey" column="toObjectKey"/>
|
|
||||||
<result property="fromName" column="fromName"/>
|
|
||||||
<result property="toName" column="toName"/>
|
|
||||||
<result property="totalAmount" column="totalAmount"/>
|
|
||||||
<result property="transactionCount" column="transactionCount"/>
|
|
||||||
<result property="firstTrxDate" column="firstTrxDate"/>
|
|
||||||
<result property="lastTrxDate" column="lastTrxDate"/>
|
|
||||||
<result property="direction" column="direction"/>
|
|
||||||
<result property="familyRelationType" column="familyRelationType"/>
|
|
||||||
<result property="sourceType" column="sourceType"/>
|
|
||||||
<result property="relationDesc" column="relationDesc"/>
|
|
||||||
<result property="sourceDesc" column="sourceDesc"/>
|
|
||||||
<result property="remark" column="remark"/>
|
|
||||||
<result property="depth" column="depth"/>
|
|
||||||
<result property="canTrace" column="canTrace"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<resultMap id="FundGraphStatementResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiFundGraphStatementVO">
|
|
||||||
<id property="bankStatementId" column="bankStatementId"/>
|
|
||||||
<result property="trxDate" column="trxDate"/>
|
|
||||||
<result property="leAccountNo" column="leAccountNo"/>
|
|
||||||
<result property="leAccountName" column="leAccountName"/>
|
|
||||||
<result property="customerAccountName" column="customerAccountName"/>
|
|
||||||
<result property="customerAccountNo" column="customerAccountNo"/>
|
|
||||||
<result property="cashType" column="cashType"/>
|
|
||||||
<result property="userMemo" column="userMemo"/>
|
|
||||||
<result property="amount" column="amount"/>
|
|
||||||
<result property="direction" column="direction"/>
|
|
||||||
<result property="familyRelationType" column="familyRelationType"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<sql id="detailFilter">
|
|
||||||
<if test="query.transactionStartTime != null and query.transactionStartTime != ''">
|
|
||||||
AND d.trx_date <![CDATA[ >= ]]> (#{query.transactionStartTime} COLLATE utf8mb4_general_ci)
|
|
||||||
</if>
|
|
||||||
<if test="query.transactionEndTime != null and query.transactionEndTime != ''">
|
|
||||||
AND d.trx_date <![CDATA[ <= ]]>
|
|
||||||
(CASE
|
|
||||||
WHEN LENGTH(TRIM(#{query.transactionEndTime})) = 10
|
|
||||||
THEN CONCAT(TRIM(#{query.transactionEndTime}), ' 23:59:59')
|
|
||||||
ELSE TRIM(#{query.transactionEndTime})
|
|
||||||
END COLLATE utf8mb4_general_ci)
|
|
||||||
</if>
|
|
||||||
<if test="query.amountMin != null">
|
|
||||||
AND d.amount <![CDATA[ >= ]]> #{query.amountMin}
|
|
||||||
</if>
|
|
||||||
<if test="query.amountMax != null">
|
|
||||||
AND d.amount <![CDATA[ <= ]]> #{query.amountMax}
|
|
||||||
</if>
|
|
||||||
<if test="query.direction != null and query.direction != ''">
|
|
||||||
AND d.flag = (#{query.direction} COLLATE utf8mb4_general_ci)
|
|
||||||
</if>
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="subjectJoinRows">
|
|
||||||
SELECT
|
|
||||||
d.object_key AS detailObjectKey,
|
|
||||||
d.bank_statement_id AS bankStatementId,
|
|
||||||
d.trx_date AS trxDate,
|
|
||||||
d.le_account_no AS leAccountNo,
|
|
||||||
d.le_account_name AS leAccountName,
|
|
||||||
d.customer_account_name AS customerAccountName,
|
|
||||||
d.customer_account_no AS customerAccountNo,
|
|
||||||
d.cash_type AS cashType,
|
|
||||||
d.user_memo AS userMemo,
|
|
||||||
d.amount,
|
|
||||||
d.flag AS direction,
|
|
||||||
d.family_relation_type AS familyRelationType,
|
|
||||||
from_subject.object_key AS fromObjectKey,
|
|
||||||
to_subject.object_key AS toObjectKey,
|
|
||||||
CONCAT('idno_node/', from_subject.object_key) AS fromKey,
|
|
||||||
CONCAT('idno_node/', to_subject.object_key) AS toKey,
|
|
||||||
from_subject.name AS fromName,
|
|
||||||
to_subject.name AS toName,
|
|
||||||
from_subject.idnocfno AS fromIdNo,
|
|
||||||
to_subject.idnocfno AS toIdNo
|
|
||||||
FROM lx_fund_flow_detail_edge d
|
|
||||||
INNER JOIN lx_fund_flow_own_account_edge from_own
|
|
||||||
ON from_own.to_key = d.from_key
|
|
||||||
INNER JOIN lx_fund_flow_subject_node from_subject
|
|
||||||
ON CONCAT('idno_node/', from_subject.object_key) = from_own.from_key
|
|
||||||
INNER JOIN lx_fund_flow_own_account_edge to_own
|
|
||||||
ON to_own.to_key = d.to_key
|
|
||||||
INNER JOIN lx_fund_flow_subject_node to_subject
|
|
||||||
ON CONCAT('idno_node/', to_subject.object_key) = to_own.from_key
|
|
||||||
WHERE 1 = 1
|
|
||||||
<include refid="detailFilter"/>
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="subjectJoinRowsByCenter">
|
|
||||||
SELECT
|
|
||||||
d.object_key AS detailObjectKey,
|
|
||||||
d.bank_statement_id AS bankStatementId,
|
|
||||||
d.trx_date AS trxDate,
|
|
||||||
d.le_account_no AS leAccountNo,
|
|
||||||
d.le_account_name AS leAccountName,
|
|
||||||
d.customer_account_name AS customerAccountName,
|
|
||||||
d.customer_account_no AS customerAccountNo,
|
|
||||||
d.cash_type AS cashType,
|
|
||||||
d.user_memo AS userMemo,
|
|
||||||
d.amount,
|
|
||||||
d.flag AS direction,
|
|
||||||
d.family_relation_type AS familyRelationType,
|
|
||||||
center_subject.object_key AS fromObjectKey,
|
|
||||||
to_subject.object_key AS toObjectKey,
|
|
||||||
CONCAT('idno_node/', center_subject.object_key) AS fromKey,
|
|
||||||
CONCAT('idno_node/', to_subject.object_key) AS toKey,
|
|
||||||
center_subject.name AS fromName,
|
|
||||||
to_subject.name AS toName,
|
|
||||||
center_subject.idnocfno AS fromIdNo,
|
|
||||||
to_subject.idnocfno AS toIdNo
|
|
||||||
FROM lx_fund_flow_subject_node center_subject
|
|
||||||
INNER JOIN lx_fund_flow_own_account_edge from_own
|
|
||||||
ON from_own.from_key = CONCAT('idno_node/', center_subject.object_key)
|
|
||||||
INNER JOIN lx_fund_flow_detail_edge d
|
|
||||||
ON d.from_key = from_own.to_key
|
|
||||||
INNER JOIN lx_fund_flow_own_account_edge to_own
|
|
||||||
ON to_own.to_key = d.to_key
|
|
||||||
INNER JOIN lx_fund_flow_subject_node to_subject
|
|
||||||
ON to_subject.object_key = SUBSTRING(to_own.from_key, 11)
|
|
||||||
WHERE center_subject.object_key = (#{query.objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
<include refid="detailFilter"/>
|
|
||||||
UNION ALL
|
|
||||||
SELECT
|
|
||||||
d.object_key AS detailObjectKey,
|
|
||||||
d.bank_statement_id AS bankStatementId,
|
|
||||||
d.trx_date AS trxDate,
|
|
||||||
d.le_account_no AS leAccountNo,
|
|
||||||
d.le_account_name AS leAccountName,
|
|
||||||
d.customer_account_name AS customerAccountName,
|
|
||||||
d.customer_account_no AS customerAccountNo,
|
|
||||||
d.cash_type AS cashType,
|
|
||||||
d.user_memo AS userMemo,
|
|
||||||
d.amount,
|
|
||||||
d.flag AS direction,
|
|
||||||
d.family_relation_type AS familyRelationType,
|
|
||||||
from_subject.object_key AS fromObjectKey,
|
|
||||||
center_subject.object_key AS toObjectKey,
|
|
||||||
CONCAT('idno_node/', from_subject.object_key) AS fromKey,
|
|
||||||
CONCAT('idno_node/', center_subject.object_key) AS toKey,
|
|
||||||
from_subject.name AS fromName,
|
|
||||||
center_subject.name AS toName,
|
|
||||||
from_subject.idnocfno AS fromIdNo,
|
|
||||||
center_subject.idnocfno AS toIdNo
|
|
||||||
FROM lx_fund_flow_subject_node center_subject
|
|
||||||
INNER JOIN lx_fund_flow_own_account_edge to_own
|
|
||||||
ON to_own.from_key = CONCAT('idno_node/', center_subject.object_key)
|
|
||||||
INNER JOIN lx_fund_flow_detail_edge d
|
|
||||||
ON d.to_key = to_own.to_key
|
|
||||||
INNER JOIN lx_fund_flow_own_account_edge from_own
|
|
||||||
ON from_own.to_key = d.from_key
|
|
||||||
INNER JOIN lx_fund_flow_subject_node from_subject
|
|
||||||
ON from_subject.object_key = SUBSTRING(from_own.from_key, 11)
|
|
||||||
WHERE center_subject.object_key = (#{query.objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
AND from_subject.object_key != center_subject.object_key
|
|
||||||
<include refid="detailFilter"/>
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="fundGraphSubjectColumns">
|
|
||||||
n.object_key AS objectKey,
|
|
||||||
CONCAT('idno_node/', n.object_key) AS nodeKey,
|
|
||||||
n.name AS nodeName,
|
|
||||||
n.idnocfno AS idNo,
|
|
||||||
n.cinocsno AS cinocsno,
|
|
||||||
n.idno_type AS idnoType,
|
|
||||||
n.staff_id AS staffId,
|
|
||||||
n.source_type AS sourceType,
|
|
||||||
CASE
|
|
||||||
WHEN n.idno_type = 'NAME_PROXY' OR n.source_type LIKE '%COUNTERPARTY%' THEN 'PROXY'
|
|
||||||
ELSE 'PERSON'
|
|
||||||
END AS nodeType,
|
|
||||||
CASE
|
|
||||||
WHEN n.idnocfno IS NOT NULL AND TRIM(n.idnocfno) != '' THEN 'IDNO'
|
|
||||||
ELSE 'NAME'
|
|
||||||
END AS identityType,
|
|
||||||
CASE
|
|
||||||
WHEN n.source_type LIKE 'GRAPH_TEST_FAMILY_%' THEN REPLACE(n.source_type, 'GRAPH_TEST_FAMILY_', '')
|
|
||||||
ELSE NULL
|
|
||||||
END AS relationType,
|
|
||||||
CASE
|
|
||||||
WHEN EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM lx_fund_flow_own_account_edge own
|
|
||||||
WHERE own.from_key = CONCAT('idno_node/', n.object_key)
|
|
||||||
) THEN 1
|
|
||||||
ELSE 0
|
|
||||||
END AS canExpand,
|
|
||||||
(
|
|
||||||
SELECT COUNT(1)
|
|
||||||
FROM lx_fund_flow_own_account_edge own_count
|
|
||||||
WHERE own_count.from_key = CONCAT('idno_node/', n.object_key)
|
|
||||||
) AS accountCount,
|
|
||||||
DATE_FORMAT(n.created_time, '%Y-%m-%d %H:%i:%s') AS createdTime,
|
|
||||||
DATE_FORMAT(n.updated_time, '%Y-%m-%d %H:%i:%s') AS updatedTime,
|
|
||||||
0 AS depth,
|
|
||||||
0 AS totalAmount,
|
|
||||||
0 AS transactionCount
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<select id="selectFundGraphSubjects" resultMap="FundGraphNodeResultMap">
|
|
||||||
SELECT
|
|
||||||
<include refid="fundGraphSubjectColumns"/>
|
|
||||||
FROM lx_fund_flow_subject_node n
|
|
||||||
WHERE n.object_key = (#{query.objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectFundGraphSubjectsByExactKeyword" resultMap="FundGraphNodeResultMap">
|
|
||||||
SELECT exact_rows.*
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
<include refid="fundGraphSubjectColumns"/>,
|
|
||||||
0 AS matchOrder
|
|
||||||
FROM lx_fund_flow_subject_node n
|
|
||||||
WHERE n.idnocfno = (TRIM(#{query.keyword}) COLLATE utf8mb4_general_ci)
|
|
||||||
UNION ALL
|
|
||||||
SELECT
|
|
||||||
<include refid="fundGraphSubjectColumns"/>,
|
|
||||||
1 AS matchOrder
|
|
||||||
FROM lx_fund_flow_subject_node n
|
|
||||||
WHERE n.object_key = (TRIM(#{query.keyword}) COLLATE utf8mb4_general_ci)
|
|
||||||
AND (
|
|
||||||
n.idnocfno IS NULL
|
|
||||||
OR n.idnocfno != (TRIM(#{query.keyword}) COLLATE utf8mb4_general_ci)
|
|
||||||
)
|
|
||||||
) exact_rows
|
|
||||||
ORDER BY exact_rows.matchOrder, exact_rows.nodeName
|
|
||||||
LIMIT
|
|
||||||
<choose>
|
|
||||||
<when test="query.limit != null and query.limit > 0">
|
|
||||||
#{query.limit}
|
|
||||||
</when>
|
|
||||||
<otherwise>
|
|
||||||
20
|
|
||||||
</otherwise>
|
|
||||||
</choose>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectFundGraphSubjectsByName" resultMap="FundGraphNodeResultMap">
|
|
||||||
SELECT
|
|
||||||
<include refid="fundGraphSubjectColumns"/>
|
|
||||||
FROM lx_fund_flow_subject_node n
|
|
||||||
WHERE n.name LIKE (CONCAT('%', TRIM(#{query.keyword}), '%') COLLATE utf8mb4_general_ci)
|
|
||||||
ORDER BY
|
|
||||||
CASE
|
|
||||||
WHEN n.staff_id IS NOT NULL AND TRIM(n.staff_id) != '' THEN 0
|
|
||||||
WHEN UPPER(IFNULL(n.source_type, '')) LIKE '%EMPLOYEE%' THEN 0
|
|
||||||
WHEN n.source_type LIKE '%员工%' THEN 0
|
|
||||||
ELSE 1
|
|
||||||
END,
|
|
||||||
n.name
|
|
||||||
LIMIT
|
|
||||||
<choose>
|
|
||||||
<when test="query.limit != null and query.limit > 0">
|
|
||||||
#{query.limit}
|
|
||||||
</when>
|
|
||||||
<otherwise>
|
|
||||||
20
|
|
||||||
</otherwise>
|
|
||||||
</choose>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectFundGraphEdges" resultMap="FundGraphEdgeResultMap">
|
|
||||||
SELECT
|
|
||||||
MD5(CONCAT(graph_rows.fromKey, '|', graph_rows.toKey, '|', graph_rows.direction, '|', IFNULL(graph_rows.familyRelationType, ''))) AS edgeKey,
|
|
||||||
graph_rows.fromKey,
|
|
||||||
graph_rows.toKey,
|
|
||||||
graph_rows.fromObjectKey,
|
|
||||||
graph_rows.toObjectKey,
|
|
||||||
MAX(graph_rows.fromName) AS fromName,
|
|
||||||
MAX(graph_rows.toName) AS toName,
|
|
||||||
SUM(graph_rows.amount) AS totalAmount,
|
|
||||||
COUNT(1) AS transactionCount,
|
|
||||||
MIN(graph_rows.trxDate) AS firstTrxDate,
|
|
||||||
MAX(graph_rows.trxDate) AS lastTrxDate,
|
|
||||||
graph_rows.direction,
|
|
||||||
graph_rows.familyRelationType,
|
|
||||||
'BANK' AS sourceType,
|
|
||||||
NULL AS relationDesc,
|
|
||||||
NULL AS sourceDesc,
|
|
||||||
NULL AS remark,
|
|
||||||
1 AS depth,
|
|
||||||
CASE
|
|
||||||
WHEN MAX(
|
|
||||||
CASE
|
|
||||||
WHEN graph_rows.fromObjectKey = #{query.objectKey} THEN
|
|
||||||
CASE
|
|
||||||
WHEN graph_rows.toIdNo IS NOT NULL AND TRIM(graph_rows.toIdNo) != '' THEN 1
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
ELSE
|
|
||||||
CASE
|
|
||||||
WHEN graph_rows.fromIdNo IS NOT NULL AND TRIM(graph_rows.fromIdNo) != '' THEN 1
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
END
|
|
||||||
) = 1 THEN 1
|
|
||||||
WHEN EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM lx_fund_flow_own_account_edge own
|
|
||||||
WHERE own.from_key = CASE
|
|
||||||
WHEN graph_rows.fromObjectKey = #{query.objectKey}
|
|
||||||
THEN graph_rows.toKey
|
|
||||||
ELSE graph_rows.fromKey
|
|
||||||
END
|
|
||||||
) THEN 1
|
|
||||||
ELSE 0
|
|
||||||
END AS canTrace
|
|
||||||
FROM (
|
|
||||||
<include refid="subjectJoinRowsByCenter"/>
|
|
||||||
) graph_rows
|
|
||||||
WHERE 1 = 1
|
|
||||||
GROUP BY
|
|
||||||
graph_rows.fromKey,
|
|
||||||
graph_rows.toKey,
|
|
||||||
graph_rows.fromObjectKey,
|
|
||||||
graph_rows.toObjectKey,
|
|
||||||
graph_rows.direction,
|
|
||||||
graph_rows.familyRelationType
|
|
||||||
<if test="query.minTotalAmount != null">
|
|
||||||
HAVING SUM(graph_rows.amount) <![CDATA[ >= ]]> #{query.minTotalAmount}
|
|
||||||
</if>
|
|
||||||
ORDER BY totalAmount DESC, transactionCount DESC
|
|
||||||
LIMIT #{query.limit}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectFundGraphManualEdges" resultMap="FundGraphEdgeResultMap">
|
|
||||||
SELECT
|
|
||||||
m.object_key AS edgeKey,
|
|
||||||
CONCAT('idno_node/', m.from_object_key) AS fromKey,
|
|
||||||
CONCAT('idno_node/', m.to_object_key) AS toKey,
|
|
||||||
m.from_object_key AS fromObjectKey,
|
|
||||||
m.to_object_key AS toObjectKey,
|
|
||||||
COALESCE(from_subject.name, m.from_name) AS fromName,
|
|
||||||
COALESCE(to_subject.name, m.to_name) AS toName,
|
|
||||||
m.amount AS totalAmount,
|
|
||||||
m.transaction_count AS transactionCount,
|
|
||||||
DATE_FORMAT(m.created_time, '%Y-%m-%d %H:%i:%s') AS firstTrxDate,
|
|
||||||
DATE_FORMAT(m.created_time, '%Y-%m-%d %H:%i:%s') AS lastTrxDate,
|
|
||||||
m.direction,
|
|
||||||
NULL AS familyRelationType,
|
|
||||||
m.source_type AS sourceType,
|
|
||||||
m.relation_desc AS relationDesc,
|
|
||||||
m.source_desc AS sourceDesc,
|
|
||||||
m.remark,
|
|
||||||
1 AS depth,
|
|
||||||
0 AS canTrace
|
|
||||||
FROM lx_fund_flow_manual_edge m
|
|
||||||
LEFT JOIN lx_fund_flow_subject_node from_subject
|
|
||||||
ON from_subject.object_key = m.from_object_key
|
|
||||||
LEFT JOIN lx_fund_flow_subject_node to_subject
|
|
||||||
ON to_subject.object_key = m.to_object_key
|
|
||||||
WHERE m.source_type = 'MANUAL'
|
|
||||||
AND (
|
|
||||||
m.from_object_key = (#{query.objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
OR m.to_object_key = (#{query.objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
)
|
|
||||||
ORDER BY m.updated_time DESC
|
|
||||||
LIMIT #{query.limit}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectFundGraphEdgeDetails" resultMap="FundGraphStatementResultMap">
|
|
||||||
SELECT
|
|
||||||
graph_rows.bankStatementId,
|
|
||||||
graph_rows.trxDate,
|
|
||||||
graph_rows.leAccountNo,
|
|
||||||
graph_rows.leAccountName,
|
|
||||||
graph_rows.customerAccountName,
|
|
||||||
graph_rows.customerAccountNo,
|
|
||||||
graph_rows.cashType,
|
|
||||||
graph_rows.userMemo,
|
|
||||||
graph_rows.amount,
|
|
||||||
graph_rows.direction,
|
|
||||||
graph_rows.familyRelationType
|
|
||||||
FROM (
|
|
||||||
<include refid="subjectJoinRows"/>
|
|
||||||
) graph_rows
|
|
||||||
WHERE graph_rows.fromKey = (#{query.fromKey} COLLATE utf8mb4_general_ci)
|
|
||||||
AND graph_rows.toKey = (#{query.toKey} COLLATE utf8mb4_general_ci)
|
|
||||||
<if test="query.direction != null and query.direction != ''">
|
|
||||||
AND graph_rows.direction = (#{query.direction} COLLATE utf8mb4_general_ci)
|
|
||||||
</if>
|
|
||||||
ORDER BY graph_rows.trxDate DESC, graph_rows.bankStatementId DESC
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="countSubjectByObjectKey" resultType="int">
|
|
||||||
SELECT COUNT(1)
|
|
||||||
FROM lx_fund_flow_subject_node
|
|
||||||
WHERE object_key = (#{objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<insert id="insertManualSubject">
|
|
||||||
INSERT IGNORE INTO lx_fund_flow_subject_node (
|
|
||||||
object_key,
|
|
||||||
idnocfno,
|
|
||||||
name,
|
|
||||||
idno_type,
|
|
||||||
source_type
|
|
||||||
) VALUES (
|
|
||||||
#{objectKey},
|
|
||||||
#{idNo},
|
|
||||||
#{name},
|
|
||||||
CASE
|
|
||||||
WHEN #{idNo} IS NULL OR TRIM(#{idNo}) = '' THEN 'NAME_PROXY'
|
|
||||||
ELSE 'PERSON'
|
|
||||||
END,
|
|
||||||
'MANUAL'
|
|
||||||
)
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<insert id="insertManualEdge">
|
|
||||||
INSERT INTO lx_fund_flow_manual_edge (
|
|
||||||
object_key,
|
|
||||||
from_object_key,
|
|
||||||
to_object_key,
|
|
||||||
from_name,
|
|
||||||
to_name,
|
|
||||||
amount,
|
|
||||||
transaction_count,
|
|
||||||
direction,
|
|
||||||
relation_desc,
|
|
||||||
source_desc,
|
|
||||||
remark,
|
|
||||||
source_type,
|
|
||||||
created_by,
|
|
||||||
updated_by
|
|
||||||
) VALUES (
|
|
||||||
#{objectKey},
|
|
||||||
#{dto.fromObjectKey},
|
|
||||||
#{dto.toObjectKey},
|
|
||||||
#{dto.fromName},
|
|
||||||
#{dto.toName},
|
|
||||||
#{dto.amount},
|
|
||||||
#{dto.transactionCount},
|
|
||||||
#{dto.direction},
|
|
||||||
#{dto.relationDesc},
|
|
||||||
#{dto.sourceDesc},
|
|
||||||
#{dto.remark},
|
|
||||||
'MANUAL',
|
|
||||||
#{operator},
|
|
||||||
#{operator}
|
|
||||||
)
|
|
||||||
</insert>
|
|
||||||
</mapper>
|
|
||||||
@@ -40,9 +40,6 @@
|
|||||||
FROM ccdi_project p
|
FROM ccdi_project p
|
||||||
LEFT JOIN sys_user u ON p.create_by = u.user_name AND u.del_flag = '0'
|
LEFT JOIN sys_user u ON p.create_by = u.user_name AND u.del_flag = '0'
|
||||||
<where>
|
<where>
|
||||||
<if test="scope != null and !scope.viewAllProjects">
|
|
||||||
AND p.create_by = #{scope.username}
|
|
||||||
</if>
|
|
||||||
<if test="queryDTO.projectName != null and queryDTO.projectName != ''">
|
<if test="queryDTO.projectName != null and queryDTO.projectName != ''">
|
||||||
AND p.project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%')
|
AND p.project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%')
|
||||||
</if>
|
</if>
|
||||||
@@ -64,9 +61,6 @@
|
|||||||
FROM ccdi_project p
|
FROM ccdi_project p
|
||||||
<where>
|
<where>
|
||||||
p.status in ('1', '2')
|
p.status in ('1', '2')
|
||||||
<if test="scope != null and !scope.viewAllProjects">
|
|
||||||
AND p.create_by = #{scope.username}
|
|
||||||
</if>
|
|
||||||
<if test="queryDTO.projectName != null and queryDTO.projectName != ''">
|
<if test="queryDTO.projectName != null and queryDTO.projectName != ''">
|
||||||
AND p.project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%')
|
AND p.project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%')
|
||||||
</if>
|
</if>
|
||||||
@@ -76,8 +70,7 @@
|
|||||||
|
|
||||||
<update id="updateRiskCountsByProjectId">
|
<update id="updateRiskCountsByProjectId">
|
||||||
update ccdi_project
|
update ccdi_project
|
||||||
set target_count = #{targetCount},
|
set high_risk_count = #{highRiskCount},
|
||||||
high_risk_count = #{highRiskCount},
|
|
||||||
medium_risk_count = #{mediumRiskCount},
|
medium_risk_count = #{mediumRiskCount},
|
||||||
low_risk_count = #{lowRiskCount},
|
low_risk_count = #{lowRiskCount},
|
||||||
update_by = #{updateBy},
|
update_by = #{updateBy},
|
||||||
|
|||||||
@@ -33,38 +33,6 @@
|
|||||||
select="selectRiskHitTagsByScope"/>
|
select="selectRiskHitTagsByScope"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<resultMap id="ExternalRiskModelPeopleItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO">
|
|
||||||
<id property="idNo" column="cert_no"/>
|
|
||||||
<result property="staffName" column="person_name"/>
|
|
||||||
<result property="staffCode" column="subject_type"/>
|
|
||||||
<result property="department" column="related_object"/>
|
|
||||||
<collection property="modelNames"
|
|
||||||
column="{projectId=project_id,certNo=cert_no,selectedModelCodes=selected_model_codes}"
|
|
||||||
ofType="java.lang.String"
|
|
||||||
select="selectExternalRiskModelNamesByScope"/>
|
|
||||||
<collection property="hitTagList"
|
|
||||||
column="{projectId=project_id,certNo=cert_no,selectedModelCodes=selected_model_codes}"
|
|
||||||
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO"
|
|
||||||
select="selectExternalRiskHitTagsByScope"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<resultMap id="ExternalPersonWarningItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalPersonWarningItemVO">
|
|
||||||
<result property="name" column="person_name"/>
|
|
||||||
<result property="idNo" column="cert_no"/>
|
|
||||||
<result property="subjectType" column="subject_type"/>
|
|
||||||
<result property="riskLevel" column="risk_level_name"/>
|
|
||||||
<result property="riskLevelType" column="risk_level_type"/>
|
|
||||||
<result property="riskCount" column="risk_count"/>
|
|
||||||
<result property="modelCount" column="model_count"/>
|
|
||||||
<result property="riskPoint" column="risk_point"/>
|
|
||||||
<result property="relatedObject" column="related_object"/>
|
|
||||||
<result property="latestTradeTime" column="latest_trade_time"/>
|
|
||||||
<collection property="riskPointTagList"
|
|
||||||
column="{projectId=project_id,certNo=cert_no,selectedModelCodes=selected_model_codes}"
|
|
||||||
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO"
|
|
||||||
select="selectExternalRiskHitTagsByScope"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<resultMap id="SuspiciousTransactionItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO">
|
<resultMap id="SuspiciousTransactionItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO">
|
||||||
<id property="bankStatementId" column="bankStatementId"/>
|
<id property="bankStatementId" column="bankStatementId"/>
|
||||||
<result property="trxDate" column="trxDate"/>
|
<result property="trxDate" column="trxDate"/>
|
||||||
@@ -78,7 +46,6 @@
|
|||||||
<result property="displayAmount" column="displayAmount"/>
|
<result property="displayAmount" column="displayAmount"/>
|
||||||
<result property="hasModelRuleHit" column="hasModelRuleHit"/>
|
<result property="hasModelRuleHit" column="hasModelRuleHit"/>
|
||||||
<result property="hasNameListHit" column="hasNameListHit"/>
|
<result property="hasNameListHit" column="hasNameListHit"/>
|
||||||
<result property="nameListHitType" column="nameListHitType"/>
|
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<resultMap id="AbnormalAccountItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountItemVO">
|
<resultMap id="AbnormalAccountItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountItemVO">
|
||||||
@@ -147,13 +114,6 @@
|
|||||||
) tens
|
) tens
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="externalModelCodeFilterSql">
|
|
||||||
('EXTERNAL_LARGE_TRANSACTION',
|
|
||||||
'EXTERNAL_ABNORMAL_TRANSACTION',
|
|
||||||
'EXTERNAL_SUSPICIOUS_GAMBLING',
|
|
||||||
'EXTERNAL_SUSPICIOUS_RELATION')
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="resolvedEmployeeRiskBaseSql">
|
<sql id="resolvedEmployeeRiskBaseSql">
|
||||||
select distinct
|
select distinct
|
||||||
tr.id,
|
tr.id,
|
||||||
@@ -533,408 +493,6 @@
|
|||||||
order by result.staff_name asc, result.staff_id_card asc
|
order by result.staff_name asc, result.staff_id_card asc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectRiskModelPeopleList" resultMap="RiskModelPeopleItemResultMap">
|
|
||||||
<bind name="projectId" value="query.projectId"/>
|
|
||||||
select
|
|
||||||
result.project_id,
|
|
||||||
result.staff_id_card,
|
|
||||||
result.staff_name,
|
|
||||||
result.staff_code,
|
|
||||||
result.dept_name as department,
|
|
||||||
#{query.modelCodesCsv} as selected_model_codes
|
|
||||||
from ccdi_project_overview_employee_result result
|
|
||||||
where 1 = 1
|
|
||||||
and result.project_id = #{query.projectId}
|
|
||||||
<if test="query.modelCodes != null and query.modelCodes.size() > 0">
|
|
||||||
<choose>
|
|
||||||
<when test="query.matchMode == 'ALL'">
|
|
||||||
<foreach collection="query.modelCodes" item="modelCode">
|
|
||||||
and find_in_set(#{modelCode}, result.model_codes_csv)
|
|
||||||
</foreach>
|
|
||||||
</when>
|
|
||||||
<otherwise>
|
|
||||||
and (
|
|
||||||
<foreach collection="query.modelCodes" item="modelCode" separator=" or ">
|
|
||||||
find_in_set(#{modelCode}, result.model_codes_csv)
|
|
||||||
</foreach>
|
|
||||||
)
|
|
||||||
</otherwise>
|
|
||||||
</choose>
|
|
||||||
</if>
|
|
||||||
<if test="query.keyword != null and query.keyword != ''">
|
|
||||||
and (
|
|
||||||
result.staff_name like concat('%', trim(#{query.keyword}), '%')
|
|
||||||
or result.staff_code like concat('%', trim(#{query.keyword}), '%')
|
|
||||||
)
|
|
||||||
</if>
|
|
||||||
<if test="query.deptId != null">
|
|
||||||
and result.dept_id = #{query.deptId}
|
|
||||||
</if>
|
|
||||||
order by result.staff_name asc, result.staff_id_card asc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<sql id="externalPersonSubjectSql">
|
|
||||||
select
|
|
||||||
bs.project_id,
|
|
||||||
bs.cret_no as cert_no,
|
|
||||||
coalesce(
|
|
||||||
max(intermediary.name),
|
|
||||||
max(customer.name),
|
|
||||||
max(nullif(trim(bs.LE_ACCOUNT_NAME), '')),
|
|
||||||
'外部人员'
|
|
||||||
) as person_name,
|
|
||||||
case
|
|
||||||
when max(case when intermediary.person_id is not null then 1 else 0 end) > 0 then '中介'
|
|
||||||
when max(case when customer.person_id is not null then 1 else 0 end) > 0 then '客户'
|
|
||||||
else '外部人员'
|
|
||||||
end as subject_type
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
left join ccdi_base_staff staff
|
|
||||||
on staff.id_card = bs.cret_no
|
|
||||||
left join ccdi_staff_fmy_relation relation
|
|
||||||
on relation.status = 1
|
|
||||||
and relation.relation_cert_no = bs.cret_no
|
|
||||||
left join ccdi_biz_intermediary intermediary
|
|
||||||
on intermediary.person_sub_type = '本人'
|
|
||||||
and intermediary.person_id = bs.cret_no
|
|
||||||
left join ccdi_credit_customer_base customer
|
|
||||||
on customer.person_id = bs.cret_no
|
|
||||||
where bs.project_id = #{externalProjectId}
|
|
||||||
and bs.cret_no is not null
|
|
||||||
and trim(bs.cret_no) != ''
|
|
||||||
and staff.id_card is null
|
|
||||||
and relation.relation_cert_no is null
|
|
||||||
group by bs.project_id, bs.cret_no
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="externalPersonSourceSql">
|
|
||||||
select
|
|
||||||
subject.project_id,
|
|
||||||
subject.cert_no,
|
|
||||||
subject.person_name,
|
|
||||||
subject.subject_type,
|
|
||||||
bs.bank_statement_id,
|
|
||||||
bs.TRX_DATE as trx_date,
|
|
||||||
bs.CUSTOMER_ACCOUNT_NAME as customer_account_name,
|
|
||||||
bs.customer_cert_no,
|
|
||||||
case
|
|
||||||
when counter_account.owner_type = 'EMPLOYEE' then '员工'
|
|
||||||
when counter_account.owner_type = 'RELATION' then '员工亲属'
|
|
||||||
when counter_account.owner_type = 'CREDIT_CUSTOMER' then '信贷客户'
|
|
||||||
when counter_account.owner_type = 'INTERMEDIARY' then '中介库人员'
|
|
||||||
when counter_staff.id_card is not null then '员工'
|
|
||||||
when counter_relation.relation_cert_no is not null then '员工亲属'
|
|
||||||
when counter_intermediary.person_id is not null then '中介库人员'
|
|
||||||
else null
|
|
||||||
end as related_object,
|
|
||||||
tr.model_code,
|
|
||||||
tr.model_name,
|
|
||||||
tr.rule_code,
|
|
||||||
tr.rule_name,
|
|
||||||
tr.risk_level,
|
|
||||||
tr.reason_detail
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSubjectSql"/>
|
|
||||||
) subject
|
|
||||||
inner join ccdi_bank_statement bs
|
|
||||||
on bs.project_id = subject.project_id
|
|
||||||
and bs.cret_no = subject.cert_no
|
|
||||||
inner join ccdi_bank_statement_tag_result tr
|
|
||||||
on tr.project_id = bs.project_id
|
|
||||||
and tr.bank_statement_id = bs.bank_statement_id
|
|
||||||
and tr.model_code in <include refid="externalModelCodeFilterSql"/>
|
|
||||||
left join ccdi_account_info counter_account
|
|
||||||
on trim(bs.CUSTOMER_ACCOUNT_NO) != ''
|
|
||||||
and counter_account.account_no = trim(bs.CUSTOMER_ACCOUNT_NO)
|
|
||||||
and counter_account.owner_type in ('EMPLOYEE', 'RELATION', 'INTERMEDIARY', 'CREDIT_CUSTOMER')
|
|
||||||
left join ccdi_base_staff counter_staff
|
|
||||||
on counter_account.account_no is null
|
|
||||||
and trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
|
||||||
and counter_staff.name = trim(bs.CUSTOMER_ACCOUNT_NAME)
|
|
||||||
left join ccdi_staff_fmy_relation counter_relation
|
|
||||||
on counter_account.account_no is null
|
|
||||||
and counter_relation.status = 1
|
|
||||||
and trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
|
||||||
and counter_relation.relation_name = trim(bs.CUSTOMER_ACCOUNT_NAME)
|
|
||||||
left join ccdi_biz_intermediary counter_intermediary
|
|
||||||
on counter_account.account_no is null
|
|
||||||
and counter_intermediary.person_sub_type = '本人'
|
|
||||||
and trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
|
||||||
and counter_intermediary.name = trim(bs.CUSTOMER_ACCOUNT_NAME)
|
|
||||||
where trim(ifnull(bs.LE_ACCOUNT_NAME, '')) != trim(ifnull(bs.CUSTOMER_ACCOUNT_NAME, ''))
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
subject.project_id,
|
|
||||||
subject.cert_no,
|
|
||||||
subject.person_name,
|
|
||||||
subject.subject_type,
|
|
||||||
null as bank_statement_id,
|
|
||||||
null as trx_date,
|
|
||||||
null as customer_account_name,
|
|
||||||
null as customer_cert_no,
|
|
||||||
'资金' as related_object,
|
|
||||||
tr.model_code,
|
|
||||||
tr.model_name,
|
|
||||||
tr.rule_code,
|
|
||||||
tr.rule_name,
|
|
||||||
tr.risk_level,
|
|
||||||
tr.reason_detail
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSubjectSql"/>
|
|
||||||
) subject
|
|
||||||
inner join ccdi_bank_statement_tag_result tr
|
|
||||||
on tr.project_id = subject.project_id
|
|
||||||
and tr.object_type = 'EXTERNAL_CERT_NO'
|
|
||||||
and tr.object_key = subject.cert_no
|
|
||||||
and tr.model_code in <include refid="externalModelCodeFilterSql"/>
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="externalPersonAggregateSql">
|
|
||||||
select
|
|
||||||
source.project_id,
|
|
||||||
source.cert_no,
|
|
||||||
max(source.person_name) as person_name,
|
|
||||||
max(source.subject_type) as subject_type,
|
|
||||||
count(*) as risk_count,
|
|
||||||
count(distinct source.model_code) as model_count,
|
|
||||||
group_concat(distinct source.rule_name order by source.rule_name separator '、') as risk_point,
|
|
||||||
group_concat(distinct source.related_object order by source.related_object separator '、') as related_object,
|
|
||||||
max(source.trx_date) as latest_trade_time,
|
|
||||||
case
|
|
||||||
when sum(case when source.risk_level = 'HIGH' then 1 else 0 end) > 0 then 'HIGH'
|
|
||||||
when sum(case when source.risk_level = 'MEDIUM' then 1 else 0 end) > 0 then 'MEDIUM'
|
|
||||||
else 'LOW'
|
|
||||||
end as risk_level_code,
|
|
||||||
null as selected_model_codes
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
group by source.project_id, source.cert_no
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="externalPersonWarningSelectSql">
|
|
||||||
select
|
|
||||||
agg.project_id,
|
|
||||||
agg.cert_no,
|
|
||||||
agg.person_name,
|
|
||||||
agg.subject_type,
|
|
||||||
agg.risk_count,
|
|
||||||
agg.model_count,
|
|
||||||
agg.risk_point,
|
|
||||||
coalesce(agg.related_object, '-') as related_object,
|
|
||||||
agg.latest_trade_time,
|
|
||||||
agg.risk_level_code,
|
|
||||||
case
|
|
||||||
when agg.risk_level_code = 'HIGH' then '高风险'
|
|
||||||
when agg.risk_level_code = 'MEDIUM' then '中风险'
|
|
||||||
else '低风险'
|
|
||||||
end as risk_level_name,
|
|
||||||
case
|
|
||||||
when agg.risk_level_code = 'HIGH' then 'danger'
|
|
||||||
when agg.risk_level_code = 'MEDIUM' then 'warning'
|
|
||||||
else 'info'
|
|
||||||
end as risk_level_type,
|
|
||||||
case
|
|
||||||
when agg.risk_level_code = 'HIGH' then 1
|
|
||||||
when agg.risk_level_code = 'MEDIUM' then 2
|
|
||||||
else 3
|
|
||||||
end as risk_level_sort,
|
|
||||||
agg.selected_model_codes
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonAggregateSql"/>
|
|
||||||
) agg
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<select id="selectExternalPersonWarningPage" resultMap="ExternalPersonWarningItemResultMap">
|
|
||||||
<bind name="externalProjectId" value="query.projectId"/>
|
|
||||||
select *
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonWarningSelectSql"/>
|
|
||||||
) warning
|
|
||||||
order by warning.risk_level_sort asc, warning.model_count desc, warning.risk_count desc, warning.latest_trade_time desc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalPersonWarningList" resultMap="ExternalPersonWarningItemResultMap">
|
|
||||||
<bind name="externalProjectId" value="projectId"/>
|
|
||||||
select *
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonWarningSelectSql"/>
|
|
||||||
) warning
|
|
||||||
order by warning.risk_level_sort asc, warning.model_count desc, warning.risk_count desc, warning.latest_trade_time desc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalRiskSummaryByProjectId" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectExternalRiskSummaryVO">
|
|
||||||
<bind name="externalProjectId" value="projectId"/>
|
|
||||||
select
|
|
||||||
count(*) as total,
|
|
||||||
coalesce(sum(case when risk.risk_level_code = 'HIGH' then 1 else 0 end), 0) as high,
|
|
||||||
coalesce(sum(case when risk.risk_level_code = 'MEDIUM' then 1 else 0 end), 0) as medium,
|
|
||||||
coalesce(sum(case when risk.risk_level_code = 'LOW' then 1 else 0 end), 0) as low,
|
|
||||||
coalesce(sum(case when risk.risk_level_code is null then 1 else 0 end), 0) as noRisk
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSubjectSql"/>
|
|
||||||
) subject
|
|
||||||
left join (
|
|
||||||
<include refid="externalPersonAggregateSql"/>
|
|
||||||
) risk
|
|
||||||
on risk.project_id = subject.project_id
|
|
||||||
and risk.cert_no = subject.cert_no
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalRiskModelCardsByProjectId" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO">
|
|
||||||
<bind name="externalProjectId" value="projectId"/>
|
|
||||||
select
|
|
||||||
model_scope.model_code,
|
|
||||||
max(model_scope.model_name) as model_name,
|
|
||||||
count(*) as warning_count,
|
|
||||||
count(distinct model_scope.cert_no) as people_count
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) model_scope
|
|
||||||
group by model_scope.model_code
|
|
||||||
order by warning_count desc, model_scope.model_code asc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalRiskModelPeoplePage" resultMap="ExternalRiskModelPeopleItemResultMap">
|
|
||||||
<bind name="externalProjectId" value="query.projectId"/>
|
|
||||||
select
|
|
||||||
warning.project_id,
|
|
||||||
warning.cert_no,
|
|
||||||
warning.person_name,
|
|
||||||
warning.subject_type,
|
|
||||||
warning.related_object,
|
|
||||||
#{query.modelCodesCsv} as selected_model_codes
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonWarningSelectSql"/>
|
|
||||||
) warning
|
|
||||||
where 1 = 1
|
|
||||||
<if test="query.modelCodes != null and query.modelCodes.size() > 0">
|
|
||||||
<choose>
|
|
||||||
<when test="query.matchMode == 'ALL'">
|
|
||||||
<foreach collection="query.modelCodes" item="modelCode">
|
|
||||||
and exists (
|
|
||||||
select 1
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
where source.cert_no = warning.cert_no
|
|
||||||
and source.model_code = #{modelCode}
|
|
||||||
)
|
|
||||||
</foreach>
|
|
||||||
</when>
|
|
||||||
<otherwise>
|
|
||||||
and exists (
|
|
||||||
select 1
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
where source.cert_no = warning.cert_no
|
|
||||||
and source.model_code in
|
|
||||||
<foreach collection="query.modelCodes" item="modelCode" open="(" separator="," close=")">
|
|
||||||
#{modelCode}
|
|
||||||
</foreach>
|
|
||||||
)
|
|
||||||
</otherwise>
|
|
||||||
</choose>
|
|
||||||
</if>
|
|
||||||
<if test="query.keyword != null and query.keyword != ''">
|
|
||||||
and (
|
|
||||||
warning.person_name like concat('%', trim(#{query.keyword}), '%')
|
|
||||||
or warning.cert_no like concat('%', trim(#{query.keyword}), '%')
|
|
||||||
)
|
|
||||||
</if>
|
|
||||||
order by warning.person_name asc, warning.cert_no asc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalRiskModelPeopleList" resultMap="ExternalRiskModelPeopleItemResultMap">
|
|
||||||
<bind name="externalProjectId" value="query.projectId"/>
|
|
||||||
select
|
|
||||||
warning.project_id,
|
|
||||||
warning.cert_no,
|
|
||||||
warning.person_name,
|
|
||||||
warning.subject_type,
|
|
||||||
warning.related_object,
|
|
||||||
#{query.modelCodesCsv} as selected_model_codes
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonWarningSelectSql"/>
|
|
||||||
) warning
|
|
||||||
where 1 = 1
|
|
||||||
<if test="query.modelCodes != null and query.modelCodes.size() > 0">
|
|
||||||
<choose>
|
|
||||||
<when test="query.matchMode == 'ALL'">
|
|
||||||
<foreach collection="query.modelCodes" item="modelCode">
|
|
||||||
and exists (
|
|
||||||
select 1
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
where source.cert_no = warning.cert_no
|
|
||||||
and source.model_code = #{modelCode}
|
|
||||||
)
|
|
||||||
</foreach>
|
|
||||||
</when>
|
|
||||||
<otherwise>
|
|
||||||
and exists (
|
|
||||||
select 1
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
where source.cert_no = warning.cert_no
|
|
||||||
and source.model_code in
|
|
||||||
<foreach collection="query.modelCodes" item="modelCode" open="(" separator="," close=")">
|
|
||||||
#{modelCode}
|
|
||||||
</foreach>
|
|
||||||
)
|
|
||||||
</otherwise>
|
|
||||||
</choose>
|
|
||||||
</if>
|
|
||||||
<if test="query.keyword != null and query.keyword != ''">
|
|
||||||
and (
|
|
||||||
warning.person_name like concat('%', trim(#{query.keyword}), '%')
|
|
||||||
or warning.cert_no like concat('%', trim(#{query.keyword}), '%')
|
|
||||||
)
|
|
||||||
</if>
|
|
||||||
order by warning.person_name asc, warning.cert_no asc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalRiskModelNamesByScope" resultType="java.lang.String">
|
|
||||||
<bind name="externalProjectId" value="projectId"/>
|
|
||||||
select distinct source.model_name
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
where source.cert_no = #{certNo}
|
|
||||||
<if test="selectedModelCodes != null and selectedModelCodes != ''">
|
|
||||||
and find_in_set(source.model_code, #{selectedModelCodes})
|
|
||||||
</if>
|
|
||||||
order by source.model_name asc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectExternalRiskHitTagsByScope" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO">
|
|
||||||
<bind name="externalProjectId" value="projectId"/>
|
|
||||||
select
|
|
||||||
source.model_code as modelCode,
|
|
||||||
max(source.model_name) as modelName,
|
|
||||||
source.rule_code as ruleCode,
|
|
||||||
max(source.rule_name) as ruleName,
|
|
||||||
max(source.risk_level) as riskLevel,
|
|
||||||
coalesce(
|
|
||||||
max(case when source.bank_statement_id is null then nullif(source.reason_detail, '') end),
|
|
||||||
max(nullif(source.reason_detail, ''))
|
|
||||||
) as reasonDetail
|
|
||||||
from (
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
where source.cert_no = #{certNo}
|
|
||||||
<if test="selectedModelCodes != null and selectedModelCodes != ''">
|
|
||||||
and find_in_set(source.model_code, #{selectedModelCodes})
|
|
||||||
</if>
|
|
||||||
group by source.model_code, source.rule_code
|
|
||||||
order by source.model_code asc, source.rule_code asc
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<sql id="suspiciousTransactionBaseSql">
|
<sql id="suspiciousTransactionBaseSql">
|
||||||
select
|
select
|
||||||
bs.bank_statement_id as bankStatementId,
|
bs.bank_statement_id as bankStatementId,
|
||||||
@@ -972,70 +530,57 @@
|
|||||||
from ccdi_bank_statement_tag_result tr
|
from ccdi_bank_statement_tag_result tr
|
||||||
where tr.project_id = #{query.projectId}
|
where tr.project_id = #{query.projectId}
|
||||||
and tr.bank_statement_id is not null
|
and tr.bank_statement_id is not null
|
||||||
|
and tr.rule_name like '%可疑%'
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="suspiciousTransactionNameHitSql">
|
<sql id="suspiciousTransactionNameHitSql">
|
||||||
select
|
select
|
||||||
hits.bankStatementId,
|
hits.bankStatementId,
|
||||||
hits.suspiciousPersonName,
|
hits.suspiciousPersonName,
|
||||||
hits.matchPriority,
|
hits.matchPriority
|
||||||
hits.nameListHitType
|
|
||||||
from (
|
from (
|
||||||
select
|
|
||||||
bs.bank_statement_id as bankStatementId,
|
|
||||||
coalesce(credit_customer.name, account.account_name, '信贷客户账号') as suspiciousPersonName,
|
|
||||||
1 as matchPriority,
|
|
||||||
'信贷客户' as nameListHitType
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
inner join ccdi_account_info account
|
|
||||||
on trim(bs.customer_account_no) != ''
|
|
||||||
and account.owner_type = 'CREDIT_CUSTOMER'
|
|
||||||
and account.account_no = bs.customer_account_no
|
|
||||||
left join ccdi_credit_customer_base credit_customer
|
|
||||||
on credit_customer.person_id = account.owner_id
|
|
||||||
where bs.project_id = #{query.projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
bs.bank_statement_id as bankStatementId,
|
|
||||||
coalesce(intermediary.name, enterprise.enterprise_name, account.account_name, '中介账号') as suspiciousPersonName,
|
|
||||||
2 as matchPriority,
|
|
||||||
'中介' as nameListHitType
|
|
||||||
from ccdi_bank_statement bs
|
|
||||||
inner join ccdi_account_info account
|
|
||||||
on trim(bs.customer_account_no) != ''
|
|
||||||
and account.owner_type = 'INTERMEDIARY'
|
|
||||||
and account.account_no = bs.customer_account_no
|
|
||||||
left join ccdi_biz_intermediary intermediary
|
|
||||||
on intermediary.person_id = account.owner_id
|
|
||||||
left join ccdi_enterprise_base_info enterprise
|
|
||||||
on enterprise.social_credit_code = account.owner_id
|
|
||||||
where bs.project_id = #{query.projectId}
|
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
select
|
||||||
bs.bank_statement_id as bankStatementId,
|
bs.bank_statement_id as bankStatementId,
|
||||||
intermediary.name as suspiciousPersonName,
|
intermediary.name as suspiciousPersonName,
|
||||||
3 as matchPriority,
|
1 as matchPriority
|
||||||
'中介' as nameListHitType
|
|
||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement bs
|
||||||
inner join ccdi_biz_intermediary intermediary
|
inner join ccdi_biz_intermediary intermediary
|
||||||
on trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
on trim(bs.customer_cert_no) != ''
|
||||||
and intermediary.name = bs.CUSTOMER_ACCOUNT_NAME
|
and intermediary.person_id = bs.customer_cert_no
|
||||||
where bs.project_id = #{query.projectId}
|
where bs.project_id = #{query.projectId}
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
|
|
||||||
union all
|
union all
|
||||||
|
|
||||||
select
|
select
|
||||||
bs.bank_statement_id as bankStatementId,
|
bs.bank_statement_id as bankStatementId,
|
||||||
enterprise.enterprise_name as suspiciousPersonName,
|
enterprise.enterprise_name as suspiciousPersonName,
|
||||||
4 as matchPriority,
|
2 as matchPriority
|
||||||
'中介' as nameListHitType
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_enterprise_base_info enterprise
|
||||||
|
on trim(bs.customer_social_credit_code) != ''
|
||||||
|
and enterprise.social_credit_code = bs.customer_social_credit_code
|
||||||
|
and enterprise.risk_level = '1'
|
||||||
|
and enterprise.ent_source = 'INTERMEDIARY'
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
intermediary.name as suspiciousPersonName,
|
||||||
|
3 as matchPriority
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_biz_intermediary intermediary
|
||||||
|
on trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
||||||
|
and intermediary.name = bs.CUSTOMER_ACCOUNT_NAME
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
enterprise.enterprise_name as suspiciousPersonName,
|
||||||
|
3 as matchPriority
|
||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement bs
|
||||||
inner join ccdi_enterprise_base_info enterprise
|
inner join ccdi_enterprise_base_info enterprise
|
||||||
on trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
on trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
||||||
@@ -1043,38 +588,9 @@
|
|||||||
and enterprise.risk_level = '1'
|
and enterprise.risk_level = '1'
|
||||||
and enterprise.ent_source = 'INTERMEDIARY'
|
and enterprise.ent_source = 'INTERMEDIARY'
|
||||||
where bs.project_id = #{query.projectId}
|
where bs.project_id = #{query.projectId}
|
||||||
and GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000
|
|
||||||
) hits
|
) hits
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="externalSuspiciousTransactionSql">
|
|
||||||
select
|
|
||||||
bs.bank_statement_id as bankStatementId,
|
|
||||||
bs.TRX_DATE as trxDate,
|
|
||||||
source.person_name as relatedPersonName,
|
|
||||||
null as relatedStaffName,
|
|
||||||
null as relatedStaffCode,
|
|
||||||
source.subject_type as relationType,
|
|
||||||
bs.USER_MEMO as userMemo,
|
|
||||||
bs.CASH_TYPE as cashType,
|
|
||||||
case
|
|
||||||
when ifnull(bs.AMOUNT_CR, 0) > 0 then bs.AMOUNT_CR
|
|
||||||
when ifnull(bs.AMOUNT_DR, 0) > 0 then -bs.AMOUNT_DR
|
|
||||||
else 0
|
|
||||||
end as displayAmount,
|
|
||||||
1 as hasModelRuleHit,
|
|
||||||
0 as hasNameListHit,
|
|
||||||
source.person_name as suspiciousPersonName,
|
|
||||||
9 as matchPriority,
|
|
||||||
'外部人员预警' as nameListHitType
|
|
||||||
from (
|
|
||||||
<bind name="externalProjectId" value="query.projectId"/>
|
|
||||||
<include refid="externalPersonSourceSql"/>
|
|
||||||
) source
|
|
||||||
inner join ccdi_bank_statement bs
|
|
||||||
on bs.bank_statement_id = source.bank_statement_id
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<sql id="suspiciousTransactionMergedSql">
|
<sql id="suspiciousTransactionMergedSql">
|
||||||
select
|
select
|
||||||
base.bankStatementId,
|
base.bankStatementId,
|
||||||
@@ -1089,8 +605,7 @@
|
|||||||
1 as hasModelRuleHit,
|
1 as hasModelRuleHit,
|
||||||
0 as hasNameListHit,
|
0 as hasNameListHit,
|
||||||
null as suspiciousPersonName,
|
null as suspiciousPersonName,
|
||||||
null as matchPriority,
|
null as matchPriority
|
||||||
null as nameListHitType
|
|
||||||
from (
|
from (
|
||||||
<include refid="suspiciousTransactionBaseSql"/>
|
<include refid="suspiciousTransactionBaseSql"/>
|
||||||
) base
|
) base
|
||||||
@@ -1113,35 +628,13 @@
|
|||||||
0 as hasModelRuleHit,
|
0 as hasModelRuleHit,
|
||||||
1 as hasNameListHit,
|
1 as hasNameListHit,
|
||||||
name_hits.suspiciousPersonName,
|
name_hits.suspiciousPersonName,
|
||||||
name_hits.matchPriority,
|
name_hits.matchPriority
|
||||||
name_hits.nameListHitType
|
|
||||||
from (
|
from (
|
||||||
<include refid="suspiciousTransactionBaseSql"/>
|
<include refid="suspiciousTransactionBaseSql"/>
|
||||||
) base
|
) base
|
||||||
inner join (
|
inner join (
|
||||||
<include refid="suspiciousTransactionNameHitSql"/>
|
<include refid="suspiciousTransactionNameHitSql"/>
|
||||||
) name_hits on name_hits.bankStatementId = base.bankStatementId
|
) name_hits on name_hits.bankStatementId = base.bankStatementId
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
external_hits.bankStatementId,
|
|
||||||
external_hits.trxDate,
|
|
||||||
external_hits.relatedPersonName,
|
|
||||||
external_hits.relatedStaffName,
|
|
||||||
external_hits.relatedStaffCode,
|
|
||||||
external_hits.relationType,
|
|
||||||
external_hits.userMemo,
|
|
||||||
external_hits.cashType,
|
|
||||||
external_hits.displayAmount,
|
|
||||||
external_hits.hasModelRuleHit,
|
|
||||||
external_hits.hasNameListHit,
|
|
||||||
external_hits.suspiciousPersonName,
|
|
||||||
external_hits.matchPriority,
|
|
||||||
external_hits.nameListHitType
|
|
||||||
from (
|
|
||||||
<include refid="externalSuspiciousTransactionSql"/>
|
|
||||||
) external_hits
|
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="suspiciousTransactionAggregatedSql">
|
<sql id="suspiciousTransactionAggregatedSql">
|
||||||
@@ -1170,18 +663,7 @@
|
|||||||
max(merged.cashType) as cashType,
|
max(merged.cashType) as cashType,
|
||||||
max(merged.displayAmount) as displayAmount,
|
max(merged.displayAmount) as displayAmount,
|
||||||
max(merged.hasModelRuleHit) as hasModelRuleHit,
|
max(merged.hasModelRuleHit) as hasModelRuleHit,
|
||||||
max(merged.hasNameListHit) as hasNameListHit,
|
max(merged.hasNameListHit) as hasNameListHit
|
||||||
substring_index(
|
|
||||||
min(
|
|
||||||
case
|
|
||||||
when merged.nameListHitType is not null and merged.nameListHitType != ''
|
|
||||||
then concat(lpad(merged.matchPriority, 2, '0'), '|', merged.nameListHitType)
|
|
||||||
else null
|
|
||||||
end
|
|
||||||
),
|
|
||||||
'|',
|
|
||||||
-1
|
|
||||||
) as nameListHitType
|
|
||||||
from (
|
from (
|
||||||
<include refid="suspiciousTransactionMergedSql"/>
|
<include refid="suspiciousTransactionMergedSql"/>
|
||||||
) merged
|
) merged
|
||||||
@@ -1196,9 +678,6 @@
|
|||||||
<when test="query.suspiciousType == 'MODEL_RULE'">
|
<when test="query.suspiciousType == 'MODEL_RULE'">
|
||||||
where final_result.hasModelRuleHit = 1
|
where final_result.hasModelRuleHit = 1
|
||||||
</when>
|
</when>
|
||||||
<when test="query.suspiciousType == 'EXTERNAL_PERSON'">
|
|
||||||
where final_result.nameListHitType = '外部人员预警'
|
|
||||||
</when>
|
|
||||||
<otherwise>
|
<otherwise>
|
||||||
where final_result.hasModelRuleHit = 1 or final_result.hasNameListHit = 1
|
where final_result.hasModelRuleHit = 1 or final_result.hasNameListHit = 1
|
||||||
</otherwise>
|
</otherwise>
|
||||||
@@ -1206,7 +685,7 @@
|
|||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectSuspiciousTransactionPage" resultMap="SuspiciousTransactionItemResultMap">
|
<select id="selectSuspiciousTransactionPage" resultMap="SuspiciousTransactionItemResultMap">
|
||||||
<!-- ccdi_bank_statement_tag_result -->
|
<!-- rule_name like '%可疑%' -->
|
||||||
<!-- ccdi_biz_intermediary -->
|
<!-- ccdi_biz_intermediary -->
|
||||||
<!-- ccdi_enterprise_base_info -->
|
<!-- ccdi_enterprise_base_info -->
|
||||||
<!-- group by merged.bankStatementId -->
|
<!-- group by merged.bankStatementId -->
|
||||||
@@ -1222,8 +701,7 @@
|
|||||||
final_result.cashType,
|
final_result.cashType,
|
||||||
final_result.displayAmount,
|
final_result.displayAmount,
|
||||||
final_result.hasModelRuleHit,
|
final_result.hasModelRuleHit,
|
||||||
final_result.hasNameListHit,
|
final_result.hasNameListHit
|
||||||
final_result.nameListHitType
|
|
||||||
from (
|
from (
|
||||||
<include refid="suspiciousTransactionAggregatedSql"/>
|
<include refid="suspiciousTransactionAggregatedSql"/>
|
||||||
) final_result
|
) final_result
|
||||||
@@ -1244,8 +722,7 @@
|
|||||||
final_result.cashType,
|
final_result.cashType,
|
||||||
final_result.displayAmount,
|
final_result.displayAmount,
|
||||||
final_result.hasModelRuleHit,
|
final_result.hasModelRuleHit,
|
||||||
final_result.hasNameListHit,
|
final_result.hasNameListHit
|
||||||
final_result.nameListHitType
|
|
||||||
from (
|
from (
|
||||||
<include refid="suspiciousTransactionAggregatedSql"/>
|
<include refid="suspiciousTransactionAggregatedSql"/>
|
||||||
) final_result
|
) final_result
|
||||||
@@ -1265,21 +742,7 @@
|
|||||||
final_result.relatedStaffCode,
|
final_result.relatedStaffCode,
|
||||||
final_result.userMemo,
|
final_result.userMemo,
|
||||||
final_result.cashType,
|
final_result.cashType,
|
||||||
case
|
tag_result.hitTags,
|
||||||
when final_result.nameListHitType = '中介' then
|
|
||||||
replace(
|
|
||||||
replace(ifnull(tag_result.hitTags, ''), '与客户之间非正常资金往来', '疑似与中介往来'),
|
|
||||||
'异常交易',
|
|
||||||
'疑似与中介往来'
|
|
||||||
)
|
|
||||||
when final_result.nameListHitType = '信贷客户' then
|
|
||||||
replace(
|
|
||||||
replace(ifnull(tag_result.hitTags, ''), '与客户之间非正常资金往来', '与信贷客户之间非正常资金往来'),
|
|
||||||
'异常交易',
|
|
||||||
'与信贷客户之间非正常资金往来'
|
|
||||||
)
|
|
||||||
else tag_result.hitTags
|
|
||||||
end as hitTags,
|
|
||||||
final_result.displayAmount
|
final_result.displayAmount
|
||||||
from (
|
from (
|
||||||
<include refid="suspiciousTransactionAggregatedSql"/>
|
<include refid="suspiciousTransactionAggregatedSql"/>
|
||||||
@@ -1444,8 +907,7 @@
|
|||||||
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].modelName')))) as model_name,
|
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].modelName')))) as model_name,
|
||||||
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) as rule_code,
|
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) as rule_code,
|
||||||
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleName')))) as rule_name,
|
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleName')))) as rule_name,
|
||||||
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].riskLevel')))) as risk_level,
|
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].riskLevel')))) as risk_level
|
||||||
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].reasonDetail')))) as reason_detail
|
|
||||||
from ccdi_project_overview_employee_result result
|
from ccdi_project_overview_employee_result result
|
||||||
join (
|
join (
|
||||||
<include refid="jsonArrayIndexSql"/>
|
<include refid="jsonArrayIndexSql"/>
|
||||||
@@ -1567,5 +1029,4 @@
|
|||||||
group by base.staff_id_card
|
group by base.staff_id_card
|
||||||
) agg
|
) agg
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
<result property="spouseTotalAsset" column="asset_spouse_total_asset"/>
|
<result property="spouseTotalAsset" column="asset_spouse_total_asset"/>
|
||||||
<result property="totalAsset" column="asset_total_asset"/>
|
<result property="totalAsset" column="asset_total_asset"/>
|
||||||
<collection property="items"
|
<collection property="items"
|
||||||
column="{projectId=project_id,staffIdCard=staff_id_card,spouseIdCard=spouse_id_card,spouseIsStaff=spouse_is_staff}"
|
column="{projectId=project_id,staffIdCard=staff_id_card,spouseIdCard=spouse_id_card}"
|
||||||
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetItemVO"
|
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetItemVO"
|
||||||
select="selectFamilyAssetItemsByScope"/>
|
select="selectFamilyAssetItemsByScope"/>
|
||||||
</association>
|
</association>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
<result property="spouseTotalDebt" column="debt_spouse_total_debt"/>
|
<result property="spouseTotalDebt" column="debt_spouse_total_debt"/>
|
||||||
<result property="totalDebt" column="debt_total_debt"/>
|
<result property="totalDebt" column="debt_total_debt"/>
|
||||||
<collection property="items"
|
<collection property="items"
|
||||||
column="{projectId=project_id,staffIdCard=staff_id_card,spouseIdCard=spouse_id_card,spouseIsStaff=spouse_is_staff}"
|
column="{projectId=project_id,staffIdCard=staff_id_card,spouseIdCard=spouse_id_card}"
|
||||||
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtItemVO"
|
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtItemVO"
|
||||||
select="selectFamilyDebtItemsByScope"/>
|
select="selectFamilyDebtItemsByScope"/>
|
||||||
</association>
|
</association>
|
||||||
@@ -114,55 +114,45 @@
|
|||||||
|
|
||||||
<sql id="projectEmployeeScopeSql">
|
<sql id="projectEmployeeScopeSql">
|
||||||
select distinct
|
select distinct
|
||||||
statement_staff.id_card as staff_id_card,
|
coalesce(direct_staff.id_card, statement_staff.id_card, family_staff.id_card) as staff_id_card,
|
||||||
cast(statement_staff.staff_id as char) as staff_code,
|
cast(coalesce(direct_staff.staff_id, statement_staff.staff_id, family_staff.staff_id) as char) as staff_code,
|
||||||
statement_staff.name as staff_name,
|
coalesce(direct_staff.name, statement_staff.name, family_staff.name) as staff_name,
|
||||||
dept.dept_name
|
dept.dept_name
|
||||||
from ccdi_bank_statement bs
|
from ccdi_bank_statement_tag_result tr
|
||||||
inner join ccdi_base_staff statement_staff
|
left join ccdi_base_staff direct_staff
|
||||||
on statement_staff.id_card = trim(bs.cret_no)
|
on tr.object_type = 'STAFF_ID_CARD'
|
||||||
|
and tr.object_key = direct_staff.id_card
|
||||||
|
left join ccdi_bank_statement bs
|
||||||
|
on tr.bank_statement_id = bs.bank_statement_id
|
||||||
|
left join ccdi_base_staff statement_staff
|
||||||
|
on (tr.object_key is null or tr.object_key = '')
|
||||||
|
and bs.cret_no = statement_staff.id_card
|
||||||
|
left join ccdi_staff_fmy_relation relation
|
||||||
|
on relation.status = 1
|
||||||
|
and (
|
||||||
|
((tr.object_key is null or tr.object_key = '') and bs.cret_no = relation.relation_cert_no)
|
||||||
|
or ((tr.object_key is not null and tr.object_key != '') and tr.object_type != 'STAFF_ID_CARD'
|
||||||
|
and tr.object_key = relation.relation_cert_no)
|
||||||
|
)
|
||||||
|
left join ccdi_base_staff family_staff
|
||||||
|
on relation.person_id = family_staff.id_card
|
||||||
left join sys_dept dept
|
left join sys_dept dept
|
||||||
on dept.dept_id = statement_staff.dept_id
|
on dept.dept_id = coalesce(direct_staff.dept_id, statement_staff.dept_id, family_staff.dept_id)
|
||||||
where bs.project_id = #{projectId}
|
where tr.project_id = #{projectId}
|
||||||
and bs.cret_no is not null
|
and coalesce(direct_staff.id_card, statement_staff.id_card, family_staff.id_card) is not null
|
||||||
and trim(bs.cret_no) != ''
|
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="spouseRelationSql">
|
<sql id="spouseRelationSql">
|
||||||
select
|
select
|
||||||
relation_pair.person_id,
|
person_id,
|
||||||
max(relation_pair.spouse_name) as spouse_name,
|
max(relation_name) as spouse_name,
|
||||||
min(relation_pair.spouse_id_card) as spouse_id_card,
|
min(relation_cert_no) as spouse_id_card,
|
||||||
max(coalesce(spouse_staff.annual_income, relation_pair.spouse_relation_income, 0)) as spouse_income,
|
max(annual_income) as spouse_income
|
||||||
max(case when spouse_staff.id_card is not null then 1 else 0 end) as spouse_is_staff
|
from ccdi_staff_fmy_relation
|
||||||
from (
|
where status = 1
|
||||||
select
|
and is_emp_family = 1
|
||||||
relation.person_id,
|
and relation_type = '配偶'
|
||||||
relation.relation_name as spouse_name,
|
group by person_id
|
||||||
relation.relation_cert_no as spouse_id_card,
|
|
||||||
relation.annual_income as spouse_relation_income
|
|
||||||
from ccdi_staff_fmy_relation relation
|
|
||||||
where relation.status = 1
|
|
||||||
and relation.is_emp_family = 1
|
|
||||||
and relation.relation_type = '配偶'
|
|
||||||
union all
|
|
||||||
select
|
|
||||||
relation.relation_cert_no as person_id,
|
|
||||||
base_staff.name as spouse_name,
|
|
||||||
relation.person_id as spouse_id_card,
|
|
||||||
null as spouse_relation_income
|
|
||||||
from ccdi_staff_fmy_relation relation
|
|
||||||
inner join ccdi_base_staff current_staff
|
|
||||||
on current_staff.id_card = relation.relation_cert_no
|
|
||||||
left join ccdi_base_staff base_staff
|
|
||||||
on base_staff.id_card = relation.person_id
|
|
||||||
where relation.status = 1
|
|
||||||
and relation.is_emp_family = 1
|
|
||||||
and relation.relation_type = '配偶'
|
|
||||||
) relation_pair
|
|
||||||
left join ccdi_base_staff spouse_staff
|
|
||||||
on spouse_staff.id_card = relation_pair.spouse_id_card
|
|
||||||
group by relation_pair.person_id
|
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectFamilyAssetLiabilityList" resultMap="FamilyAssetLiabilityListItemResultMap">
|
<select id="selectFamilyAssetLiabilityList" resultMap="FamilyAssetLiabilityListItemResultMap">
|
||||||
@@ -176,42 +166,19 @@
|
|||||||
aggregated.total_debt,
|
aggregated.total_debt,
|
||||||
aggregated.comparison_amount,
|
aggregated.comparison_amount,
|
||||||
case
|
case
|
||||||
when aggregated.missing_asset_info = 1 or aggregated.missing_debt_info = 1 then 'MISSING_INFO'
|
when aggregated.self_asset_record_count = 0 or aggregated.self_debt_record_count = 0 then 'MISSING_INFO'
|
||||||
when comparison_amount <= total_asset * 1.5 then 'NORMAL'
|
when comparison_amount <= total_asset * 1.5 then 'NORMAL'
|
||||||
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then 'RISK'
|
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then 'RISK'
|
||||||
when comparison_amount > total_asset * 3 then 'HIGH'
|
when comparison_amount > total_asset * 3 then 'HIGH'
|
||||||
else 'HIGH'
|
else 'HIGH'
|
||||||
end as risk_level_code,
|
end as risk_level_code,
|
||||||
case
|
case
|
||||||
when aggregated.missing_asset_info = 1 or aggregated.missing_debt_info = 1 then '缺少信息'
|
when aggregated.self_asset_record_count = 0 or aggregated.self_debt_record_count = 0 then '缺少信息'
|
||||||
when comparison_amount <= total_asset * 1.5 then '正常'
|
when comparison_amount <= total_asset * 1.5 then '正常'
|
||||||
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then '存在风险'
|
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then '存在风险'
|
||||||
when comparison_amount > total_asset * 3 then '高风险'
|
when comparison_amount > total_asset * 3 then '高风险'
|
||||||
else '高风险'
|
else '高风险'
|
||||||
end as risk_level_name
|
end as risk_level_name
|
||||||
from (
|
|
||||||
select
|
|
||||||
source.*,
|
|
||||||
case
|
|
||||||
when source.self_asset_record_count = 0
|
|
||||||
or source.spouse_staff_asset_record_count = 0 then 1
|
|
||||||
else 0
|
|
||||||
end as missing_asset_info,
|
|
||||||
case
|
|
||||||
when source.self_debt_record_count = 0
|
|
||||||
or source.spouse_staff_debt_record_count = 0 then 1
|
|
||||||
else 0
|
|
||||||
end as missing_debt_info,
|
|
||||||
case
|
|
||||||
when source.self_asset_record_count = 0
|
|
||||||
or source.spouse_staff_asset_record_count = 0
|
|
||||||
or source.self_debt_record_count = 0
|
|
||||||
or source.spouse_staff_debt_record_count = 0 then 4
|
|
||||||
when source.comparison_amount <= source.total_asset * 1.5 then 1
|
|
||||||
when source.comparison_amount <= source.total_asset * 3 then 2
|
|
||||||
when source.comparison_amount > source.total_asset * 3 then 3
|
|
||||||
else 3
|
|
||||||
end as risk_level_sort
|
|
||||||
from (
|
from (
|
||||||
select
|
select
|
||||||
scope.staff_id_card,
|
scope.staff_id_card,
|
||||||
@@ -225,46 +192,18 @@
|
|||||||
where asset.family_id = scope.staff_id_card
|
where asset.family_id = scope.staff_id_card
|
||||||
and asset.person_id = scope.staff_id_card
|
and asset.person_id = scope.staff_id_card
|
||||||
), 0) as self_asset_record_count,
|
), 0) as self_asset_record_count,
|
||||||
case
|
|
||||||
when spouse.spouse_is_staff = 1 then coalesce((
|
|
||||||
select count(1)
|
|
||||||
from ccdi_asset_info asset
|
|
||||||
where asset.family_id = spouse.spouse_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
), 0)
|
|
||||||
else 1
|
|
||||||
end as spouse_staff_asset_record_count,
|
|
||||||
coalesce((
|
coalesce((
|
||||||
select count(1)
|
select count(1)
|
||||||
from ccdi_debts_info debt
|
from ccdi_debts_info debt
|
||||||
where debt.person_id = scope.staff_id_card
|
where debt.person_id = scope.staff_id_card
|
||||||
), 0) as self_debt_record_count,
|
), 0) as self_debt_record_count,
|
||||||
case
|
|
||||||
when spouse.spouse_is_staff = 1 then coalesce((
|
|
||||||
select count(1)
|
|
||||||
from ccdi_debts_info debt
|
|
||||||
where debt.person_id = spouse.spouse_id_card
|
|
||||||
), 0)
|
|
||||||
else 1
|
|
||||||
end as spouse_staff_debt_record_count,
|
|
||||||
coalesce((
|
coalesce((
|
||||||
select sum(coalesce(asset.current_value, 0))
|
select sum(coalesce(asset.current_value, 0))
|
||||||
from ccdi_asset_info asset
|
from ccdi_asset_info asset
|
||||||
where (asset.family_id = scope.staff_id_card and asset.person_id = scope.staff_id_card)
|
where asset.family_id = scope.staff_id_card
|
||||||
or (
|
|
||||||
spouse.spouse_id_card is not null
|
|
||||||
and (
|
and (
|
||||||
(
|
asset.person_id = scope.staff_id_card
|
||||||
spouse.spouse_is_staff = 1
|
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
|
||||||
and asset.family_id = spouse.spouse_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
(spouse.spouse_is_staff is null or spouse.spouse_is_staff != 1)
|
|
||||||
and asset.family_id = scope.staff_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
), 0) as total_asset,
|
), 0) as total_asset,
|
||||||
coalesce((
|
coalesce((
|
||||||
@@ -280,7 +219,75 @@
|
|||||||
from ccdi_debts_info debt
|
from ccdi_debts_info debt
|
||||||
where debt.person_id = scope.staff_id_card
|
where debt.person_id = scope.staff_id_card
|
||||||
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
|
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
|
||||||
), 0) as comparison_amount
|
), 0) as comparison_amount,
|
||||||
|
case
|
||||||
|
when coalesce((
|
||||||
|
select count(1)
|
||||||
|
from ccdi_asset_info asset
|
||||||
|
where asset.family_id = scope.staff_id_card
|
||||||
|
and asset.person_id = scope.staff_id_card
|
||||||
|
), 0) = 0
|
||||||
|
or coalesce((
|
||||||
|
select count(1)
|
||||||
|
from ccdi_debts_info debt
|
||||||
|
where debt.person_id = scope.staff_id_card
|
||||||
|
), 0) = 0 then 4
|
||||||
|
when (
|
||||||
|
coalesce(base_staff.annual_income, 0)
|
||||||
|
+ coalesce(spouse.spouse_income, 0)
|
||||||
|
+ coalesce((
|
||||||
|
select sum(coalesce(debt.principal_balance, 0))
|
||||||
|
from ccdi_debts_info debt
|
||||||
|
where debt.person_id = scope.staff_id_card
|
||||||
|
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
|
||||||
|
), 0)
|
||||||
|
) <= coalesce((
|
||||||
|
select sum(coalesce(asset.current_value, 0))
|
||||||
|
from ccdi_asset_info asset
|
||||||
|
where asset.family_id = scope.staff_id_card
|
||||||
|
and (
|
||||||
|
asset.person_id = scope.staff_id_card
|
||||||
|
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
|
||||||
|
)
|
||||||
|
), 0) * 1.5 then 1
|
||||||
|
when (
|
||||||
|
coalesce(base_staff.annual_income, 0)
|
||||||
|
+ coalesce(spouse.spouse_income, 0)
|
||||||
|
+ coalesce((
|
||||||
|
select sum(coalesce(debt.principal_balance, 0))
|
||||||
|
from ccdi_debts_info debt
|
||||||
|
where debt.person_id = scope.staff_id_card
|
||||||
|
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
|
||||||
|
), 0)
|
||||||
|
) <= coalesce((
|
||||||
|
select sum(coalesce(asset.current_value, 0))
|
||||||
|
from ccdi_asset_info asset
|
||||||
|
where asset.family_id = scope.staff_id_card
|
||||||
|
and (
|
||||||
|
asset.person_id = scope.staff_id_card
|
||||||
|
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
|
||||||
|
)
|
||||||
|
), 0) * 3 then 2
|
||||||
|
when (
|
||||||
|
coalesce(base_staff.annual_income, 0)
|
||||||
|
+ coalesce(spouse.spouse_income, 0)
|
||||||
|
+ coalesce((
|
||||||
|
select sum(coalesce(debt.principal_balance, 0))
|
||||||
|
from ccdi_debts_info debt
|
||||||
|
where debt.person_id = scope.staff_id_card
|
||||||
|
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
|
||||||
|
), 0)
|
||||||
|
) > coalesce((
|
||||||
|
select sum(coalesce(asset.current_value, 0))
|
||||||
|
from ccdi_asset_info asset
|
||||||
|
where asset.family_id = scope.staff_id_card
|
||||||
|
and (
|
||||||
|
asset.person_id = scope.staff_id_card
|
||||||
|
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
|
||||||
|
)
|
||||||
|
), 0) * 3 then 3
|
||||||
|
else 3
|
||||||
|
end as risk_level_sort
|
||||||
from (
|
from (
|
||||||
<include refid="projectEmployeeScopeSql"/>
|
<include refid="projectEmployeeScopeSql"/>
|
||||||
) scope
|
) scope
|
||||||
@@ -290,7 +297,6 @@
|
|||||||
<include refid="spouseRelationSql"/>
|
<include refid="spouseRelationSql"/>
|
||||||
) spouse
|
) spouse
|
||||||
on spouse.person_id = scope.staff_id_card
|
on spouse.person_id = scope.staff_id_card
|
||||||
) source
|
|
||||||
) aggregated
|
) aggregated
|
||||||
order by risk_level_sort desc, comparison_amount desc, staff_name asc
|
order by risk_level_sort desc, comparison_amount desc, staff_name asc
|
||||||
</select>
|
</select>
|
||||||
@@ -300,7 +306,6 @@
|
|||||||
aggregated.project_id,
|
aggregated.project_id,
|
||||||
aggregated.staff_id_card,
|
aggregated.staff_id_card,
|
||||||
aggregated.spouse_id_card,
|
aggregated.spouse_id_card,
|
||||||
aggregated.spouse_is_staff,
|
|
||||||
aggregated.staff_code,
|
aggregated.staff_code,
|
||||||
aggregated.staff_name,
|
aggregated.staff_name,
|
||||||
aggregated.dept_name,
|
aggregated.dept_name,
|
||||||
@@ -337,19 +342,6 @@
|
|||||||
when comparison_amount > total_asset * 3 then '高风险'
|
when comparison_amount > total_asset * 3 then '高风险'
|
||||||
else '高风险'
|
else '高风险'
|
||||||
end as summary_risk_level_name
|
end as summary_risk_level_name
|
||||||
from (
|
|
||||||
select
|
|
||||||
source.*,
|
|
||||||
case
|
|
||||||
when source.self_asset_record_count = 0
|
|
||||||
or source.spouse_staff_asset_record_count = 0 then 1
|
|
||||||
else 0
|
|
||||||
end as missing_self_asset_info,
|
|
||||||
case
|
|
||||||
when source.self_debt_record_count = 0
|
|
||||||
or source.spouse_staff_debt_record_count = 0 then 1
|
|
||||||
else 0
|
|
||||||
end as missing_self_debt_info
|
|
||||||
from (
|
from (
|
||||||
select
|
select
|
||||||
#{projectId} as project_id,
|
#{projectId} as project_id,
|
||||||
@@ -358,25 +350,18 @@
|
|||||||
scope.staff_name,
|
scope.staff_name,
|
||||||
scope.dept_name,
|
scope.dept_name,
|
||||||
spouse.spouse_id_card,
|
spouse.spouse_id_card,
|
||||||
spouse.spouse_is_staff,
|
|
||||||
coalesce(base_staff.annual_income, 0) as self_income,
|
coalesce(base_staff.annual_income, 0) as self_income,
|
||||||
coalesce(spouse.spouse_income, 0) as spouse_income,
|
coalesce(spouse.spouse_income, 0) as spouse_income,
|
||||||
coalesce(base_staff.annual_income, 0) + coalesce(spouse.spouse_income, 0) as total_income,
|
coalesce(base_staff.annual_income, 0) + coalesce(spouse.spouse_income, 0) as total_income,
|
||||||
coalesce((
|
case
|
||||||
|
when coalesce((
|
||||||
select count(1)
|
select count(1)
|
||||||
from ccdi_asset_info asset
|
from ccdi_asset_info asset
|
||||||
where asset.family_id = scope.staff_id_card
|
where asset.family_id = scope.staff_id_card
|
||||||
and asset.person_id = scope.staff_id_card
|
and asset.person_id = scope.staff_id_card
|
||||||
), 0) as self_asset_record_count,
|
), 0) = 0 then 1
|
||||||
case
|
else 0
|
||||||
when spouse.spouse_is_staff = 1 then coalesce((
|
end as missing_self_asset_info,
|
||||||
select count(1)
|
|
||||||
from ccdi_asset_info asset
|
|
||||||
where asset.family_id = spouse.spouse_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
), 0)
|
|
||||||
else 1
|
|
||||||
end as spouse_staff_asset_record_count,
|
|
||||||
coalesce((
|
coalesce((
|
||||||
select sum(coalesce(asset.current_value, 0))
|
select sum(coalesce(asset.current_value, 0))
|
||||||
from ccdi_asset_info asset
|
from ccdi_asset_info asset
|
||||||
@@ -386,53 +371,27 @@
|
|||||||
coalesce((
|
coalesce((
|
||||||
select sum(coalesce(asset.current_value, 0))
|
select sum(coalesce(asset.current_value, 0))
|
||||||
from ccdi_asset_info asset
|
from ccdi_asset_info asset
|
||||||
where spouse.spouse_id_card is not null
|
where asset.family_id = scope.staff_id_card
|
||||||
and (
|
and spouse.spouse_id_card is not null
|
||||||
(
|
|
||||||
spouse.spouse_is_staff = 1
|
|
||||||
and asset.family_id = spouse.spouse_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
and asset.person_id = spouse.spouse_id_card
|
||||||
)
|
|
||||||
or (
|
|
||||||
(spouse.spouse_is_staff is null or spouse.spouse_is_staff != 1)
|
|
||||||
and asset.family_id = scope.staff_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
)
|
|
||||||
)
|
|
||||||
), 0) as spouse_total_asset,
|
), 0) as spouse_total_asset,
|
||||||
coalesce((
|
coalesce((
|
||||||
select sum(coalesce(asset.current_value, 0))
|
select sum(coalesce(asset.current_value, 0))
|
||||||
from ccdi_asset_info asset
|
from ccdi_asset_info asset
|
||||||
where (asset.family_id = scope.staff_id_card and asset.person_id = scope.staff_id_card)
|
where asset.family_id = scope.staff_id_card
|
||||||
or (
|
|
||||||
spouse.spouse_id_card is not null
|
|
||||||
and (
|
and (
|
||||||
(
|
asset.person_id = scope.staff_id_card
|
||||||
spouse.spouse_is_staff = 1
|
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
|
||||||
and asset.family_id = spouse.spouse_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
(spouse.spouse_is_staff is null or spouse.spouse_is_staff != 1)
|
|
||||||
and asset.family_id = scope.staff_id_card
|
|
||||||
and asset.person_id = spouse.spouse_id_card
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
), 0) as total_asset,
|
), 0) as total_asset,
|
||||||
coalesce((
|
case
|
||||||
|
when coalesce((
|
||||||
select count(1)
|
select count(1)
|
||||||
from ccdi_debts_info debt
|
from ccdi_debts_info debt
|
||||||
where debt.person_id = scope.staff_id_card
|
where debt.person_id = scope.staff_id_card
|
||||||
), 0) as self_debt_record_count,
|
), 0) = 0 then 1
|
||||||
case
|
else 0
|
||||||
when spouse.spouse_is_staff = 1 then coalesce((
|
end as missing_self_debt_info,
|
||||||
select count(1)
|
|
||||||
from ccdi_debts_info debt
|
|
||||||
where debt.person_id = spouse.spouse_id_card
|
|
||||||
), 0)
|
|
||||||
else 1
|
|
||||||
end as spouse_staff_debt_record_count,
|
|
||||||
coalesce((
|
coalesce((
|
||||||
select sum(coalesce(debt.principal_balance, 0))
|
select sum(coalesce(debt.principal_balance, 0))
|
||||||
from ccdi_debts_info debt
|
from ccdi_debts_info debt
|
||||||
@@ -468,7 +427,6 @@
|
|||||||
) spouse
|
) spouse
|
||||||
on spouse.person_id = scope.staff_id_card
|
on spouse.person_id = scope.staff_id_card
|
||||||
where scope.staff_id_card = #{staffIdCard}
|
where scope.staff_id_card = #{staffIdCard}
|
||||||
) source
|
|
||||||
) aggregated
|
) aggregated
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -479,15 +437,7 @@
|
|||||||
asset.asset_sub_type,
|
asset.asset_sub_type,
|
||||||
case
|
case
|
||||||
when asset.person_id = #{staffIdCard} then base_staff.name
|
when asset.person_id = #{staffIdCard} then base_staff.name
|
||||||
else coalesce(holder_staff.name, (
|
else spouse.relation_name
|
||||||
select max(relation.relation_name)
|
|
||||||
from ccdi_staff_fmy_relation relation
|
|
||||||
where relation.person_id = #{staffIdCard}
|
|
||||||
and relation.relation_cert_no = asset.person_id
|
|
||||||
and relation.status = 1
|
|
||||||
and relation.is_emp_family = 1
|
|
||||||
and relation.relation_type = '配偶'
|
|
||||||
))
|
|
||||||
end as holder_name,
|
end as holder_name,
|
||||||
asset.person_id as holder_id_card,
|
asset.person_id as holder_id_card,
|
||||||
asset.current_value,
|
asset.current_value,
|
||||||
@@ -495,23 +445,15 @@
|
|||||||
from ccdi_asset_info asset
|
from ccdi_asset_info asset
|
||||||
left join ccdi_base_staff base_staff
|
left join ccdi_base_staff base_staff
|
||||||
on base_staff.id_card = #{staffIdCard}
|
on base_staff.id_card = #{staffIdCard}
|
||||||
left join ccdi_base_staff holder_staff
|
left join ccdi_staff_fmy_relation spouse
|
||||||
on holder_staff.id_card = asset.person_id
|
on spouse.person_id = #{staffIdCard}
|
||||||
where (asset.family_id = #{staffIdCard} and asset.person_id = #{staffIdCard})
|
and spouse.status = 1
|
||||||
or (
|
and spouse.relation_type = '配偶'
|
||||||
#{spouseIdCard} is not null
|
and spouse.relation_cert_no = asset.person_id
|
||||||
|
where asset.family_id = #{staffIdCard}
|
||||||
and (
|
and (
|
||||||
(
|
asset.person_id = #{staffIdCard}
|
||||||
#{spouseIsStaff} = 1
|
or (#{spouseIdCard} is not null and asset.person_id = #{spouseIdCard})
|
||||||
and asset.family_id = #{spouseIdCard}
|
|
||||||
and asset.person_id = #{spouseIdCard}
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
(#{spouseIsStaff} is null or #{spouseIsStaff} != 1)
|
|
||||||
and asset.family_id = #{staffIdCard}
|
|
||||||
and asset.person_id = #{spouseIdCard}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
order by
|
order by
|
||||||
case when asset.person_id = #{staffIdCard} then 1 else 2 end,
|
case when asset.person_id = #{staffIdCard} then 1 else 2 end,
|
||||||
@@ -527,15 +469,7 @@
|
|||||||
debt.creditor_type,
|
debt.creditor_type,
|
||||||
case
|
case
|
||||||
when debt.person_id = #{staffIdCard} then base_staff.name
|
when debt.person_id = #{staffIdCard} then base_staff.name
|
||||||
else coalesce(owner_staff.name, (
|
else spouse.relation_name
|
||||||
select max(relation.relation_name)
|
|
||||||
from ccdi_staff_fmy_relation relation
|
|
||||||
where relation.person_id = #{staffIdCard}
|
|
||||||
and relation.relation_cert_no = debt.person_id
|
|
||||||
and relation.status = 1
|
|
||||||
and relation.is_emp_family = 1
|
|
||||||
and relation.relation_type = '配偶'
|
|
||||||
))
|
|
||||||
end as owner_name,
|
end as owner_name,
|
||||||
debt.person_id as owner_id_card,
|
debt.person_id as owner_id_card,
|
||||||
debt.principal_balance,
|
debt.principal_balance,
|
||||||
@@ -543,8 +477,11 @@
|
|||||||
from ccdi_debts_info debt
|
from ccdi_debts_info debt
|
||||||
left join ccdi_base_staff base_staff
|
left join ccdi_base_staff base_staff
|
||||||
on base_staff.id_card = #{staffIdCard}
|
on base_staff.id_card = #{staffIdCard}
|
||||||
left join ccdi_base_staff owner_staff
|
left join ccdi_staff_fmy_relation spouse
|
||||||
on owner_staff.id_card = debt.person_id
|
on spouse.person_id = #{staffIdCard}
|
||||||
|
and spouse.status = 1
|
||||||
|
and spouse.relation_type = '配偶'
|
||||||
|
and spouse.relation_cert_no = debt.person_id
|
||||||
where debt.person_id = #{staffIdCard}
|
where debt.person_id = #{staffIdCard}
|
||||||
or (#{spouseIdCard} is not null and debt.person_id = #{spouseIdCard})
|
or (#{spouseIdCard} is not null and debt.person_id = #{spouseIdCard})
|
||||||
order by
|
order by
|
||||||
|
|||||||
@@ -1,330 +0,0 @@
|
|||||||
<?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.ccdi.project.mapper.CcdiRelationGraphMapper">
|
|
||||||
|
|
||||||
<resultMap id="RelationGraphNodeResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO">
|
|
||||||
<id property="objectKey" column="objectKey"/>
|
|
||||||
<result property="nodeKey" column="nodeKey"/>
|
|
||||||
<result property="nodeName" column="nodeName"/>
|
|
||||||
<result property="idNumber" column="idNumber"/>
|
|
||||||
<result property="subjectType" column="subjectType"/>
|
|
||||||
<result property="sourceType" column="sourceType"/>
|
|
||||||
<result property="createdTime" column="createdTime"/>
|
|
||||||
<result property="updatedTime" column="updatedTime"/>
|
|
||||||
<result property="canExpand" column="canExpand"/>
|
|
||||||
<result property="depth" column="depth"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<resultMap id="RelationGraphEdgeResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphEdgeVO">
|
|
||||||
<id property="objectKey" column="objectKey"/>
|
|
||||||
<result property="fromKey" column="fromKey"/>
|
|
||||||
<result property="toKey" column="toKey"/>
|
|
||||||
<result property="edgeTable" column="edgeTable"/>
|
|
||||||
<result property="relationType" column="relationType"/>
|
|
||||||
<result property="companyName" column="companyName"/>
|
|
||||||
<result property="stockName" column="stockName"/>
|
|
||||||
<result property="stockType" column="stockType"/>
|
|
||||||
<result property="stockPercent" column="stockPercent"/>
|
|
||||||
<result property="shouldCapi" column="shouldCapi"/>
|
|
||||||
<result property="shouldCapiValue" column="shouldCapiValue"/>
|
|
||||||
<result property="shouldCapiUnit" column="shouldCapiUnit"/>
|
|
||||||
<result property="shoudDate" column="shoudDate"/>
|
|
||||||
<result property="pKeyNo" column="pKeyNo"/>
|
|
||||||
<result property="operName" column="operName"/>
|
|
||||||
<result property="operKeyNo" column="operKeyNo"/>
|
|
||||||
<result property="personId" column="personId"/>
|
|
||||||
<result property="relationName" column="relationName"/>
|
|
||||||
<result property="relationCertNo" column="relationCertNo"/>
|
|
||||||
<result property="gender" column="gender"/>
|
|
||||||
<result property="birthDate" column="birthDate"/>
|
|
||||||
<result property="relationCertType" column="relationCertType"/>
|
|
||||||
<result property="mobilePhone1" column="mobilePhone1"/>
|
|
||||||
<result property="mobilePhone2" column="mobilePhone2"/>
|
|
||||||
<result property="wechatNo1" column="wechatNo1"/>
|
|
||||||
<result property="wechatNo2" column="wechatNo2"/>
|
|
||||||
<result property="wechatNo3" column="wechatNo3"/>
|
|
||||||
<result property="contactAddress" column="contactAddress"/>
|
|
||||||
<result property="annualIncome" column="annualIncome"/>
|
|
||||||
<result property="relationDesc" column="relationDesc"/>
|
|
||||||
<result property="status" column="status"/>
|
|
||||||
<result property="effectiveDate" column="effectiveDate"/>
|
|
||||||
<result property="invalidDate" column="invalidDate"/>
|
|
||||||
<result property="remark" column="remark"/>
|
|
||||||
<result property="dataSource" column="dataSource"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<resultMap id="SuspectedEnterpriseResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseItemVO">
|
|
||||||
<result property="candidateKeyNo" column="candidateKeyNo"/>
|
|
||||||
<result property="personName" column="personName"/>
|
|
||||||
<result property="companyId" column="companyId"/>
|
|
||||||
<result property="companyName" column="companyName"/>
|
|
||||||
<result property="creditCode" column="creditCode"/>
|
|
||||||
<result property="enterpriseStatus" column="enterpriseStatus"/>
|
|
||||||
<result property="industryName" column="industryName"/>
|
|
||||||
<result property="relationType" column="relationType"/>
|
|
||||||
<result property="stockPercent" column="stockPercent"/>
|
|
||||||
<result property="establishDate" column="establishDate"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<sql id="nodeColumns">
|
|
||||||
n.object_key AS objectKey,
|
|
||||||
CONCAT('rel_node/', n.object_key) AS nodeKey,
|
|
||||||
n.node_name AS nodeName,
|
|
||||||
n.id_number AS idNumber,
|
|
||||||
n.subject_type AS subjectType,
|
|
||||||
n.source_type AS sourceType,
|
|
||||||
DATE_FORMAT(n.created_time, '%Y-%m-%d %H:%i:%s') AS createdTime,
|
|
||||||
DATE_FORMAT(n.updated_time, '%Y-%m-%d %H:%i:%s') AS updatedTime,
|
|
||||||
1 AS canExpand,
|
|
||||||
0 AS depth
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<select id="selectRelationGraphSubjects" resultMap="RelationGraphNodeResultMap">
|
|
||||||
SELECT
|
|
||||||
<include refid="nodeColumns"/>
|
|
||||||
FROM lx_rel_node n
|
|
||||||
WHERE 1 = 1
|
|
||||||
<if test="query.objectKey != null and query.objectKey != ''">
|
|
||||||
AND n.object_key = (#{query.objectKey} COLLATE utf8mb4_general_ci)
|
|
||||||
</if>
|
|
||||||
<if test="query.objectKey == null or query.objectKey == ''">
|
|
||||||
<if test="query.keyword != null and query.keyword != ''">
|
|
||||||
AND (
|
|
||||||
n.object_key = (TRIM(#{query.keyword}) COLLATE utf8mb4_general_ci)
|
|
||||||
OR n.id_number = (TRIM(#{query.keyword}) COLLATE utf8mb4_general_ci)
|
|
||||||
OR n.node_name LIKE (CONCAT('%', TRIM(#{query.keyword}), '%') COLLATE utf8mb4_general_ci)
|
|
||||||
)
|
|
||||||
</if>
|
|
||||||
</if>
|
|
||||||
ORDER BY
|
|
||||||
CASE
|
|
||||||
WHEN n.object_key = (TRIM(IFNULL(#{query.keyword}, '')) COLLATE utf8mb4_general_ci) THEN 0
|
|
||||||
WHEN n.id_number = (TRIM(IFNULL(#{query.keyword}, '')) COLLATE utf8mb4_general_ci) THEN 1
|
|
||||||
ELSE 2
|
|
||||||
END,
|
|
||||||
n.node_name
|
|
||||||
LIMIT 20
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectRelationGraphNodesByKeys" resultMap="RelationGraphNodeResultMap">
|
|
||||||
SELECT
|
|
||||||
<include refid="nodeColumns"/>
|
|
||||||
FROM lx_rel_node n
|
|
||||||
WHERE n.object_key IN
|
|
||||||
<foreach collection="objectKeys" item="objectKey" open="(" separator="," close=")">
|
|
||||||
#{objectKey}
|
|
||||||
</foreach>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectRelationGraphEdges" resultMap="RelationGraphEdgeResultMap">
|
|
||||||
SELECT *
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
e.object_key AS objectKey,
|
|
||||||
e.from_key AS fromKey,
|
|
||||||
e.to_key AS toKey,
|
|
||||||
'lx_rel_family_edge' AS edgeTable,
|
|
||||||
e.relation_type AS relationType,
|
|
||||||
NULL AS companyName,
|
|
||||||
NULL AS stockName,
|
|
||||||
NULL AS stockType,
|
|
||||||
NULL AS stockPercent,
|
|
||||||
NULL AS shouldCapi,
|
|
||||||
NULL AS shouldCapiValue,
|
|
||||||
NULL AS shouldCapiUnit,
|
|
||||||
NULL AS shoudDate,
|
|
||||||
NULL AS pKeyNo,
|
|
||||||
NULL AS operName,
|
|
||||||
NULL AS operKeyNo,
|
|
||||||
e.person_id AS personId,
|
|
||||||
e.relation_name AS relationName,
|
|
||||||
e.relation_cert_no AS relationCertNo,
|
|
||||||
NULL AS gender,
|
|
||||||
NULL AS birthDate,
|
|
||||||
NULL AS relationCertType,
|
|
||||||
NULL AS mobilePhone1,
|
|
||||||
NULL AS mobilePhone2,
|
|
||||||
NULL AS wechatNo1,
|
|
||||||
NULL AS wechatNo2,
|
|
||||||
NULL AS wechatNo3,
|
|
||||||
NULL AS contactAddress,
|
|
||||||
NULL AS annualIncome,
|
|
||||||
e.relation_desc AS relationDesc,
|
|
||||||
NULL AS status,
|
|
||||||
NULL AS effectiveDate,
|
|
||||||
NULL AS invalidDate,
|
|
||||||
NULL AS remark,
|
|
||||||
NULL AS dataSource
|
|
||||||
FROM lx_rel_family_edge e
|
|
||||||
WHERE e.from_key = CONCAT('rel_node/', #{query.objectKey})
|
|
||||||
OR e.to_key = CONCAT('rel_node/', #{query.objectKey})
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
e.object_key AS objectKey,
|
|
||||||
e.from_key AS fromKey,
|
|
||||||
e.to_key AS toKey,
|
|
||||||
'lx_rel_stock_edge' AS edgeTable,
|
|
||||||
e.stock_type AS relationType,
|
|
||||||
e.company_name AS companyName,
|
|
||||||
e.stock_name AS stockName,
|
|
||||||
e.stock_type AS stockType,
|
|
||||||
e.stock_percent AS stockPercent,
|
|
||||||
e.should_capi AS shouldCapi,
|
|
||||||
e.should_capi_value AS shouldCapiValue,
|
|
||||||
e.should_capi_unit AS shouldCapiUnit,
|
|
||||||
e.shoud_date AS shoudDate,
|
|
||||||
e.p_key_no AS pKeyNo,
|
|
||||||
NULL AS operName,
|
|
||||||
NULL AS operKeyNo,
|
|
||||||
NULL AS personId,
|
|
||||||
NULL AS relationName,
|
|
||||||
NULL AS relationCertNo,
|
|
||||||
NULL AS gender,
|
|
||||||
NULL AS birthDate,
|
|
||||||
NULL AS relationCertType,
|
|
||||||
NULL AS mobilePhone1,
|
|
||||||
NULL AS mobilePhone2,
|
|
||||||
NULL AS wechatNo1,
|
|
||||||
NULL AS wechatNo2,
|
|
||||||
NULL AS wechatNo3,
|
|
||||||
NULL AS contactAddress,
|
|
||||||
NULL AS annualIncome,
|
|
||||||
NULL AS relationDesc,
|
|
||||||
NULL AS status,
|
|
||||||
NULL AS effectiveDate,
|
|
||||||
NULL AS invalidDate,
|
|
||||||
NULL AS remark,
|
|
||||||
NULL AS dataSource
|
|
||||||
FROM lx_rel_stock_edge e
|
|
||||||
WHERE e.from_key = CONCAT('rel_node/', #{query.objectKey})
|
|
||||||
OR e.to_key = CONCAT('rel_node/', #{query.objectKey})
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
e.object_key AS objectKey,
|
|
||||||
e.from_key AS fromKey,
|
|
||||||
e.to_key AS toKey,
|
|
||||||
'lx_rel_represent_edge' AS edgeTable,
|
|
||||||
'法定代表人' AS relationType,
|
|
||||||
NULL AS companyName,
|
|
||||||
NULL AS stockName,
|
|
||||||
NULL AS stockType,
|
|
||||||
NULL AS stockPercent,
|
|
||||||
NULL AS shouldCapi,
|
|
||||||
NULL AS shouldCapiValue,
|
|
||||||
NULL AS shouldCapiUnit,
|
|
||||||
NULL AS shoudDate,
|
|
||||||
NULL AS pKeyNo,
|
|
||||||
e.oper_name AS operName,
|
|
||||||
e.oper_key_no AS operKeyNo,
|
|
||||||
NULL AS personId,
|
|
||||||
NULL AS relationName,
|
|
||||||
NULL AS relationCertNo,
|
|
||||||
NULL AS gender,
|
|
||||||
NULL AS birthDate,
|
|
||||||
NULL AS relationCertType,
|
|
||||||
NULL AS mobilePhone1,
|
|
||||||
NULL AS mobilePhone2,
|
|
||||||
NULL AS wechatNo1,
|
|
||||||
NULL AS wechatNo2,
|
|
||||||
NULL AS wechatNo3,
|
|
||||||
NULL AS contactAddress,
|
|
||||||
NULL AS annualIncome,
|
|
||||||
NULL AS relationDesc,
|
|
||||||
NULL AS status,
|
|
||||||
NULL AS effectiveDate,
|
|
||||||
NULL AS invalidDate,
|
|
||||||
NULL AS remark,
|
|
||||||
NULL AS dataSource
|
|
||||||
FROM lx_rel_represent_edge e
|
|
||||||
WHERE e.from_key = CONCAT('rel_node/', #{query.objectKey})
|
|
||||||
OR e.to_key = CONCAT('rel_node/', #{query.objectKey})
|
|
||||||
) graph_edges
|
|
||||||
ORDER BY
|
|
||||||
CASE edgeTable
|
|
||||||
WHEN 'lx_rel_family_edge' THEN 0
|
|
||||||
WHEN 'lx_rel_stock_edge' THEN 1
|
|
||||||
ELSE 2
|
|
||||||
END,
|
|
||||||
relationType,
|
|
||||||
objectKey
|
|
||||||
LIMIT #{query.limit}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="countSuspectedEnterpriseKeyNos" resultType="int">
|
|
||||||
SELECT COUNT(DISTINCT candidate_key_no)
|
|
||||||
FROM (
|
|
||||||
SELECT e.oper_key_no AS candidate_key_no
|
|
||||||
FROM lx_rel_represent_edge e
|
|
||||||
WHERE e.oper_name = (#{personName} COLLATE utf8mb4_general_ci)
|
|
||||||
AND e.oper_key_no IS NOT NULL
|
|
||||||
AND e.oper_key_no != ''
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT e.p_key_no AS candidate_key_no
|
|
||||||
FROM lx_rel_stock_edge e
|
|
||||||
WHERE e.stock_name = (#{personName} COLLATE utf8mb4_general_ci)
|
|
||||||
AND e.stock_type = '自然人股东'
|
|
||||||
AND e.p_key_no IS NOT NULL
|
|
||||||
AND e.p_key_no != ''
|
|
||||||
) same_name_candidates
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectSuspectedEnterprises" resultMap="SuspectedEnterpriseResultMap">
|
|
||||||
SELECT *
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
e.oper_key_no AS candidateKeyNo,
|
|
||||||
e.oper_name AS personName,
|
|
||||||
REPLACE(e.to_key, 'rel_node/', '') AS companyId,
|
|
||||||
COALESCE(ent.enterprise_name, company_node.node_name) AS companyName,
|
|
||||||
COALESCE(ent.social_credit_code, company_node.id_number) AS creditCode,
|
|
||||||
ent.status AS enterpriseStatus,
|
|
||||||
ent.industry_name AS industryName,
|
|
||||||
'法定代表人' AS relationType,
|
|
||||||
NULL AS stockPercent,
|
|
||||||
DATE_FORMAT(ent.establish_date, '%Y-%m-%d') AS establishDate,
|
|
||||||
0 AS relationSort
|
|
||||||
FROM lx_rel_represent_edge e
|
|
||||||
LEFT JOIN lx_rel_node company_node
|
|
||||||
ON company_node.object_key = REPLACE(e.to_key, 'rel_node/', '')
|
|
||||||
LEFT JOIN ccdi_enterprise_base_info ent
|
|
||||||
ON ent.social_credit_code = company_node.id_number
|
|
||||||
WHERE e.oper_name = (#{personName} COLLATE utf8mb4_general_ci)
|
|
||||||
AND e.oper_key_no IS NOT NULL
|
|
||||||
AND e.oper_key_no != ''
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
e.p_key_no AS candidateKeyNo,
|
|
||||||
e.stock_name AS personName,
|
|
||||||
REPLACE(e.to_key, 'rel_node/', '') AS companyId,
|
|
||||||
COALESCE(ent.enterprise_name, e.company_name, company_node.node_name) AS companyName,
|
|
||||||
COALESCE(ent.social_credit_code, company_node.id_number) AS creditCode,
|
|
||||||
ent.status AS enterpriseStatus,
|
|
||||||
ent.industry_name AS industryName,
|
|
||||||
e.stock_type AS relationType,
|
|
||||||
e.stock_percent AS stockPercent,
|
|
||||||
COALESCE(DATE_FORMAT(ent.establish_date, '%Y-%m-%d'), e.shoud_date) AS establishDate,
|
|
||||||
1 AS relationSort
|
|
||||||
FROM lx_rel_stock_edge e
|
|
||||||
LEFT JOIN lx_rel_node company_node
|
|
||||||
ON company_node.object_key = REPLACE(e.to_key, 'rel_node/', '')
|
|
||||||
LEFT JOIN ccdi_enterprise_base_info ent
|
|
||||||
ON ent.social_credit_code = company_node.id_number
|
|
||||||
WHERE e.stock_name = (#{personName} COLLATE utf8mb4_general_ci)
|
|
||||||
AND e.stock_type = '自然人股东'
|
|
||||||
AND e.p_key_no IS NOT NULL
|
|
||||||
AND e.p_key_no != ''
|
|
||||||
) suspected_enterprises
|
|
||||||
ORDER BY relationSort, companyName, companyId
|
|
||||||
LIMIT #{limit}
|
|
||||||
</select>
|
|
||||||
</mapper>
|
|
||||||
@@ -281,8 +281,7 @@ class CcdiProjectOverviewControllerTest {
|
|||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
||||||
CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
|
CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
|
||||||
row.setLeAccountName("张三");
|
row.setSuspiciousPersonName("张三");
|
||||||
row.setCustomerAccountName("测试对手方");
|
|
||||||
row.setDisplayAmount(new java.math.BigDecimal("10.00"));
|
row.setDisplayAmount(new java.math.BigDecimal("10.00"));
|
||||||
when(overviewService.exportSuspiciousTransactions(same(queryDTO))).thenReturn(List.of(row));
|
when(overviewService.exportSuspiciousTransactions(same(queryDTO))).thenReturn(List.of(row));
|
||||||
|
|
||||||
|
|||||||
@@ -104,26 +104,4 @@ class CcdiBankStatementTest {
|
|||||||
assertEquals("330101199001011234", entity.getCustomerCertNo());
|
assertEquals("330101199001011234", entity.getCustomerCertNo());
|
||||||
assertEquals("91330100123456789X", entity.getCustomerSocialCreditCode());
|
assertEquals("91330100123456789X", entity.getCustomerSocialCreditCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testFromResponse_ShouldNormalizeCretNoBeforeDash() {
|
|
||||||
BankStatementItem item = new BankStatementItem();
|
|
||||||
item.setCretNo(" 330100198801010033 - 测试人员 ");
|
|
||||||
|
|
||||||
CcdiBankStatement entity = CcdiBankStatement.fromResponse(item);
|
|
||||||
|
|
||||||
assertNotNull(entity);
|
|
||||||
assertEquals("330100198801010033", entity.getCretNo());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testFromResponse_ShouldNormalizeCretNoWithChineseDash() {
|
|
||||||
BankStatementItem item = new BankStatementItem();
|
|
||||||
item.setCretNo("330100198801010033-测试人员");
|
|
||||||
|
|
||||||
CcdiBankStatement entity = CcdiBankStatement.fromResponse(item);
|
|
||||||
|
|
||||||
assertNotNull(entity);
|
|
||||||
assertEquals("330100198801010033", entity.getCretNo());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,20 +220,6 @@ class CcdiBankStatementMapperXmlTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void targetCount_shouldOnlyUseStatementCretNoMatchedStaff() throws Exception {
|
|
||||||
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(RESOURCE)) {
|
|
||||||
String xml = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
|
||||||
String selectSql = extractSelect(xml, "countMatchedStaffCountByProjectId");
|
|
||||||
|
|
||||||
assertTrue(selectSql.contains("select count(distinct trim(bs.cret_no))"), selectSql);
|
|
||||||
assertTrue(selectSql.contains("inner join ccdi_base_staff staff on staff.id_card = trim(bs.cret_no)"), selectSql);
|
|
||||||
assertFalse(selectSql.contains("ccdi_staff_fmy_relation"), selectSql);
|
|
||||||
assertFalse(selectSql.contains("ccdi_account_info"), selectSql);
|
|
||||||
assertFalse(selectSql.contains("LE_ACCOUNT_NO"), selectSql);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MappedStatement loadMappedStatement(String statementId) throws Exception {
|
private MappedStatement loadMappedStatement(String statementId) throws Exception {
|
||||||
Configuration configuration = new Configuration();
|
Configuration configuration = new Configuration();
|
||||||
configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource()));
|
configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource()));
|
||||||
@@ -256,15 +242,6 @@ class CcdiBankStatementMapperXmlTest {
|
|||||||
return boundSql.getSql().replaceAll("\\s+", " ").trim();
|
return boundSql.getSql().replaceAll("\\s+", " ").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractSelect(String xml, String selectId) {
|
|
||||||
String start = "<select id=\"" + selectId + "\"";
|
|
||||||
int startIndex = xml.indexOf(start);
|
|
||||||
assertTrue(startIndex >= 0, "missing select: " + selectId);
|
|
||||||
int endIndex = xml.indexOf("</select>", startIndex);
|
|
||||||
assertTrue(endIndex >= 0, "missing closing select tag: " + selectId);
|
|
||||||
return xml.substring(startIndex, endIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerTypeAliases(TypeAliasRegistry typeAliasRegistry) {
|
private void registerTypeAliases(TypeAliasRegistry typeAliasRegistry) {
|
||||||
typeAliasRegistry.registerAlias("map", Map.class);
|
typeAliasRegistry.registerAlias("map", Map.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class CcdiBankTagAnalysisMapperXmlTest {
|
class CcdiBankTagAnalysisMapperXmlTest {
|
||||||
@@ -27,18 +26,11 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
|||||||
"selectForexSellAmtStatements",
|
"selectForexSellAmtStatements",
|
||||||
"selectLargePurchaseTransactionStatements",
|
"selectLargePurchaseTransactionStatements",
|
||||||
"selectStockTfrLargeStatements",
|
"selectStockTfrLargeStatements",
|
||||||
"selectLargeStockTradingStatements",
|
"selectLargeStockTradingStatements"
|
||||||
"selectExternalSingleLargeAmountStatements",
|
|
||||||
"selectExternalNightTransactionStatements",
|
|
||||||
"selectExternalGamblingMemoStatements",
|
|
||||||
"selectExternalToStaffOrFamilyTransactionStatements"
|
|
||||||
);
|
);
|
||||||
private static final List<String> PHASE_TWO_OBJECT_SELECT_IDS = List.of(
|
private static final List<String> PHASE_TWO_OBJECT_SELECT_IDS = List.of(
|
||||||
"selectLowIncomeRelativeLargeTransactionObjects",
|
"selectLowIncomeRelativeLargeTransactionObjects",
|
||||||
"selectMultiPartyGamblingTransferObjects",
|
"selectMultiPartyGamblingTransferObjects",
|
||||||
"selectExternalCumulativeTransactionAmountObjects",
|
|
||||||
"selectExternalAnnualTurnoverObjects",
|
|
||||||
"selectExternalMultiPartyGamblingTransferObjects",
|
|
||||||
"selectMonthlyFixedIncomeObjects",
|
"selectMonthlyFixedIncomeObjects",
|
||||||
"selectFixedCounterpartyTransferObjects",
|
"selectFixedCounterpartyTransferObjects",
|
||||||
"selectSupplierConcentrationObjects",
|
"selectSupplierConcentrationObjects",
|
||||||
@@ -108,10 +100,7 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
|||||||
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
||||||
String xml = readXml(RESOURCE);
|
String xml = readXml(RESOURCE);
|
||||||
assertTrue(xml.contains("占位SQL,待补充真实规则"));
|
assertTrue(xml.contains("占位SQL,待补充真实规则"));
|
||||||
assertEquals(
|
assertEquals(6, countMatches(xml, "where 1 = 0"));
|
||||||
countMatches(xml, "占位SQL,待补充真实规则"),
|
|
||||||
countMatches(xml, "where 1 = 0")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -127,31 +116,6 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void lowIncomeRelativeRule_shouldIgnoreNullAnnualIncome() throws Exception {
|
|
||||||
String xml = readXml(RESOURCE);
|
|
||||||
String selectSql = extractSelectSql(xml, "selectLowIncomeRelativeLargeTransactionObjects");
|
|
||||||
assertTrue(selectSql.contains("relation.annual_income is not null"));
|
|
||||||
assertTrue(!selectSql.contains("relation.annual_income is null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void abnormalCustomerTransactionRule_shouldUseCreditCustomerAndIntermediaryAccountRules() throws Exception {
|
|
||||||
String xml = readXml(RESOURCE);
|
|
||||||
String selectSql = extractSelectSql(xml, "selectAbnormalCustomerTransactionStatements");
|
|
||||||
|
|
||||||
assertTrue(selectSql.contains("account.owner_type = 'CREDIT_CUSTOMER'"));
|
|
||||||
assertTrue(selectSql.contains("account.owner_type = 'INTERMEDIARY'"));
|
|
||||||
assertTrue(selectSql.contains("account.account_no = bs.customer_account_no"));
|
|
||||||
assertTrue(selectSql.contains("enterprise.ent_source = 'INTERMEDIARY'"));
|
|
||||||
assertTrue(selectSql.contains("intermediary.name = bs.CUSTOMER_ACCOUNT_NAME"));
|
|
||||||
assertTrue(selectSql.contains("bs.CUSTOMER_ACCOUNT_NAME like concat('%', intermediary.name, '%')"));
|
|
||||||
assertTrue(selectSql.contains("bs.bank in ('ALIPAY', 'WECHAT')"));
|
|
||||||
assertEquals(5, countMatches(selectSql, "GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000"));
|
|
||||||
assertTrue(!selectSql.contains("customer_cert_no"));
|
|
||||||
assertTrue(!selectSql.contains("social_credit_code = bs"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void withdrawCntObjectRule_shouldUseRealSqlAndKeepObjectHitFields() throws Exception {
|
void withdrawCntObjectRule_shouldUseRealSqlAndKeepObjectHitFields() throws Exception {
|
||||||
String xml = readXml(RESOURCE);
|
String xml = readXml(RESOURCE);
|
||||||
@@ -167,11 +131,7 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
|||||||
String xml = readXml(RESOURCE);
|
String xml = readXml(RESOURCE);
|
||||||
for (String selectId : PHASE_TWO_OBJECT_SELECT_IDS) {
|
for (String selectId : PHASE_TWO_OBJECT_SELECT_IDS) {
|
||||||
String selectSql = extractSelectSql(xml, selectId);
|
String selectSql = extractSelectSql(xml, selectId);
|
||||||
assertTrue(
|
assertTrue(selectSql.contains("'STAFF_ID_CARD' AS objectType"), () -> selectId + " 缺少 objectType");
|
||||||
selectSql.contains("'STAFF_ID_CARD' AS objectType")
|
|
||||||
|| selectSql.contains("'EXTERNAL_CERT_NO' AS objectType"),
|
|
||||||
() -> selectId + " 缺少 objectType"
|
|
||||||
);
|
|
||||||
assertTrue(selectSql.contains("AS objectKey"), () -> selectId + " 缺少 objectKey");
|
assertTrue(selectSql.contains("AS objectKey"), () -> selectId + " 缺少 objectKey");
|
||||||
assertTrue(selectSql.contains("reasonDetail"), () -> selectId + " 缺少 reasonDetail");
|
assertTrue(selectSql.contains("reasonDetail"), () -> selectId + " 缺少 reasonDetail");
|
||||||
assertTrue(!selectSql.contains("where 1 = 0"), () -> selectId + " 仍是占位 SQL");
|
assertTrue(!selectSql.contains("where 1 = 0"), () -> selectId + " 仍是占位 SQL");
|
||||||
@@ -236,31 +196,6 @@ class CcdiBankTagAnalysisMapperXmlTest {
|
|||||||
factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
|
factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void externalPersonRules_shouldUseExternalSubjectScopeAndCounterpartyEmployeeMatching() throws Exception {
|
|
||||||
String xml = readXml(RESOURCE);
|
|
||||||
String scopeSql = extractSqlFragment(xml, "externalPersonPredicateSql");
|
|
||||||
String relationSql = extractSelectSql(xml, "selectExternalToStaffOrFamilyTransactionStatements");
|
|
||||||
String annualSql = extractSelectSql(xml, "selectExternalAnnualTurnoverObjects");
|
|
||||||
String gamblingSql = extractSelectSql(xml, "selectExternalMultiPartyGamblingTransferObjects");
|
|
||||||
|
|
||||||
assertTrue(scopeSql.contains("bs.cret_no is not null"));
|
|
||||||
assertTrue(scopeSql.contains("staff.id_card = bs.cret_no"));
|
|
||||||
assertTrue(scopeSql.contains("relation.relation_cert_no = bs.cret_no"));
|
|
||||||
assertTrue(scopeSql.contains("trim(IFNULL(bs.LE_ACCOUNT_NAME, '')) <> trim(IFNULL(bs.CUSTOMER_ACCOUNT_NAME, ''))"));
|
|
||||||
assertFalse(scopeSql.contains("LE_ACCOUNT_NO"));
|
|
||||||
assertTrue(annualSql.contains("'EXTERNAL_CERT_NO' AS objectType"));
|
|
||||||
assertTrue(annualSql.contains("bs.cret_no AS certNo"));
|
|
||||||
assertTrue(gamblingSql.contains("'EXTERNAL_CERT_NO' AS objectType"));
|
|
||||||
assertTrue(gamblingSql.contains("having COUNT(1) > 2"));
|
|
||||||
assertTrue(relationSql.contains("counter_account.owner_type in ('EMPLOYEE', 'RELATION', 'INTERMEDIARY', 'CREDIT_CUSTOMER')"));
|
|
||||||
assertTrue(relationSql.contains("on counter_account.account_no is null"));
|
|
||||||
assertTrue(relationSql.contains("counter_staff.name = trim(bs.CUSTOMER_ACCOUNT_NAME)"));
|
|
||||||
assertTrue(relationSql.contains("counter_relation.relation_name = trim(bs.CUSTOMER_ACCOUNT_NAME)"));
|
|
||||||
assertTrue(relationSql.contains("counter_account.owner_type in ('EMPLOYEE', 'RELATION')"));
|
|
||||||
assertFalse(relationSql.contains("customer_cert_no"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readXml(String resource) throws Exception {
|
private String readXml(String resource) throws Exception {
|
||||||
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resource)) {
|
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resource)) {
|
||||||
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
|||||||
@@ -90,77 +90,13 @@ class CcdiProjectOverviewMapperSqlTest {
|
|||||||
void shouldExposeSuspiciousTransactionAggregationQuery() throws Exception {
|
void shouldExposeSuspiciousTransactionAggregationQuery() throws Exception {
|
||||||
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
||||||
String suspiciousSql = extractSelect(xml, "selectSuspiciousTransactionPage");
|
String suspiciousSql = extractSelect(xml, "selectSuspiciousTransactionPage");
|
||||||
String modelHitSql = extractSqlFragment(xml, "suspiciousTransactionModelHitSql");
|
|
||||||
String aggregatedSql = extractSqlFragment(xml, "suspiciousTransactionAggregatedSql");
|
|
||||||
|
|
||||||
assertTrue(modelHitSql.contains("from ccdi_bank_statement_tag_result tr"), modelHitSql);
|
assertTrue(suspiciousSql.contains("rule_name like '%可疑%'"), suspiciousSql);
|
||||||
assertTrue(modelHitSql.contains("tr.bank_statement_id is not null"), modelHitSql);
|
|
||||||
assertFalse(modelHitSql.contains("rule_name like '%可疑%'"), modelHitSql);
|
|
||||||
assertFalse(modelHitSql.contains("ABNORMAL_CUSTOMER_TRANSACTION"), modelHitSql);
|
|
||||||
assertTrue(suspiciousSql.contains("ccdi_biz_intermediary"), suspiciousSql);
|
assertTrue(suspiciousSql.contains("ccdi_biz_intermediary"), suspiciousSql);
|
||||||
assertTrue(suspiciousSql.contains("ccdi_enterprise_base_info"), suspiciousSql);
|
assertTrue(suspiciousSql.contains("ccdi_enterprise_base_info"), suspiciousSql);
|
||||||
assertTrue(suspiciousSql.contains("group by merged.bankStatementId"), suspiciousSql);
|
assertTrue(suspiciousSql.contains("group by merged.bankStatementId"), suspiciousSql);
|
||||||
assertTrue(aggregatedSql.contains("lpad(merged.matchPriority, 2, '0')"), aggregatedSql);
|
|
||||||
assertTrue(suspiciousSql.contains("hasModelRuleHit"), suspiciousSql);
|
assertTrue(suspiciousSql.contains("hasModelRuleHit"), suspiciousSql);
|
||||||
assertTrue(suspiciousSql.contains("hasNameListHit"), suspiciousSql);
|
assertTrue(suspiciousSql.contains("hasNameListHit"), suspiciousSql);
|
||||||
assertTrue(suspiciousSql.contains("final_result.nameListHitType"), suspiciousSql);
|
|
||||||
|
|
||||||
String reportSuspiciousSql = extractSelect(xml, "selectReportSuspiciousTransactionList");
|
|
||||||
assertTrue(reportSuspiciousSql.contains("final_result.nameListHitType = '中介'"), reportSuspiciousSql);
|
|
||||||
assertTrue(reportSuspiciousSql.contains("疑似与中介往来"), reportSuspiciousSql);
|
|
||||||
assertTrue(reportSuspiciousSql.contains("final_result.nameListHitType = '信贷客户'"), reportSuspiciousSql);
|
|
||||||
assertTrue(reportSuspiciousSql.contains("与信贷客户之间非正常资金往来"), reportSuspiciousSql);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void externalPersonSourceSql_shouldIncludeStatementAndObjectHits() throws Exception {
|
|
||||||
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
|
||||||
String externalSourceSql = extractSqlFragment(xml, "externalPersonSourceSql");
|
|
||||||
String normalizedExternalSourceSql = externalSourceSql.replace("\r\n", "\n");
|
|
||||||
|
|
||||||
assertTrue(externalSourceSql.contains("tr.bank_statement_id = bs.bank_statement_id"), externalSourceSql);
|
|
||||||
assertTrue(externalSourceSql.contains("union all"), externalSourceSql);
|
|
||||||
assertTrue(externalSourceSql.contains("tr.object_type = 'EXTERNAL_CERT_NO'"), externalSourceSql);
|
|
||||||
assertTrue(externalSourceSql.contains("tr.object_key = subject.cert_no"), externalSourceSql);
|
|
||||||
assertTrue(externalSourceSql.contains("'资金' as related_object"), externalSourceSql);
|
|
||||||
assertFalse(externalSourceSql.contains("counter_staff.id_card = bs.customer_cert_no"), externalSourceSql);
|
|
||||||
assertFalse(externalSourceSql.contains("counter_relation.relation_cert_no = bs.customer_cert_no"), externalSourceSql);
|
|
||||||
assertFalse(externalSourceSql.contains("counter_intermediary.person_id = bs.customer_cert_no"), externalSourceSql);
|
|
||||||
assertInOrder(
|
|
||||||
externalSourceSql,
|
|
||||||
"when counter_account.owner_type = 'EMPLOYEE' then '员工'",
|
|
||||||
"when counter_account.owner_type = 'RELATION' then '员工亲属'",
|
|
||||||
"when counter_account.owner_type = 'CREDIT_CUSTOMER' then '信贷客户'",
|
|
||||||
"when counter_account.owner_type = 'INTERMEDIARY' then '中介库人员'",
|
|
||||||
"when counter_staff.id_card is not null then '员工'",
|
|
||||||
"when counter_relation.relation_cert_no is not null then '员工亲属'"
|
|
||||||
);
|
|
||||||
assertTrue(normalizedExternalSourceSql.contains("left join ccdi_base_staff counter_staff\n on counter_account.account_no is null"), externalSourceSql);
|
|
||||||
assertTrue(normalizedExternalSourceSql.contains("left join ccdi_staff_fmy_relation counter_relation\n on counter_account.account_no is null"), externalSourceSql);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void suspiciousTransactionNameListSql_shouldKeepCreditCustomerAndIntermediaryRulesScopedByAmount() throws Exception {
|
|
||||||
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
|
||||||
String nameHitSql = extractSqlFragment(xml, "suspiciousTransactionNameHitSql");
|
|
||||||
String aggregatedSql = extractSqlFragment(xml, "suspiciousTransactionAggregatedSql");
|
|
||||||
|
|
||||||
assertTrue(nameHitSql.contains("account.owner_type = 'CREDIT_CUSTOMER'"), nameHitSql);
|
|
||||||
assertTrue(nameHitSql.contains("account.owner_type = 'INTERMEDIARY'"), nameHitSql);
|
|
||||||
assertTrue(nameHitSql.contains("account.account_no = bs.customer_account_no"), nameHitSql);
|
|
||||||
assertTrue(nameHitSql.contains("'信贷客户' as nameListHitType"), nameHitSql);
|
|
||||||
assertTrue(nameHitSql.contains("'中介' as nameListHitType"), nameHitSql);
|
|
||||||
assertTrue(nameHitSql.contains("intermediary.name = bs.CUSTOMER_ACCOUNT_NAME"), nameHitSql);
|
|
||||||
assertTrue(nameHitSql.contains("enterprise.ent_source = 'INTERMEDIARY'"), nameHitSql);
|
|
||||||
assertTrue(
|
|
||||||
nameHitSql.contains("GREATEST(IFNULL(bs.AMOUNT_DR, 0), IFNULL(bs.AMOUNT_CR, 0)) > 1000"),
|
|
||||||
nameHitSql
|
|
||||||
);
|
|
||||||
assertFalse(nameHitSql.contains("customer_cert_no"), nameHitSql);
|
|
||||||
assertFalse(nameHitSql.contains("social_credit_code = bs"), nameHitSql);
|
|
||||||
assertTrue(aggregatedSql.contains("group by merged.bankStatementId"), aggregatedSql);
|
|
||||||
assertTrue(aggregatedSql.contains("max(merged.hasModelRuleHit) as hasModelRuleHit"), aggregatedSql);
|
|
||||||
assertTrue(aggregatedSql.contains("max(merged.hasNameListHit) as hasNameListHit"), aggregatedSql);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -223,22 +159,4 @@ class CcdiProjectOverviewMapperSqlTest {
|
|||||||
assertTrue(endIndex >= 0, "missing closing select tag: " + selectId);
|
assertTrue(endIndex >= 0, "missing closing select tag: " + selectId);
|
||||||
return xml.substring(startIndex, endIndex);
|
return xml.substring(startIndex, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractSqlFragment(String xml, String sqlId) {
|
|
||||||
String start = "<sql id=\"" + sqlId + "\"";
|
|
||||||
int startIndex = xml.indexOf(start);
|
|
||||||
assertTrue(startIndex >= 0, "missing sql fragment: " + sqlId);
|
|
||||||
int endIndex = xml.indexOf("</sql>", startIndex);
|
|
||||||
assertTrue(endIndex >= 0, "missing closing sql tag: " + sqlId);
|
|
||||||
return xml.substring(startIndex, endIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertInOrder(String sql, String... fragments) {
|
|
||||||
int previousIndex = -1;
|
|
||||||
for (String fragment : fragments) {
|
|
||||||
int currentIndex = sql.indexOf(fragment);
|
|
||||||
assertTrue(currentIndex > previousIndex, () -> "fragment order mismatch: " + fragment + "\n" + sql);
|
|
||||||
previousIndex = currentIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,6 @@ class CcdiProjectSpecialCheckMapperDetailSqlTest {
|
|||||||
assertTrue(xml.contains("select id=\"selectFamilyAssetItemsByScope\""));
|
assertTrue(xml.contains("select id=\"selectFamilyAssetItemsByScope\""));
|
||||||
assertTrue(xml.contains("select id=\"selectFamilyDebtItemsByScope\""));
|
assertTrue(xml.contains("select id=\"selectFamilyDebtItemsByScope\""));
|
||||||
assertTrue(xml.contains("scope.staff_id_card = #{staffIdCard}"));
|
assertTrue(xml.contains("scope.staff_id_card = #{staffIdCard}"));
|
||||||
assertTrue(xml.contains("spouseIsStaff=spouse_is_staff"));
|
|
||||||
assertTrue(xml.contains("relation.relation_cert_no as person_id"));
|
|
||||||
assertTrue(xml.contains("spouse_staff.annual_income"));
|
|
||||||
assertTrue(xml.contains("spouse.spouse_is_staff = 1"));
|
|
||||||
assertTrue(xml.contains("asset.family_id = spouse.spouse_id_card"));
|
|
||||||
assertTrue(xml.contains("source.spouse_staff_asset_record_count = 0"));
|
|
||||||
assertTrue(xml.contains("source.spouse_staff_debt_record_count = 0"));
|
|
||||||
assertTrue(xml.contains("incomeDetail"));
|
assertTrue(xml.contains("incomeDetail"));
|
||||||
assertTrue(xml.contains("assetDetail"));
|
assertTrue(xml.contains("assetDetail"));
|
||||||
assertTrue(xml.contains("debtDetail"));
|
assertTrue(xml.contains("debtDetail"));
|
||||||
@@ -38,9 +31,6 @@ class CcdiProjectSpecialCheckMapperDetailSqlTest {
|
|||||||
assertTrue(xml.contains("asset_main_type"));
|
assertTrue(xml.contains("asset_main_type"));
|
||||||
assertTrue(xml.contains("asset_sub_type"));
|
assertTrue(xml.contains("asset_sub_type"));
|
||||||
assertTrue(xml.contains("holder_name"));
|
assertTrue(xml.contains("holder_name"));
|
||||||
assertTrue(xml.contains("holder_staff.name"));
|
|
||||||
assertTrue(xml.contains("select max(relation.relation_name)"));
|
|
||||||
assertTrue(xml.contains("relation.relation_cert_no = asset.person_id"));
|
|
||||||
assertTrue(xml.contains("current_value"));
|
assertTrue(xml.contains("current_value"));
|
||||||
assertTrue(xml.contains("valuation_date"));
|
assertTrue(xml.contains("valuation_date"));
|
||||||
assertTrue(xml.contains("debt_name"));
|
assertTrue(xml.contains("debt_name"));
|
||||||
@@ -48,8 +38,6 @@ class CcdiProjectSpecialCheckMapperDetailSqlTest {
|
|||||||
assertTrue(xml.contains("debt_sub_type"));
|
assertTrue(xml.contains("debt_sub_type"));
|
||||||
assertTrue(xml.contains("creditor_type"));
|
assertTrue(xml.contains("creditor_type"));
|
||||||
assertTrue(xml.contains("owner_name"));
|
assertTrue(xml.contains("owner_name"));
|
||||||
assertTrue(xml.contains("owner_staff.name"));
|
|
||||||
assertTrue(xml.contains("relation.relation_cert_no = debt.person_id"));
|
|
||||||
assertTrue(xml.contains("principal_balance"));
|
assertTrue(xml.contains("principal_balance"));
|
||||||
assertTrue(xml.contains("query_date"));
|
assertTrue(xml.contains("query_date"));
|
||||||
assertFalse(xml.contains("ccdi_project_overview_employee_result"));
|
assertFalse(xml.contains("ccdi_project_overview_employee_result"));
|
||||||
|
|||||||
@@ -15,31 +15,15 @@ class CcdiProjectSpecialCheckMapperListSqlTest {
|
|||||||
String listSql = extractSelect(xml, "selectFamilyAssetLiabilityList");
|
String listSql = extractSelect(xml, "selectFamilyAssetLiabilityList");
|
||||||
|
|
||||||
assertTrue(listSql.contains("order by risk_level_sort desc, comparison_amount desc, staff_name asc"));
|
assertTrue(listSql.contains("order by risk_level_sort desc, comparison_amount desc, staff_name asc"));
|
||||||
assertTrue(xml.contains("from ccdi_bank_statement bs"));
|
assertTrue(xml.contains("from ccdi_bank_statement_tag_result"));
|
||||||
assertTrue(xml.contains("statement_staff.id_card = trim(bs.cret_no)"));
|
|
||||||
assertTrue(xml.contains("bs.project_id = #{projectId}"));
|
|
||||||
assertTrue(xml.contains("ccdi_base_staff"));
|
assertTrue(xml.contains("ccdi_base_staff"));
|
||||||
assertTrue(xml.contains("ccdi_staff_fmy_relation"));
|
assertTrue(xml.contains("ccdi_staff_fmy_relation"));
|
||||||
assertTrue(xml.contains("relation_type = '配偶'"));
|
assertTrue(xml.contains("relation_type = '配偶'"));
|
||||||
assertTrue(xml.contains("union all"));
|
|
||||||
assertTrue(xml.contains("relation.relation_cert_no as person_id"));
|
|
||||||
assertTrue(xml.contains("inner join ccdi_base_staff current_staff"));
|
|
||||||
assertTrue(xml.contains("spouse_staff.annual_income"));
|
|
||||||
assertTrue(xml.contains("spouse_is_staff"));
|
|
||||||
assertTrue(xml.contains("annual_income"));
|
assertTrue(xml.contains("annual_income"));
|
||||||
assertTrue(xml.contains("current_value"));
|
assertTrue(xml.contains("current_value"));
|
||||||
assertTrue(xml.contains("principal_balance"));
|
assertTrue(xml.contains("principal_balance"));
|
||||||
assertTrue(listSql.contains("self_asset_record_count"));
|
assertTrue(listSql.contains("self_asset_record_count"));
|
||||||
assertTrue(listSql.contains("spouse_staff_asset_record_count"));
|
|
||||||
assertTrue(listSql.contains("self_debt_record_count"));
|
assertTrue(listSql.contains("self_debt_record_count"));
|
||||||
assertTrue(listSql.contains("spouse_staff_debt_record_count"));
|
|
||||||
assertTrue(listSql.contains("source.self_asset_record_count = 0"));
|
|
||||||
assertTrue(listSql.contains("source.spouse_staff_asset_record_count = 0"));
|
|
||||||
assertTrue(listSql.contains("source.self_debt_record_count = 0"));
|
|
||||||
assertTrue(listSql.contains("source.spouse_staff_debt_record_count = 0"));
|
|
||||||
assertTrue(listSql.contains("asset.family_id = spouse.spouse_id_card"));
|
|
||||||
assertTrue(listSql.contains("asset.person_id = spouse.spouse_id_card"));
|
|
||||||
assertTrue(listSql.contains("debt.person_id = spouse.spouse_id_card"));
|
|
||||||
assertTrue(listSql.contains("then 'MISSING_INFO'"));
|
assertTrue(listSql.contains("then 'MISSING_INFO'"));
|
||||||
assertTrue(listSql.contains("then '缺少信息'"));
|
assertTrue(listSql.contains("then '缺少信息'"));
|
||||||
assertTrue(listSql.contains("comparison_amount"));
|
assertTrue(listSql.contains("comparison_amount"));
|
||||||
|
|||||||
@@ -132,49 +132,6 @@ class BankTagRuleConfigResolverTest {
|
|||||||
assertEquals("8888", config.getThresholdValue("ANNUAL_TURNOVER"));
|
assertEquals("8888", config.getThresholdValue("ANNUAL_TURNOVER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void resolve_shouldUseLargeTransactionParamsForExternalLargeTransactionRule() {
|
|
||||||
CcdiProject project = new CcdiProject();
|
|
||||||
project.setProjectId(40L);
|
|
||||||
project.setConfigType("default");
|
|
||||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
|
||||||
when(modelParamMapper.selectByProjectAndModel(0L, "LARGE_TRANSACTION")).thenReturn(List.of(
|
|
||||||
buildParam("LARGE_TRANSACTION", "FREQUENT_TRANSFER", "100000")
|
|
||||||
));
|
|
||||||
|
|
||||||
CcdiBankTagRule ruleMeta = new CcdiBankTagRule();
|
|
||||||
ruleMeta.setModelCode("EXTERNAL_LARGE_TRANSACTION");
|
|
||||||
ruleMeta.setRuleCode("EXTERNAL_SINGLE_LARGE_AMOUNT");
|
|
||||||
ruleMeta.setIndicatorCode("FREQUENT_TRANSFER");
|
|
||||||
|
|
||||||
BankTagRuleExecutionConfig config = resolver.resolve(40L, ruleMeta);
|
|
||||||
|
|
||||||
assertEquals("100000", config.getThresholdValue("FREQUENT_TRANSFER"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void resolve_shouldUseOriginalModelParamsForExternalObjectRules() {
|
|
||||||
CcdiProject project = new CcdiProject();
|
|
||||||
project.setProjectId(40L);
|
|
||||||
project.setConfigType("default");
|
|
||||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
|
||||||
when(modelParamMapper.selectByProjectAndModel(0L, "LARGE_TRANSACTION")).thenReturn(List.of(
|
|
||||||
buildParam("LARGE_TRANSACTION", "CUMULATIVE_TRANSACTION_AMOUNT", "500000"),
|
|
||||||
buildParam("LARGE_TRANSACTION", "ANNUAL_TURNOVER", "800000")
|
|
||||||
));
|
|
||||||
when(modelParamMapper.selectByProjectAndModel(0L, "SUSPICIOUS_GAMBLING")).thenReturn(List.of(
|
|
||||||
buildParam("SUSPICIOUS_GAMBLING", "MULTI_PARTY_AMT_MIN", "500"),
|
|
||||||
buildParam("SUSPICIOUS_GAMBLING", "MULTI_PARTY_AMT_MAX", "5000")
|
|
||||||
));
|
|
||||||
|
|
||||||
assertRuleThresholds("EXTERNAL_LARGE_TRANSACTION", "EXTERNAL_CUMULATIVE_TRANSACTION_AMOUNT",
|
|
||||||
Map.of("CUMULATIVE_TRANSACTION_AMOUNT", "500000"));
|
|
||||||
assertRuleThresholds("EXTERNAL_LARGE_TRANSACTION", "EXTERNAL_ANNUAL_TURNOVER",
|
|
||||||
Map.of("ANNUAL_TURNOVER", "800000"));
|
|
||||||
assertRuleThresholds("EXTERNAL_SUSPICIOUS_GAMBLING", "EXTERNAL_MULTI_PARTY_GAMBLING_TRANSFER",
|
|
||||||
Map.of("MULTI_PARTY_AMT_MIN", "500", "MULTI_PARTY_AMT_MAX", "5000"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resolve_shouldMapPhaseOneThresholdRulesToUppercaseParamCodes() {
|
void resolve_shouldMapPhaseOneThresholdRulesToUppercaseParamCodes() {
|
||||||
CcdiProject project = new CcdiProject();
|
CcdiProject project = new CcdiProject();
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ class CcdiBankTagServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldMarkProjectTagFailedWhenRebuildFails() {
|
void shouldRollbackProjectStatusToProcessingWhenRebuildFails() {
|
||||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||||
|
|
||||||
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
|
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
|
||||||
@@ -329,7 +329,7 @@ class CcdiBankTagServiceImplTest {
|
|||||||
assertThrows(RuntimeException.class,
|
assertThrows(RuntimeException.class,
|
||||||
() -> service.rebuildProject(40L, null, "tester", TriggerType.MANUAL));
|
() -> service.rebuildProject(40L, null, "tester", TriggerType.MANUAL));
|
||||||
|
|
||||||
verify(projectService).updateProjectStatus(40L, "4", "tester");
|
verify(projectService).updateProjectStatus(40L, "0", "tester");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -496,95 +496,6 @@ class CcdiBankTagServiceImplTest {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void rebuildProject_shouldDispatchExternalPersonStatementRules() {
|
|
||||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
|
||||||
|
|
||||||
CcdiBankTagRule largeRule = buildRule("EXTERNAL_LARGE_TRANSACTION", "外部人员大额交易",
|
|
||||||
"EXTERNAL_SINGLE_LARGE_AMOUNT", "外部人员单笔大额交易", "STATEMENT");
|
|
||||||
CcdiBankTagRule nightRule = buildRule("EXTERNAL_ABNORMAL_TRANSACTION", "外部人员异常交易",
|
|
||||||
"EXTERNAL_NIGHT_TRANSACTION", "外部人员夜间集中交易", "STATEMENT");
|
|
||||||
CcdiBankTagRule gamblingRule = buildRule("EXTERNAL_SUSPICIOUS_GAMBLING", "外部人员可疑赌博",
|
|
||||||
"EXTERNAL_GAMBLING_MEMO", "外部人员疑似赌博摘要", "STATEMENT");
|
|
||||||
CcdiBankTagRule relationRule = buildRule("EXTERNAL_SUSPICIOUS_RELATION", "外部人员可疑关系",
|
|
||||||
"EXTERNAL_TO_STAFF_FAMILY_TRANSACTION", "外部人员与员工或员工亲属交易", "STATEMENT");
|
|
||||||
BankTagRuleExecutionConfig largeConfig = buildConfig(40L, largeRule);
|
|
||||||
largeConfig.setThresholdValues(Map.of("FREQUENT_TRANSFER", "100000"));
|
|
||||||
|
|
||||||
BankTagStatementHitVO hit = new BankTagStatementHitVO();
|
|
||||||
hit.setBankStatementId(100L);
|
|
||||||
hit.setGroupId(40);
|
|
||||||
hit.setLogId(40001);
|
|
||||||
hit.setReasonDetail("外部人员命中");
|
|
||||||
|
|
||||||
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(largeRule, nightRule, gamblingRule, relationRule));
|
|
||||||
when(configResolver.resolve(40L, largeRule)).thenReturn(largeConfig);
|
|
||||||
when(configResolver.resolve(40L, nightRule)).thenReturn(buildConfig(40L, nightRule));
|
|
||||||
when(configResolver.resolve(40L, gamblingRule)).thenReturn(buildConfig(40L, gamblingRule));
|
|
||||||
when(configResolver.resolve(40L, relationRule)).thenReturn(buildConfig(40L, relationRule));
|
|
||||||
when(analysisMapper.selectExternalSingleLargeAmountStatements(40L, new BigDecimal("100000"))).thenReturn(List.of(hit));
|
|
||||||
when(analysisMapper.selectExternalNightTransactionStatements(40L)).thenReturn(List.of());
|
|
||||||
when(analysisMapper.selectExternalGamblingMemoStatements(40L)).thenReturn(List.of());
|
|
||||||
when(analysisMapper.selectExternalToStaffOrFamilyTransactionStatements(40L)).thenReturn(List.of());
|
|
||||||
|
|
||||||
service.rebuildProject(40L, null, "admin", TriggerType.MANUAL);
|
|
||||||
|
|
||||||
verify(analysisMapper).selectExternalSingleLargeAmountStatements(40L, new BigDecimal("100000"));
|
|
||||||
verify(analysisMapper).selectExternalNightTransactionStatements(40L);
|
|
||||||
verify(analysisMapper).selectExternalGamblingMemoStatements(40L);
|
|
||||||
verify(analysisMapper).selectExternalToStaffOrFamilyTransactionStatements(40L);
|
|
||||||
verify(resultMapper).insertBatch(argThat(results -> results.stream().anyMatch(item ->
|
|
||||||
"EXTERNAL_LARGE_TRANSACTION".equals(item.getModelCode())
|
|
||||||
&& "EXTERNAL_SINGLE_LARGE_AMOUNT".equals(item.getRuleCode())
|
|
||||||
&& "STATEMENT".equals(item.getResultType())
|
|
||||||
&& Long.valueOf(100L).equals(item.getBankStatementId())
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rebuildProject_shouldDispatchExternalPersonObjectRules() {
|
|
||||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
|
||||||
|
|
||||||
CcdiBankTagRule cumulativeRule = buildRule("EXTERNAL_LARGE_TRANSACTION", "外部人员大额交易",
|
|
||||||
"EXTERNAL_CUMULATIVE_TRANSACTION_AMOUNT", "外部人员累计交易超限", "OBJECT");
|
|
||||||
CcdiBankTagRule annualRule = buildRule("EXTERNAL_LARGE_TRANSACTION", "外部人员大额交易",
|
|
||||||
"EXTERNAL_ANNUAL_TURNOVER", "外部人员年流水交易额超限", "OBJECT");
|
|
||||||
CcdiBankTagRule gamblingRule = buildRule("EXTERNAL_SUSPICIOUS_GAMBLING", "外部人员可疑赌博",
|
|
||||||
"EXTERNAL_MULTI_PARTY_GAMBLING_TRANSFER", "外部人员同日多对手方疑似赌博交易", "OBJECT");
|
|
||||||
BankTagRuleExecutionConfig cumulativeConfig = buildConfig(40L, cumulativeRule);
|
|
||||||
cumulativeConfig.setThresholdValues(Map.of("CUMULATIVE_TRANSACTION_AMOUNT", "500000"));
|
|
||||||
BankTagRuleExecutionConfig annualConfig = buildConfig(40L, annualRule);
|
|
||||||
annualConfig.setThresholdValues(Map.of("ANNUAL_TURNOVER", "800000"));
|
|
||||||
BankTagRuleExecutionConfig gamblingConfig = buildConfig(40L, gamblingRule);
|
|
||||||
gamblingConfig.setThresholdValues(Map.of("MULTI_PARTY_AMT_MIN", "500", "MULTI_PARTY_AMT_MAX", "5000"));
|
|
||||||
|
|
||||||
BankTagObjectHitVO hit = new BankTagObjectHitVO();
|
|
||||||
hit.setObjectType("EXTERNAL_CERT_NO");
|
|
||||||
hit.setObjectKey("330100198801010033");
|
|
||||||
hit.setReasonDetail("外部人员累计交易超限");
|
|
||||||
|
|
||||||
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(cumulativeRule, annualRule, gamblingRule));
|
|
||||||
when(configResolver.resolve(40L, cumulativeRule)).thenReturn(cumulativeConfig);
|
|
||||||
when(configResolver.resolve(40L, annualRule)).thenReturn(annualConfig);
|
|
||||||
when(configResolver.resolve(40L, gamblingRule)).thenReturn(gamblingConfig);
|
|
||||||
when(analysisMapper.selectExternalCumulativeTransactionAmountObjects(40L, new BigDecimal("500000"))).thenReturn(List.of(hit));
|
|
||||||
when(analysisMapper.selectExternalAnnualTurnoverObjects(40L, new BigDecimal("800000"))).thenReturn(List.of());
|
|
||||||
when(analysisMapper.selectExternalMultiPartyGamblingTransferObjects(40L, new BigDecimal("500"), new BigDecimal("5000"))).thenReturn(List.of());
|
|
||||||
|
|
||||||
service.rebuildProject(40L, null, "admin", TriggerType.MANUAL);
|
|
||||||
|
|
||||||
verify(analysisMapper).selectExternalCumulativeTransactionAmountObjects(40L, new BigDecimal("500000"));
|
|
||||||
verify(analysisMapper).selectExternalAnnualTurnoverObjects(40L, new BigDecimal("800000"));
|
|
||||||
verify(analysisMapper).selectExternalMultiPartyGamblingTransferObjects(40L, new BigDecimal("500"), new BigDecimal("5000"));
|
|
||||||
verify(resultMapper).insertBatch(argThat(results -> results.stream().anyMatch(item ->
|
|
||||||
"EXTERNAL_LARGE_TRANSACTION".equals(item.getModelCode())
|
|
||||||
&& "EXTERNAL_CUMULATIVE_TRANSACTION_AMOUNT".equals(item.getRuleCode())
|
|
||||||
&& "OBJECT".equals(item.getResultType())
|
|
||||||
&& "EXTERNAL_CERT_NO".equals(item.getObjectType())
|
|
||||||
&& "330100198801010033".equals(item.getObjectKey())
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void buildSafeTaskErrorMessage_shouldKeepLongMessageForLongTextColumn() throws Exception {
|
void buildSafeTaskErrorMessage_shouldKeepLongMessageForLongTextColumn() throws Exception {
|
||||||
Method method = CcdiBankTagServiceImpl.class.getDeclaredMethod(
|
Method method = CcdiBankTagServiceImpl.class.getDeclaredMethod(
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class CcdiBankTagServiceRiskCountRefreshTest {
|
|||||||
|
|
||||||
verify(taskMapper).updateTask(argThat(task -> "FAILED".equals(task.getStatus())
|
verify(taskMapper).updateTask(argThat(task -> "FAILED".equals(task.getStatus())
|
||||||
&& "refresh failed".equals(task.getErrorMessage())));
|
&& "refresh failed".equals(task.getErrorMessage())));
|
||||||
verify(projectService).updateProjectStatus(40L, "4", "tester");
|
verify(projectService).updateProjectStatus(40L, "0", "tester");
|
||||||
}
|
}
|
||||||
|
|
||||||
private CcdiBankTagRule buildRule() {
|
private CcdiBankTagRule buildRule() {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user