180 Commits

Author SHA1 Message Date
wjj
75d094dad4 支持拉取行内和金综流水 2026-07-20 13:08:33 +08:00
wjj
1561df924f 修复流水明细导出权限校验 2026-07-20 11:26:58 +08:00
wjj
657977dfb7 完善异常账户预警口径和账户查询 2026-07-20 11:13:54 +08:00
wjj
4a75d68bf6 修复流水上传身份证号回填 2026-07-20 11:03:05 +08:00
wjj
b70f967153 调整涉疑交易默认每页十条 2026-07-16 19:05:34 +08:00
wjj
6492fca2dd 设计涉疑交易默认分页调整 2026-07-16 18:52:47 +08:00
wjj
e0188b5e5b 修复涉疑交易筛选与标签错乱 2026-07-16 18:46:51 +08:00
wjj
c3b56bdf2a 设计涉疑交易筛选一致性优化 2026-07-16 18:01:14 +08:00
wjj
6d90bb4e58 优化无外部人员涉疑交易查询 2026-07-16 15:26:14 +08:00
wjj
4a7cfde4af 调整专项排查资产收入匹配口径 2026-07-16 14:47:04 +08:00
wjj
15aa9117c3 调整员工信息维护列表和个人资料校验 2026-07-16 11:28:55 +08:00
wjj
ded07a2c33 优化前端页面布局细节 2026-07-13 16:46:37 +08:00
wjj
7a317204f7 调整流水打标规则口径 2026-07-13 13:05:35 +08:00
wjj
2c6c982ed3 账户库中介风险等级固定为高 2026-07-10 17:46:51 +08:00
wjj
486a499af7 优化流水账号入库和明细筛选 2026-07-10 17:33:16 +08:00
wjj
c999497693 优化外部人员风险明细模型筛选 2026-07-09 16:19:08 +08:00
wjj
3f3e9268f2 实现结果总览排除可疑预警 2026-07-09 14:49:02 +08:00
wjj
9c02812675 合并dev最新代码到dev-ui 2026-07-06 09:05:10 +08:00
wkc
b7e9a8da03 校验上传流水文件名身份证信息 2026-07-03 17:38:46 +08:00
wjj
bb41fd7e89 新增新增贷款查询和导出 2026-07-03 10:01:17 +08:00
wjj
8b9226643c 新增涉疑交易明细筛选 2026-07-03 09:15:14 +08:00
wjj
6d2f40843a 调整项目列表入口和操作按钮 2026-07-02 17:48:08 +08:00
wkc
21c4e91f41 修复专项核查图谱项目ID传递 2026-07-02 17:04:22 +08:00
wkc
87fb6443e6 test 2026-07-02 16:48:17 +08:00
wkc
979ed9669f 调整测试目录忽略规则 2026-07-02 16:46:57 +08:00
wkc
57a33098c9 实现流程项目逻辑删除与恢复 2026-07-02 10:54:36 +08:00
wkc
2f53fc4d1e 用户角色选择改为下拉单选 2026-07-02 10:53:01 +08:00
wkc
64ddc362e3 新增项目管理员角色权限控制 2026-07-01 17:02:17 +08:00
wkc
6c70149e0c 项目上传删除暂不调用流水分析平台 2026-07-01 17:02:17 +08:00
wkc
2298f9e589 移除风险模型导出按钮 2026-07-01 17:02:17 +08:00
wkc
e08f437acc 为风险明细列表添加分页选择器 2026-07-01 17:00:11 +08:00
wjj
6134ec06af 放开涉疑交易流水级模型命中 2026-07-01 16:58:16 +08:00
wjj
dfa35ebd85 优化涉疑交易模型口径和报告展示 2026-07-01 16:58:16 +08:00
wkc
1f82c7e16e 优化资金图谱主题节点检索 2026-07-01 16:57:31 +08:00
wkc
6c206deb5f 实现结果总览详情资产和征信页签 2026-07-01 16:57:31 +08:00
wkc
2999d7cf7d 调整项目人数按流水证件号统计 2026-07-01 16:55:09 +08:00
wjj
f42981f153 放开涉疑交易流水级模型命中 2026-07-01 16:53:44 +08:00
wkc
67f206be1b Refactor project pages and update related docs 2026-07-01 16:47:26 +08:00
wjj
66d34c7ceb 优化涉疑交易模型口径和报告展示 2026-07-01 16:46:23 +08:00
wkc
2e30b2a877 修正双员工夫妻家庭专项核查口径 2026-07-01 16:37:00 +08:00
wkc
d2cdb0f3c9 修复风险总览无风险人员负数问题 2026-07-01 16:37:00 +08:00
wkc
aaec746181 优化资产估值万元展示 2026-07-01 16:37:00 +08:00
wkc
dcfd869d84 Implement credit parse result polling and sentinel handling 2026-07-01 16:37:00 +08:00
wkc
d284923c59 调整征信解析返回解析和日志 2026-07-01 16:37:00 +08:00
wkc
84b04e31ed Refactor credit parse to use remote HTML paths 2026-07-01 16:37:00 +08:00
wkc
507bc1c2d7 Remove obsolete code and documentation 2026-07-01 16:37:00 +08:00
wkc
64891a621d 移除前端默认登录凭据 2026-07-01 16:37:00 +08:00
wkc
606b4f836b 生产配置 2026-07-01 16:37:00 +08:00
wkc
33c1f9df90 新增生产统一部署脚本 2026-07-01 16:37:00 +08:00
wkc
bee1e800ce Fix PDF font loading for project overview reports 2026-07-01 16:37:00 +08:00
wkc
a191cc7ca3 新增专项排查图谱展示 2026-07-01 16:33:59 +08:00
wkc
7826645a90 新增专项排查图谱前端实施计划 2026-07-01 16:31:35 +08:00
wkc
9b11426617 新增专项排查图谱嵌入设计 2026-07-01 16:31:35 +08:00
wkc
1494491d89 展示员工亲属实体关联统信码 2026-07-01 16:31:35 +08:00
wkc
5509ece708 调整招聘信息毕业年月选择控件 2026-07-01 16:31:35 +08:00
wkc
02e45dadcc 完善招聘信息主键关联与工作经历维护 2026-07-01 16:31:35 +08:00
wkc
caeac0f115 招聘导入模板增加招聘类型下拉框 2026-07-01 16:31:35 +08:00
wkc
a4005d3e9f 修复导入模板格式和必填标记 2026-07-01 16:31:35 +08:00
wkc
f9e0a079f5 Update import templates and relation query fields 2026-07-01 16:31:35 +08:00
wkc
e00d60f5c1 回测四类导入自动补入实体库 2026-07-01 16:31:35 +08:00
wkc
87da17bccb 修复双Sheet资产单独导入任务ID 2026-07-01 16:31:35 +08:00
wkc
4a9c6ba64b 实现关联业务自动补入实体库 2026-07-01 16:31:35 +08:00
wkc
17eb11b3b8 uat配置更新 2026-07-01 16:31:35 +08:00
wkc
b740d4c100 移除.DS_Store跟踪 2026-07-01 16:31:35 +08:00
wkc
f5a98bbd7a 修订: 中介实体补入无需机构名称 2026-07-01 16:31:35 +08:00
wkc
987afc3863 调整信息维护页面并修复项目概览统计 2026-07-01 16:31:35 +08:00
wkc
051650370e 计划: 员工资产导入与实体库自动补入修复 2026-07-01 16:28:16 +08:00
wkc
c211413bd2 修订: 明确资产Sheet单独导入规则 2026-07-01 16:28:16 +08:00
wkc
bb9994a339 修订: 完善员工资产导入与实体库补入设计 2026-07-01 16:28:16 +08:00
wkc
daf463bbb4 设计: 员工资产导入与实体库自动补入修复 2026-07-01 16:28:16 +08:00
wkc
6f6f2d264c 统一项目分析弹窗圆角样式 2026-07-01 16:28:16 +08:00
wkc
266108ac06 修复流水上传原始文件名保持 2026-07-01 16:16:48 +08:00
wkc
5ff67442bb 修复流水分析上传文件名传递 2026-07-01 16:16:48 +08:00
wkc
b7bb96b43b 设计: 保持上传流水原始文件名 2026-07-01 16:16:48 +08:00
wkc
9902430350 Add import dropdown validation 2026-07-01 16:16:48 +08:00
wkc
89773caa7b 新增导入下拉框校验实施计划 2026-07-01 16:16:48 +08:00
wkc
def656d57b 完善导入下拉框校验设计 2026-07-01 16:16:48 +08:00
wkc
6b6fae4b92 新增导入下拉框校验设计文档 2026-07-01 16:16:48 +08:00
wkc
0dc3b6ee2f uat配置文件 2026-07-01 16:16:47 +08:00
wjj
04ede64767 优化项目分析详情与异常明细展示 2026-07-01 11:26:01 +08:00
wjj
066d850389 清洗流水本方证件号格式 2026-06-30 13:40:24 +08:00
wjj
5e4bfca05b 完善外部人员预警与项目分析上线内容 2026-06-30 13:26:29 +08:00
wkc
ce66dc3ba8 项目上传删除暂不调用流水分析平台 2026-06-26 15:19:23 +08:00
wkc
f3c1e2ea93 移除风险模型导出按钮 2026-06-26 15:18:46 +08:00
wkc
87b2352001 为风险明细列表添加分页选择器 2026-06-26 15:16:30 +08:00
wjj
999350265b 优化项目详情资金流向图谱展示 2026-06-26 10:05:46 +08:00
wjj
f8ee1ecf1c 调整流水模型理财剔除与提现规则 2026-06-26 10:05:46 +08:00
wjj
35467fd361 放开涉疑交易流水级模型命中 2026-06-26 10:05:46 +08:00
wjj
64cb847db3 优化涉疑交易模型口径和报告展示 2026-06-26 10:03:33 +08:00
wjj
bf290c509c 补充项目分析个人详情页正式化样式 2026-06-26 10:00:09 +08:00
wjj
4e90e22ee2 优化项目详情资金流向图谱展示 2026-06-04 18:01:55 +08:00
wjj
a5eba12ed5 调整流水模型理财剔除与提现规则 2026-06-04 15:55:25 +08:00
wkc
c5b2033a3d 优化资金图谱主题节点检索 2026-06-03 17:11:09 +08:00
wkc
d45e9410ef 实现结果总览详情资产和征信页签 2026-06-02 17:17:49 +08:00
wkc
457e6c1d27 调整项目人数按流水证件号统计 2026-06-02 10:38:43 +08:00
wkc
850f97ea22 调整专项核查图谱展示 2026-06-01 17:37:59 +08:00
wjj
de6e6bd628 放开涉疑交易流水级模型命中 2026-06-01 17:22:51 +08:00
wjj
3a867e5857 修正短时间多次存现本人口径 2026-06-01 17:22:51 +08:00
wjj
19a60c987e 新增图谱功能及验收清单 2026-06-01 17:22:51 +08:00
wjj
aef6c43181 放开涉疑交易流水级模型命中 2026-06-01 16:44:51 +08:00
wkc
7ce721ef93 Refactor project pages and update related docs 2026-06-01 15:52:50 +08:00
wjj
000e8698a5 优化涉疑交易模型口径和报告展示 2026-06-01 15:52:50 +08:00
wjj
9d3e8beceb 补充项目分析个人详情页正式化样式 2026-06-01 15:50:26 +08:00
wjj
4c2ea9d8e6 修正短时间多次存现本人口径 2026-06-01 15:50:25 +08:00
wjj
26cd049991 新增图谱功能及验收清单 2026-05-29 18:33:26 +08:00
wkc
0ea504f6b3 修正双员工夫妻家庭专项核查口径 2026-05-26 17:18:26 +08:00
wkc
a39594faf8 修复风险总览无风险人员负数问题 2026-05-26 16:55:53 +08:00
wkc
1b45296df3 优化资产估值万元展示 2026-05-26 16:53:45 +08:00
wjj
180a892275 优化涉疑交易模型口径和报告展示 2026-05-26 16:43:39 +08:00
wkc
1fadb38d99 Implement credit parse result polling and sentinel handling 2026-05-18 10:56:25 +08:00
wkc
9917d10e59 调整征信解析返回解析和日志 2026-05-13 16:28:57 +08:00
wkc
be443d1b31 Refactor credit parse to use remote HTML paths 2026-05-13 14:20:42 +08:00
wkc
b822cc202e Remove obsolete code and documentation 2026-05-12 17:53:02 +08:00
wkc
598f5dec1c 移除前端默认登录凭据 2026-05-11 16:32:20 +08:00
wkc
0bf73a923f 生产配置 2026-05-09 10:28:00 +08:00
wkc
ec67794f88 新增生产统一部署脚本 2026-05-08 13:32:07 +08:00
wkc
3ef45bc398 Fix PDF font loading for project overview reports 2026-05-08 10:51:42 +08:00
wkc
37e17ac903 新增专项排查图谱展示 2026-05-08 10:22:00 +08:00
wkc
d561d068d6 新增专项排查图谱前端实施计划 2026-05-07 18:53:00 +08:00
wkc
43bc0e4f65 新增专项排查图谱嵌入设计 2026-05-07 18:41:55 +08:00
wkc
3fe78d8d3a 展示员工亲属实体关联统信码 2026-05-07 09:20:06 +08:00
wkc
4c58966529 调整招聘信息毕业年月选择控件 2026-05-07 01:07:52 +08:00
wkc
3bc60fedeb 完善招聘信息主键关联与工作经历维护 2026-05-07 01:04:23 +08:00
wkc
4d1acc7484 招聘导入模板增加招聘类型下拉框 2026-05-07 00:13:00 +08:00
wkc
402a0c3e2f 修复导入模板格式和必填标记 2026-05-07 00:01:27 +08:00
wkc
5980ed0790 Update import templates and relation query fields 2026-05-06 23:37:32 +08:00
wkc
75cb8967da 回测四类导入自动补入实体库 2026-05-06 23:31:43 +08:00
wkc
90a5c42313 合并实体库自动补入与双Sheet导入修复 2026-05-06 20:53:29 +08:00
wkc
356bcdd6de 修复双Sheet资产单独导入任务ID 2026-05-06 20:50:09 +08:00
wkc
9a60371a8f uat配置更新 2026-05-06 20:33:40 +08:00
wkc
380f9b4e7a 移除.DS_Store跟踪 2026-05-06 20:33:01 +08:00
wkc
928f65dfca 修订: 中介实体补入无需机构名称 2026-05-06 18:30:03 +08:00
wkc
c64146ac40 调整信息维护页面并修复项目概览统计 2026-05-06 18:22:26 +08:00
wkc
0541ce0ac6 计划: 员工资产导入与实体库自动补入修复 2026-05-06 18:02:19 +08:00
wkc
26c639134e 修订: 明确资产Sheet单独导入规则 2026-05-06 17:29:32 +08:00
wkc
0f7b57e824 修订: 完善员工资产导入与实体库补入设计 2026-05-06 17:24:25 +08:00
wkc
104e8697fe 设计: 员工资产导入与实体库自动补入修复 2026-05-06 17:18:21 +08:00
wkc
bbc6a2050b 统一项目分析弹窗圆角样式 2026-05-06 17:03:55 +08:00
wjj
ef8147892e 补充项目分析个人详情页正式化样式 2026-05-06 16:18:03 +08:00
wkc
bf7a4c0538 Merge remote-tracking branch 'origin/dev-ui' into dev 2026-05-06 16:15:20 +08:00
wkc
b2e177dd24 修复流水上传原始文件名保持 2026-05-06 15:05:36 +08:00
wkc
2071d04c08 修复流水分析上传文件名传递 2026-05-06 14:49:47 +08:00
wkc
4988ab5944 设计: 保持上传流水原始文件名 2026-05-06 14:22:05 +08:00
wkc
c00d5475e6 Add import dropdown validation 2026-05-06 14:04:21 +08:00
wkc
0b64532959 新增导入下拉框校验实施计划 2026-04-30 16:58:34 +08:00
wkc
9f0ad4ce87 完善导入下拉框校验设计 2026-04-30 16:39:58 +08:00
wkc
75b5989774 新增导入下拉框校验设计文档 2026-04-30 16:36:52 +08:00
wjj
369c682564 新增结果总览一键导出报告 2026-04-30 16:01:13 +08:00
wkc
d8c069a836 uat配置文件 2026-04-30 09:36:34 +08:00
wjj
6f2ea5994a 统一信息维护正式化外壳样式 2026-04-29 17:19:45 +08:00
wkc
95ac01d7dc Merge pull request 'dev' (#2) from dev into dev-ui
Reviewed-on: #2
2026-04-29 01:04:20 +00:00
wkc
be5605c98a 生产配置文件 2026-04-28 17:28:01 +08:00
wkc
cf91be838f 部署脚本 2026-04-28 17:27:24 +08:00
wkc
592c58534a Use browser-use for page testing instructions 2026-04-27 10:22:31 +08:00
wkc
26be75adad 实现关联业务自动补入实体库 2026-04-26 17:23:47 +08:00
wkc
e48c9b4d49 新增关联业务自动补入实体库实施计划 2026-04-26 16:41:26 +08:00
wkc
c0eedfaaa1 修订实体库自动补入设计文档 2026-04-26 16:25:54 +08:00
wkc
344b115038 更新 AGENTS 协作规则 2026-04-26 16:18:41 +08:00
wkc
de9a7b3099 新增关联业务自动补入实体库设计文档 2026-04-26 16:12:12 +08:00
wkc
aa08ab4711 员工亲属实体关联 2026-04-24 13:29:13 +08:00
wkc
b7db711906 完成员工亲属实体关联改造并清理旧数据 2026-04-24 08:55:05 +08:00
wkc
b7d020c0b2 调整实体库管理数据来源维护规则 2026-04-23 17:31:56 +08:00
wkc
d444eafd5f 调整征信记录维护页面搜索区并去掉顶部标题 2026-04-23 17:24:52 +08:00
wkc
c9398881f3 新增员工亲属实体关联维护实施计划 2026-04-23 16:52:15 +08:00
wkc
d79a60ab8c 新增员工亲属实体关联维护设计文档 2026-04-23 16:47:26 +08:00
wkc
e9403662e2 新增信息维护页面搜索区四列栅格前端实施计划 2026-04-23 16:40:06 +08:00
wkc
fd79bfe62f 新增信息维护页面搜索区四列栅格统一设计文档 2026-04-23 16:21:13 +08:00
wkc
c7f4982451 补充招投标供应商企业详情查看 2026-04-23 16:12:56 +08:00
wkc
0b2571b962 统一信息维护页面头部按钮布局 2026-04-23 16:07:17 +08:00
wkc
129e44c808 新增信息维护页面头部按钮统一前端实施计划 2026-04-23 15:53:32 +08:00
wkc
50c177da78 新增信息维护页面头部按钮统一设计文档 2026-04-23 15:50:44 +08:00
wkc
c660025bcc 完善招投标供应商企业详情设计与计划 2026-04-23 15:40:42 +08:00
wkc
bd51991248 新增招投标供应商企业详情设计文档 2026-04-23 15:18:12 +08:00
wkc
a2ba044ebe 完成招聘双Sheet导入改造 2026-04-23 10:27:08 +08:00
wkc
110817abba 新增招聘信息双Sheet导入实施计划 2026-04-23 09:45:50 +08:00
wkc
ff9627d0d9 新增招聘信息双Sheet导入设计文档 2026-04-23 09:40:15 +08:00
wkc
2d1b02474c Add import page test guidelines 2026-04-22 16:38:52 +08:00
wkc
5a9b79d4ee 完善招投标导入测试与文档 2026-04-22 16:20:37 +08:00
wkc
0c5fa6b2c8 Remove obsolete export APIs and persist recruitment work history 2026-04-22 13:38:43 +08:00
wkc
94507e3747 Unify staff recruitment work table collation 2026-04-22 10:07:05 +08:00
734 changed files with 55975 additions and 5624 deletions

BIN
.DS_Store vendored

Binary file not shown.

13
.gitignore vendored
View File

@@ -74,6 +74,7 @@ db_config.conf
# Local deployment bundles # Local deployment bundles
.deploy/ .deploy/
/ccdi_????????.zip
output/ output/
@@ -83,10 +84,16 @@ logs/
ruoyi-ui/vue.config.js ruoyi-ui/vue.config.js
*/src/test/ ruoyi-ui/dist.zip
.pytest_cache/ .pytest_cache/
tests/ tongweb_62318.properties
tongweb_62318.properties .superpowers/
tmp/
.codegraph/
.claude/

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
14.21.3

View File

@@ -1,5 +1,44 @@
# AGENTS.md - AI Coding Assistant Guide # AGENTS.md - AI Coding Assistant Guide
## 全局执行规则
### Git
- Git 提交时使用中文添加描述
- 无视 `.DS_Store`
### AGENT
- `using-superpowers` 只有在用户明确声明调用时才允许启用;不要因为“会话开始”“任务较复杂”或“可能适用”而自动调用
- 当用户没有明确声明 `using-superpowers` 时,按普通流程直接处理需求
- 默认不开启 subagent
- 如用户明确要求启用 subagent所有 subagent 必须使用 `gpt-5.5`;默认推理强度为高,审查类 subagent 的推理强度为超高,测试类 subagent 的推理强度为中
### 文档
- 写完的设计文档必须要由审查类子代理进行内容审查,确保方案与实施方法符合实际需求,并检查是否还有需要明确的功能点
- 当功能设计涉及到前端和后端都有改动时,输出两份执行文档,一份为后端的实施计划,一份为前端的实施计划。如果不是前后端架构的项目不需要输出两份执行文档
- 当功能修改只涉及到前端或只涉及到后端,只需要输出对应的实施计划
- 每一次改动都需要留下实施文档,记录修改的内容
- 每次写设计文档的时候,都要检查一下保存路径是否正确
### 测试
- 测试结束后,自动结束测试时开启的前后端进程
- 前端 Node 需要使用 nvm 进行控制版本
- 在完成页面功能开发后,必须使用 `browser-use` 技能打开浏览器进行实际页面测试,禁止打开 prototype 原型页面进行测试
- 所有生成的测试文件不需要上传到 Git
### 方案规范
当需要给出方案时必须符合以下规范:
- 不允许给出兼容性或补丁性的方案,不允许过度设计,保持最短路径实现且不能违反第一条要求
- 不允许自行给出用户提供的需求以外的方案,例如一些兜底和降级方案,这可能导致业务逻辑偏移问题
- 必须确保方案的逻辑正确,必须经过全链路的逻辑验证
---
## 项目概述 ## 项目概述
本仓库是纪检初核系统主仓库,基于若依 `v3.9.1`,当前技术栈以 `Java 21 + Spring Boot 3 + Vue 2` 为主,并包含独立的流水分析 Mock 服务、Docker 部署文件、SQL 脚本、实施文档与测试文档。 本仓库是纪检初核系统主仓库,基于若依 `v3.9.1`,当前技术栈以 `Java 21 + Spring Boot 3 + Vue 2` 为主,并包含独立的流水分析 Mock 服务、Docker 部署文件、SQL 脚本、实施文档与测试文档。
@@ -28,6 +67,7 @@
- 前端相关安装、构建、调试、测试命令执行前,必须先通过 `nvm` 切换并确认 Node 版本 - 前端相关安装、构建、调试、测试命令执行前,必须先通过 `nvm` 切换并确认 Node 版本
- 测试结束后,自动关闭测试过程中启动的前后端进程 - 测试结束后,自动关闭测试过程中启动的前后端进程
- 重启后端时,必须优先使用 `bin/restart_java_backend.sh` - 重启后端时,必须优先使用 `bin/restart_java_backend.sh`
- 禁止在前端源码、配置、示例数据或页面默认值中硬编码或预填真实账号密码;登录页不得将密码保存到 Cookie、localStorage 或 sessionStorage
--- ---
@@ -80,7 +120,7 @@
mvn clean compile mvn clean compile
# 启动主应用Jar # 启动主应用Jar
cd ruoyi-admin/target && java -jar ruoyi-admin.jar sh bin/restart_java_backend.sh
# 打包全部模块 # 打包全部模块
mvn clean package mvn clean package
@@ -218,6 +258,7 @@ return AjaxResult.success(result);
- 请求统一使用 `@/utils/request` - 请求统一使用 `@/utils/request`
- 新增页面或功能入口时,同步检查 `sys_menu`、路由、权限标识 - 新增页面或功能入口时,同步检查 `sys_menu`、路由、权限标识
- 优先延续现有 `ccdi*` 业务目录与命名方式,不随意新造平行目录 - 优先延续现有 `ccdi*` 业务目录与命名方式,不随意新造平行目录
- 登录页只能在用户主动选择时保存用户名,不允许保存密码或预填默认密码
### 导入功能规范 ### 导入功能规范
@@ -225,6 +266,18 @@ return AjaxResult.success(result);
- 返回结果仅展示失败数据 - 返回结果仅展示失败数据
- 大数据量导入优先采用 EasyExcel + 异步处理 - 大数据量导入优先采用 EasyExcel + 异步处理
### 导入页面测试规范
- 导入功能测试必须进入真实业务页面执行,先在页面内下载当前导入模板,再基于该模板生成测试文件,禁止手工凭记忆新建表头或脱离页面直接构造上传文件
- 双 Sheet 模板的导入测试必须覆盖两个 Sheet 的联动关系;除“缺少 Sheet / 空 Sheet”专项场景外默认两个 Sheet 都要准备测试数据
- 导入测试文件优先放在 `output/spreadsheet/``output/browser-use/`,不提交到 git
- 需要按场景拆分测试文件,避免多个互斥校验互相覆盖;至少覆盖空模板、主信息必填、主信息格式与金额、主从关系异常、供应商校验、缺少/空 Sheet、成功导入、成功与失败混合、失败记录查看、导入后清理回滚
- 主从关系异常测试至少覆盖:已存在主键、供应商有数据但主信息缺失、主信息重复、供应商 Sheet 中采购事项 ID 为空
- 供应商校验测试至少覆盖:重复供应商、多条中标、供应商名称为空、名称超长、联系人超长、银行账户超长、联系电话非法、统一信用代码非法、是否中标枚举非法
- 页面上传后必须核对页面提示、导入状态、失败记录弹窗和列表总数变化;异步导入场景还要核对任务状态从 `PROCESSING` 到最终状态的变化
- 对“成功导入 + 异常数据混合”的样本,必须额外核对成功数据是否真正入库、异常数据是否被拦截,以及是否存在被静默忽略的行
- 导入测试结束后,必须删除本轮成功写入的测试数据,清理页面本地导入任务缓存,并关闭测试过程中启动的前后端进程
--- ---
## 当前仓库结构 ## 当前仓库结构
@@ -348,4 +401,3 @@ ccdi/
- `sql/migration/` 用于增量迁移脚本,新增修复脚本优先按日期或功能命名 - `sql/migration/` 用于增量迁移脚本,新增修复脚本优先按日期或功能命名
- 启动前后端或 Mock 服务做验证后,结束测试时要主动停止进程,避免残留占用端口 - 启动前后端或 Mock 服务做验证后,结束测试时要主动停止进程,避免残留占用端口
- 前端相关安装、构建、调试、测试命令执行前,必须先通过 `nvm` 切换并确认 Node 版本 - 前端相关安装、构建、调试、测试命令执行前,必须先通过 `nvm` 切换并确认 Node 版本

669
CLAUDE.md
View File

@@ -1,669 +0,0 @@
# 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>
```

View File

@@ -51,6 +51,7 @@
"msg": "查询成功", "msg": "查询成功",
"rows": [ "rows": [
{ {
"id": 1002,
"recruitId": "REC20250205001", "recruitId": "REC20250205001",
"recruitName": "2025春季校园招聘", "recruitName": "2025春季校园招聘",
"posName": "Java开发工程师", "posName": "Java开发工程师",
@@ -80,19 +81,19 @@
### 1.2 查询招聘信息详情 ### 1.2 查询招聘信息详情
**接口描述:** 根据招聘项目编号查询详细信息 **接口描述:** 根据招聘信息主键ID查询详细信息
**请求方式:** `GET` **请求方式:** `GET`
**接口路径:** `/ccdi/staffRecruitment/{recruitId}` **接口路径:** `/ccdi/staffRecruitment/{id}`
**权限标识:** `ccdi:staffRecruitment:query` **权限标识:** `ccdi:staffRecruitment:query`
**路径参数:** **路径参数:**
| 参数名 | 类型 | 必填 | 说明 | 示例值 | | 参数名 | 类型 | 必填 | 说明 | 示例值 |
|-----------|--------|----|--------|----------------| |------|------|----|--------------|-----|
| recruitId | String | 是 | 招聘项目编号 | REC20250205001 | | id | Long | 是 | 招聘信息主键ID | 1002 |
**响应示例:** **响应示例:**
@@ -101,6 +102,7 @@
"code": 200, "code": 200,
"msg": "操作成功", "msg": "操作成功",
"data": { "data": {
"id": 1002,
"recruitId": "REC20250205001", "recruitId": "REC20250205001",
"recruitName": "2025春季校园招聘", "recruitName": "2025春季校园招聘",
"posName": "Java开发工程师", "posName": "Java开发工程师",
@@ -237,15 +239,15 @@
**请求方式:** `DELETE` **请求方式:** `DELETE`
**接口路径:** `/ccdi/staffRecruitment/{recruitIds}` **接口路径:** `/ccdi/staffRecruitment/{ids}`
**权限标识:** `ccdi:staffRecruitment:remove` **权限标识:** `ccdi:staffRecruitment:remove`
**路径参数:** **路径参数:**
| 参数名 | 类型 | 必填 | 说明 | 示例值 | | 参数名 | 类型 | 必填 | 说明 | 示例值 |
|------------|----------|----|------------------|-------------------------------| |------|------|----|-----------------------|----------|
| recruitIds | String[] | 是 | 招聘项目编号数组,多个用逗号分隔 | REC20250205001,REC20250205002 | | ids | Long[] | 是 | 招聘信息主键ID数组,多个用逗号分隔 | 1002,1003 |
**响应示例:** **响应示例:**
@@ -276,7 +278,7 @@
| 序号 | 字段名 | 说明 | 必填 | | 序号 | 字段名 | 说明 | 必填 |
|----|----------|-----------|----| |----|----------|-----------|----|
| 1 | 招聘项目编号 | 唯一标识 | 是 | | 1 | 招聘项目编号 | 允许重复 | 是 |
| 2 | 招聘项目名称 | - | 是 | | 2 | 招聘项目名称 | - | 是 |
| 3 | 职位名称 | - | 是 | | 3 | 职位名称 | - | 是 |
| 4 | 职位类别 | - | 是 | | 4 | 职位类别 | - | 是 |
@@ -326,7 +328,7 @@
```json ```json
{ {
"code": 500, "code": 500,
"msg": "很抱歉,导入完成!成功 8 条,失败 2 条,错误如下:<br/>1、招聘项目编号 REC001 导入失败:该招聘项目编号已存在<br/>2、招聘项目编号 REC002 导入失败:证件号码格式不正确" "msg": "很抱歉,导入完成!成功 8 条,失败 2 条,错误如下:<br/>1、招聘项目编号 REC001 导入失败:历史工作经历匹配到多条招聘主信息<br/>2、招聘项目编号 REC002 导入失败:证件号码格式不正确"
} }
``` ```
@@ -375,14 +377,14 @@ Excel导入导出对象,使用EasyExcel注解。
| 401 | 未授权,请先登录 | | 401 | 未授权,请先登录 |
| 403 | 无权限访问 | | 403 | 无权限访问 |
| 404 | 资源不存在 | | 404 | 资源不存在 |
| 409 | 主键冲突 | | 409 | 数据冲突 |
| 500 | 服务器内部错误 | | 500 | 服务器内部错误 |
### 常见业务错误 ### 常见业务错误
| 错误信息 | 说明 | | 错误信息 | 说明 |
|------------|--------------------| |------------|--------------------|
| 该招聘项目编号已存在 | 新增时recruitId重复 | | 历史工作经历匹配到多条招聘主信息 | 招聘项目编号重复且候选人、项目名、职位名仍无法唯一匹配从表归属 |
| 招聘项目编号不能为空 | recruitId字段为空 | | 招聘项目编号不能为空 | recruitId字段为空 |
| 证件号码格式不正确 | 身份证号格式验证失败 | | 证件号码格式不正确 | 身份证号格式验证失败 |
| 毕业年月格式不正确 | candGrad不是YYYYMM格式 | | 毕业年月格式不正确 | candGrad不是YYYYMM格式 |

View File

@@ -1,22 +1,23 @@
4.员工招聘信息表ccdi_staff_recruitment,,,,,, 4.员工招聘信息表ccdi_staff_recruitment,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释 序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,recruit_id,VARCHAR(32),,,,招聘项目编号 1,id,BIGINT,,,,主键ID
2,recruit_name,VARCHAR(100),,,,招聘项目名称 2,recruit_id,VARCHAR(32),,,,招聘项目编号(允许重复)
3,pos_name,VARCHAR(100),,,,职位名称 3,recruit_name,VARCHAR(100),,,,招聘项目名称
4,pos_category,VARCHAR(50),,,,职位类别 4,pos_name,VARCHAR(100),,,,职位名称
5,pos_desc,TEXT,,,,职位描述 5,pos_category,VARCHAR(50),,,,职位类别
6,cand_name,VARCHAR(20),,,,应聘人员姓名 6,pos_desc,TEXT,,,,职位描述
7,cand_edu,VARCHAR(20),,,,应聘人员学历 7,cand_name,VARCHAR(20),,,,应聘人员姓名
8,cand_id,VARCHAR(18),,,,应聘人员证件号码 8,cand_edu,VARCHAR(20),,,,应聘人员学历
9,cand_school,VARCHAR(50),,,,应聘人员毕业院校 9,cand_id,VARCHAR(18),,,,应聘人员证件号码
10,cand_major,VARCHAR(30),,,,应聘人员专业 10,cand_school,VARCHAR(50),,,,应聘人员毕业院校
11,cand_grad,VARCHAR(6),,,,应聘人员毕业年月 11,cand_major,VARCHAR(30),,,,应聘人员专业
12,admit_status,VARCHAR(10),,,,记录录用情况:录用、未录用、放弃等 12,cand_grad,VARCHAR(6),,,,应聘人员毕业年月
13,interviewer_name1,VARCHAR(20),,,,面试官1姓名 13,admit_status,VARCHAR(10),,,,记录录用情况:录用、未录用、放弃等
14,interviewer_id1,VARCHAR(10),,,,面试官1工号 14,interviewer_name1,VARCHAR(20),,,,面试官1姓名
13,interviewer_name2,VARCHAR(20),,,,面试官2姓名 15,interviewer_id1,VARCHAR(10),,,,面试官1工号
14,interviewer_id2,VARCHAR(10),,,,面试官2工号 16,interviewer_name2,VARCHAR(20),,,,面试官2姓名
16,created_by,VARCHAR(20),-,,,记录创建人 17,interviewer_id2,VARCHAR(10),,,,面试官2工号
17,updated_by,VARCHAR(20),-,,,记录更新 18,created_by,VARCHAR(20),-,,,记录创建
18,create_time,VARCHAR(10),0000-00-00,,,创建时间 19,updated_by,VARCHAR(20),-,,,记录更新人
19,update_time,VARCHAR(10),0000-00-00,,,更新时间 20,create_time,VARCHAR(10),0000-00-00,,,创建时间
21,update_time,VARCHAR(10),0000-00-00,,,更新时间
1 4.员工招聘信息表:ccdi_staff_recruitment
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 recruit_id id VARCHAR(32) BIGINT 招聘项目编号 主键ID
4 2 recruit_name recruit_id VARCHAR(100) VARCHAR(32) 招聘项目名称 招聘项目编号(允许重复)
5 3 pos_name recruit_name VARCHAR(100) 职位名称 招聘项目名称
6 4 pos_category pos_name VARCHAR(50) VARCHAR(100) 职位类别 职位名称
7 5 pos_desc pos_category TEXT VARCHAR(50) 职位描述 职位类别
8 6 cand_name pos_desc VARCHAR(20) TEXT 应聘人员姓名 职位描述
9 7 cand_edu cand_name VARCHAR(20) 应聘人员学历 应聘人员姓名
10 8 cand_id cand_edu VARCHAR(18) VARCHAR(20) 应聘人员证件号码 应聘人员学历
11 9 cand_school cand_id VARCHAR(50) VARCHAR(18) 应聘人员毕业院校 应聘人员证件号码
12 10 cand_major cand_school VARCHAR(30) VARCHAR(50) 应聘人员专业 应聘人员毕业院校
13 11 cand_grad cand_major VARCHAR(6) VARCHAR(30) 应聘人员毕业年月 应聘人员专业
14 12 admit_status cand_grad VARCHAR(10) VARCHAR(6) 记录录用情况:录用、未录用、放弃等 应聘人员毕业年月
15 13 interviewer_name1 admit_status VARCHAR(20) VARCHAR(10) 面试官1姓名 记录录用情况:录用、未录用、放弃等
16 14 interviewer_id1 interviewer_name1 VARCHAR(10) VARCHAR(20) 面试官1工号 面试官1姓名
17 13 15 interviewer_name2 interviewer_id1 VARCHAR(20) VARCHAR(10) 面试官2姓名 面试官1工号
18 14 16 interviewer_id2 interviewer_name2 VARCHAR(10) VARCHAR(20) 面试官2工号 面试官2姓名
19 16 17 created_by interviewer_id2 VARCHAR(20) VARCHAR(10) - 记录创建人 面试官2工号
20 17 18 updated_by created_by VARCHAR(20) - 记录更新人 记录创建人
21 18 19 create_time updated_by VARCHAR(10) VARCHAR(20) 0000-00-00 - 创建时间 记录更新人
22 19 20 update_time create_time VARCHAR(10) 0000-00-00 更新时间 创建时间
23 21 update_time VARCHAR(10) 0000-00-00 更新时间

3
assets/图谱.txt Normal file
View File

@@ -0,0 +1,3 @@
关系图谱http://64.202.65.112:8082/atlas/refactor/#/home/graph/downloadService?id=lanxitest&mode=K_EXPAND&type=NORMAL&atlasToken=2C914E5E1FBFBC4AD15163E0AB03B800&params={"vId":"rel_node/15942f5b84bada01ccd25f5e5678ac22"}
资金流图谱http://64.202.65.112:8082/atlas/refactor/#/home/graph/downloadService?id=ccdi_lanxi_trans&mode=K_EXPAND&type=NORMAL&atlasToken=F4BBA291A285858BAF4526C6EC312388&params={"vId":"idno_node/f2f797081494c5c0555a3bbf0f57c5e7"}

View File

@@ -215,7 +215,7 @@ follow_logs() {
start_action() { start_action() {
running_pids=$(collect_pids) running_pids=$(collect_pids)
if [ -n "${running_pids:-}" ]; then if [ -n "${running_pids:-}" ]; then
log_error "检测到已有后端进程在运行: $running_pids,请先执行 stop 或 restart" log_error "检测到已有后端进程在运行: ${running_pids},请先执行 stop 或 restart"
exit 1 exit 1
fi fi

View File

@@ -80,18 +80,6 @@ public class CcdiAccountInfoController extends BaseController {
return success(accountInfoService.selectAccountInfoById(id)); return success(accountInfoService.selectAccountInfoById(id));
} }
/**
* 导出账户库列表
*/
@Operation(summary = "导出账户库列表")
@PreAuthorize("@ss.hasPermi('ccdi:accountInfo:export')")
@Log(title = "账户库管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiAccountInfoQueryDTO queryDTO) {
List<CcdiAccountInfoExcel> list = accountInfoService.selectAccountInfoListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiAccountInfoExcel.class, "账户库管理");
}
/** /**
* 新增账户 * 新增账户
*/ */

View File

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.vo.*; import com.ruoyi.info.collection.domain.vo.*;
import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService; import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
@@ -70,18 +71,6 @@ public class CcdiBaseStaffController extends BaseController {
return success(list); return success(list);
} }
/**
* 导出员工列表
*/
@Operation(summary = "导出员工列表")
@PreAuthorize("@ss.hasPermi('ccdi:baseStaff:export')")
@Log(title = "员工信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiBaseStaffQueryDTO queryDTO) {
List<CcdiBaseStaffExcel> list = baseStaffService.selectBaseStaffListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiBaseStaffExcel.class, "员工信息");
}
/** /**
* 获取员工详细信息 * 获取员工详细信息
*/ */
@@ -132,7 +121,14 @@ public class CcdiBaseStaffController extends BaseController {
@Operation(summary = "下载导入模板") @Operation(summary = "下载导入模板")
@PostMapping("/importTemplate") @PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) { public void importTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiBaseStaffExcel.class, "员工信息"); EasyExcelUtil.importTemplateWithDictDropdown(
response,
CcdiBaseStaffExcel.class,
"员工信息",
CcdiBaseStaffAssetInfoExcel.class,
"员工资产信息",
"员工信息维护导入模板"
);
} }
/** /**
@@ -142,21 +138,26 @@ public class CcdiBaseStaffController extends BaseController {
@PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')") @PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')")
@Log(title = "员工信息", businessType = BusinessType.IMPORT) @Log(title = "员工信息", businessType = BusinessType.IMPORT)
@PostMapping("/importData") @PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { public AjaxResult importData(MultipartFile file) throws Exception {
List<CcdiBaseStaffExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiBaseStaffExcel.class); List<CcdiBaseStaffExcel> staffList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiBaseStaffExcel.class,
"员工信息"
);
List<CcdiBaseStaffAssetInfoExcel> assetList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiBaseStaffAssetInfoExcel.class,
"员工资产信息"
);
if (list == null || list.isEmpty()) { boolean hasStaffRows = staffList != null && !staffList.isEmpty();
boolean hasAssetRows = assetList != null && !assetList.isEmpty();
if (!hasStaffRows && !hasAssetRows) {
return error("至少需要一条数据"); return error("至少需要一条数据");
} }
// 提交异步任务 BaseStaffImportSubmitResultVO result = baseStaffService.importBaseStaffWithAssets(staffList, assetList);
String taskId = baseStaffService.importBaseStaff(list, updateSupport);
// 立即返回,不等待后台任务完成
ImportResultVO result = new ImportResultVO();
result.setTaskId(taskId);
result.setStatus("PROCESSING");
result.setMessage("导入任务已提交,正在后台处理");
return AjaxResult.success("导入任务已提交,正在后台处理", result); return AjaxResult.success("导入任务已提交,正在后台处理", result);
} }
@@ -202,4 +203,5 @@ public class CcdiBaseStaffController extends BaseController {
return getDataTable(pageData, failures.size()); return getDataTable(pageData, failures.size());
} }
} }

View File

@@ -63,18 +63,6 @@ public class CcdiCustEnterpriseRelationController extends BaseController {
return getDataTable(result.getRecords(), result.getTotal()); return getDataTable(result.getRecords(), result.getTotal());
} }
/**
* 导出信贷客户实体关联列表
*/
@Operation(summary = "导出信贷客户实体关联列表")
@PreAuthorize("@ss.hasPermi('ccdi:custEnterpriseRelation:export')")
@Log(title = "信贷客户实体关联信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiCustEnterpriseRelationQueryDTO queryDTO) {
List<CcdiCustEnterpriseRelationExcel> list = relationService.selectRelationListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiCustEnterpriseRelationExcel.class, "信贷客户实体关联信息");
}
/** /**
* 获取信贷客户实体关联详细信息 * 获取信贷客户实体关联详细信息
*/ */

View File

@@ -103,17 +103,6 @@ public class CcdiCustFmyRelationController extends BaseController {
return toAjax(relationService.deleteRelationByIds(ids)); return toAjax(relationService.deleteRelationByIds(ids));
} }
/**
* 导出信贷客户家庭关系
*/
@Operation(summary = "导出信贷客户家庭关系")
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:export')")
@Log(title = "信贷客户家庭关系", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiCustFmyRelationQueryDTO query) {
relationService.exportRelations(query, response);
}
/** /**
* 下载带字典下拉框的导入模板 * 下载带字典下拉框的导入模板
* 使用@DictDropdown注解自动添加下拉框 * 使用@DictDropdown注解自动添加下拉框

View File

@@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel; import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExcel;
import com.ruoyi.info.collection.domain.vo.CcdiPurchaseTransactionVO; import com.ruoyi.info.collection.domain.vo.CcdiPurchaseTransactionVO;
import com.ruoyi.info.collection.domain.vo.ImportResultVO; import com.ruoyi.info.collection.domain.vo.ImportResultVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
@@ -33,12 +34,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 采购交易信息Controller * 招投标信息维护Controller
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
*/ */
@Tag(name = "采购交易信息管理") @Tag(name = "招投标信息维护")
@RestController @RestController
@RequestMapping("/ccdi/purchaseTransaction") @RequestMapping("/ccdi/purchaseTransaction")
public class CcdiPurchaseTransactionController extends BaseController { public class CcdiPurchaseTransactionController extends BaseController {
@@ -50,9 +51,9 @@ public class CcdiPurchaseTransactionController extends BaseController {
private ICcdiPurchaseTransactionImportService transactionImportService; private ICcdiPurchaseTransactionImportService transactionImportService;
/** /**
* 查询采购交易列表 * 查询招投标信息列表
*/ */
@Operation(summary = "查询采购交易列表") @Operation(summary = "查询招投标信息列表")
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:list')") @PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:list')")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo list(CcdiPurchaseTransactionQueryDTO queryDTO) { public TableDataInfo list(CcdiPurchaseTransactionQueryDTO queryDTO) {
@@ -64,21 +65,9 @@ public class CcdiPurchaseTransactionController extends BaseController {
} }
/** /**
* 导出采购交易列表 * 获取招投标信息详细信息
*/ */
@Operation(summary = "导出采购交易列表") @Operation(summary = "获取招投标信息详细信息")
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:export')")
@Log(title = "采购交易信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiPurchaseTransactionQueryDTO queryDTO) {
List<CcdiPurchaseTransactionExcel> list = transactionService.selectTransactionListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiPurchaseTransactionExcel.class, "采购交易信息");
}
/**
* 获取采购交易详细信息
*/
@Operation(summary = "获取采购交易详细信息")
@Parameter(name = "purchaseId", description = "采购事项ID", required = true) @Parameter(name = "purchaseId", description = "采购事项ID", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:query')") @PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:query')")
@GetMapping(value = "/{purchaseId}") @GetMapping(value = "/{purchaseId}")
@@ -87,66 +76,81 @@ public class CcdiPurchaseTransactionController extends BaseController {
} }
/** /**
* 新增采购交易 * 新增招投标信息
*/ */
@Operation(summary = "新增采购交易") @Operation(summary = "新增招投标信息")
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:add')") @PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:add')")
@Log(title = "采购交易信息", businessType = BusinessType.INSERT) @Log(title = "招投标信息维护", businessType = BusinessType.INSERT)
@PostMapping @PostMapping
public AjaxResult add(@Validated @RequestBody CcdiPurchaseTransactionAddDTO addDTO) { public AjaxResult add(@Validated @RequestBody CcdiPurchaseTransactionAddDTO addDTO) {
return toAjax(transactionService.insertTransaction(addDTO)); return toAjax(transactionService.insertTransaction(addDTO));
} }
/** /**
* 修改采购交易 * 修改招投标信息
*/ */
@Operation(summary = "修改采购交易") @Operation(summary = "修改招投标信息")
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:edit')") @PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:edit')")
@Log(title = "采购交易信息", businessType = BusinessType.UPDATE) @Log(title = "招投标信息维护", businessType = BusinessType.UPDATE)
@PutMapping @PutMapping
public AjaxResult edit(@Validated @RequestBody CcdiPurchaseTransactionEditDTO editDTO) { public AjaxResult edit(@Validated @RequestBody CcdiPurchaseTransactionEditDTO editDTO) {
return toAjax(transactionService.updateTransaction(editDTO)); return toAjax(transactionService.updateTransaction(editDTO));
} }
/** /**
* 删除采购交易 * 删除招投标信息
*/ */
@Operation(summary = "删除采购交易") @Operation(summary = "删除招投标信息")
@Parameter(name = "purchaseIds", description = "采购事项ID数组", required = true) @Parameter(name = "purchaseIds", description = "采购事项ID数组", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:remove')") @PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:remove')")
@Log(title = "采购交易信息", businessType = BusinessType.DELETE) @Log(title = "招投标信息维护", businessType = BusinessType.DELETE)
@DeleteMapping("/{purchaseIds}") @DeleteMapping("/{purchaseIds}")
public AjaxResult remove(@PathVariable String[] purchaseIds) { public AjaxResult remove(@PathVariable String[] purchaseIds) {
return toAjax(transactionService.deleteTransactionByIds(purchaseIds)); return toAjax(transactionService.deleteTransactionByIds(purchaseIds));
} }
/** /**
* 下载带字典下拉框的导入模板 * 下载双Sheet导入模板
* 使用@DictDropdown注解自动添加下拉框
*/ */
@Operation(summary = "下载导入模板") @Operation(summary = "下载导入模板")
@PostMapping("/importTemplate") @PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) { public void importTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiPurchaseTransactionExcel.class, "采购交易信息"); EasyExcelUtil.importTemplateWithDictDropdown(
response,
CcdiPurchaseTransactionExcel.class,
"招投标主信息",
CcdiPurchaseTransactionSupplierExcel.class,
"供应商明细",
"招投标信息维护导入模板"
);
} }
/** /**
* 异步导入采购交易 * 异步导入招投标信息
*/ */
@Operation(summary = "异步导入采购交易") @Operation(summary = "异步导入招投标信息")
@Parameter(name = "file", description = "导入文件", required = true) @Parameter(name = "file", description = "导入文件", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:import')") @PreAuthorize("@ss.hasPermi('ccdi:purchaseTransaction:import')")
@Log(title = "采购交易信息", businessType = BusinessType.IMPORT) @Log(title = "招投标信息维护", businessType = BusinessType.IMPORT)
@PostMapping("/importData") @PostMapping("/importData")
public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception { public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception {
List<CcdiPurchaseTransactionExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiPurchaseTransactionExcel.class); List<CcdiPurchaseTransactionExcel> mainList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiPurchaseTransactionExcel.class,
"招投标主信息"
);
List<CcdiPurchaseTransactionSupplierExcel> supplierList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiPurchaseTransactionSupplierExcel.class,
"供应商明细"
);
if (list == null || list.isEmpty()) { if ((mainList == null || mainList.isEmpty()) && (supplierList == null || supplierList.isEmpty())) {
return error("至少需要一条数据"); return error("至少需要一条数据");
} }
// 提交异步任务 // 提交异步任务
String taskId = transactionService.importTransaction(list); String taskId = transactionService.importTransaction(mainList, supplierList);
// 立即返回,不等待后台任务完成 // 立即返回,不等待后台任务完成
ImportResultVO result = new ImportResultVO(); ImportResultVO result = new ImportResultVO();

View File

@@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationOptionVO;
import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO;
import com.ruoyi.info.collection.domain.vo.ImportResultVO; import com.ruoyi.info.collection.domain.vo.ImportResultVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
@@ -33,12 +34,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 员工实体关系信息Controller * 员工亲属实体关Controller
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Tag(name = "员工实体关系信息管理") @Tag(name = "员工亲属实体关管理")
@RestController @RestController
@RequestMapping("/ccdi/staffEnterpriseRelation") @RequestMapping("/ccdi/staffEnterpriseRelation")
public class CcdiStaffEnterpriseRelationController extends BaseController { public class CcdiStaffEnterpriseRelationController extends BaseController {
@@ -50,9 +51,9 @@ public class CcdiStaffEnterpriseRelationController extends BaseController {
private ICcdiStaffEnterpriseRelationImportService relationImportService; private ICcdiStaffEnterpriseRelationImportService relationImportService;
/** /**
* 查询员工实体关列表 * 查询员工亲属实体关列表
*/ */
@Operation(summary = "查询员工实体关列表") @Operation(summary = "查询员工亲属实体关列表")
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:list')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:list')")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo list(CcdiStaffEnterpriseRelationQueryDTO queryDTO) { public TableDataInfo list(CcdiStaffEnterpriseRelationQueryDTO queryDTO) {
@@ -64,21 +65,20 @@ public class CcdiStaffEnterpriseRelationController extends BaseController {
} }
/** /**
* 导出员工实体关系列表 * 查询有效员工亲属下拉列表
*/ */
@Operation(summary = "导出员工实体关系列表") @Operation(summary = "查询有效员工亲属下拉列表")
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:export')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:list')")
@Log(title = "员工实体关系信息", businessType = BusinessType.EXPORT) @GetMapping("/familyOptions")
@PostMapping("/export") public AjaxResult familyOptions(@RequestParam(required = false) String query) {
public void export(HttpServletResponse response, CcdiStaffEnterpriseRelationQueryDTO queryDTO) { List<CcdiStaffEnterpriseRelationOptionVO> list = relationService.selectFamilyOptions(query);
List<CcdiStaffEnterpriseRelationExcel> list = relationService.selectRelationListForExport(queryDTO); return success(list);
EasyExcelUtil.exportExcel(response, list, CcdiStaffEnterpriseRelationExcel.class, "员工实体关系信息");
} }
/** /**
* 获取员工实体关详细信息 * 获取员工亲属实体关详细信息
*/ */
@Operation(summary = "获取员工实体关详细信息") @Operation(summary = "获取员工亲属实体关详细信息")
@Parameter(name = "id", description = "主键ID", required = true) @Parameter(name = "id", description = "主键ID", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:query')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:query')")
@GetMapping(value = "/{id}") @GetMapping(value = "/{id}")
@@ -87,34 +87,34 @@ public class CcdiStaffEnterpriseRelationController extends BaseController {
} }
/** /**
* 新增员工实体关 * 新增员工亲属实体关
*/ */
@Operation(summary = "新增员工实体关") @Operation(summary = "新增员工亲属实体关")
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:add')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:add')")
@Log(title = "员工实体关系信息", businessType = BusinessType.INSERT) @Log(title = "员工亲属实体关", businessType = BusinessType.INSERT)
@PostMapping @PostMapping
public AjaxResult add(@Validated @RequestBody CcdiStaffEnterpriseRelationAddDTO addDTO) { public AjaxResult add(@Validated @RequestBody CcdiStaffEnterpriseRelationAddDTO addDTO) {
return toAjax(relationService.insertRelation(addDTO)); return toAjax(relationService.insertRelation(addDTO));
} }
/** /**
* 修改员工实体关 * 修改员工亲属实体关
*/ */
@Operation(summary = "修改员工实体关") @Operation(summary = "修改员工亲属实体关")
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:edit')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:edit')")
@Log(title = "员工实体关系信息", businessType = BusinessType.UPDATE) @Log(title = "员工亲属实体关", businessType = BusinessType.UPDATE)
@PutMapping @PutMapping
public AjaxResult edit(@Validated @RequestBody CcdiStaffEnterpriseRelationEditDTO editDTO) { public AjaxResult edit(@Validated @RequestBody CcdiStaffEnterpriseRelationEditDTO editDTO) {
return toAjax(relationService.updateRelation(editDTO)); return toAjax(relationService.updateRelation(editDTO));
} }
/** /**
* 删除员工实体关 * 删除员工亲属实体关
*/ */
@Operation(summary = "删除员工实体关") @Operation(summary = "删除员工亲属实体关")
@Parameter(name = "ids", description = "主键ID数组", required = true) @Parameter(name = "ids", description = "主键ID数组", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:remove')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:remove')")
@Log(title = "员工实体关系信息", businessType = BusinessType.DELETE) @Log(title = "员工亲属实体关", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}") @DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) { public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(relationService.deleteRelationByIds(ids)); return toAjax(relationService.deleteRelationByIds(ids));
@@ -127,16 +127,16 @@ public class CcdiStaffEnterpriseRelationController extends BaseController {
@Operation(summary = "下载导入模板") @Operation(summary = "下载导入模板")
@PostMapping("/importTemplate") @PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) { public void importTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiStaffEnterpriseRelationExcel.class, "员工实体关系信息"); EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiStaffEnterpriseRelationExcel.class, "员工亲属实体关");
} }
/** /**
* 异步导入员工实体关 * 异步导入员工亲属实体关
*/ */
@Operation(summary = "异步导入员工实体关") @Operation(summary = "异步导入员工亲属实体关")
@Parameter(name = "file", description = "导入文件", required = true) @Parameter(name = "file", description = "导入文件", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:import')") @PreAuthorize("@ss.hasPermi('ccdi:staffEnterpriseRelation:import')")
@Log(title = "员工实体关系信息", businessType = BusinessType.IMPORT) @Log(title = "员工亲属实体关", businessType = BusinessType.IMPORT)
@PostMapping("/importData") @PostMapping("/importData")
public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception { public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception {
List<CcdiStaffEnterpriseRelationExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiStaffEnterpriseRelationExcel.class); List<CcdiStaffEnterpriseRelationExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiStaffEnterpriseRelationExcel.class);
@@ -152,9 +152,9 @@ public class CcdiStaffEnterpriseRelationController extends BaseController {
ImportResultVO result = new ImportResultVO(); ImportResultVO result = new ImportResultVO();
result.setTaskId(taskId); result.setTaskId(taskId);
result.setStatus("PROCESSING"); result.setStatus("PROCESSING");
result.setMessage("导入任务已提交,正在后台处理"); result.setMessage("员工亲属实体关联导入任务已提交,正在后台处理");
return AjaxResult.success("导入任务已提交,正在后台处理", result); return AjaxResult.success("员工亲属实体关联导入任务已提交,正在后台处理", result);
} }
/** /**

View File

@@ -4,11 +4,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel; import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO;
import com.ruoyi.info.collection.domain.vo.ImportResultVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO; import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO;
import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportSubmitResultVO;
import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService;
import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationService;
import com.ruoyi.info.collection.utils.EasyExcelUtil; import com.ruoyi.info.collection.utils.EasyExcelUtil;
@@ -63,18 +64,6 @@ public class CcdiStaffFmyRelationController extends BaseController {
return getDataTable(result.getRecords(), result.getTotal()); return getDataTable(result.getRecords(), result.getTotal());
} }
/**
* 导出员工亲属关系列表
*/
@Operation(summary = "导出员工亲属关系列表")
@PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:export')")
@Log(title = "员工亲属关系", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiStaffFmyRelationQueryDTO queryDTO) {
List<CcdiStaffFmyRelationExcel> list = relationService.selectRelationListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiStaffFmyRelationExcel.class, "员工亲属关系信息");
}
/** /**
* 获取员工亲属关系详细信息 * 获取员工亲属关系详细信息
*/ */
@@ -127,7 +116,14 @@ public class CcdiStaffFmyRelationController extends BaseController {
@Operation(summary = "下载导入模板") @Operation(summary = "下载导入模板")
@PostMapping("/importTemplate") @PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) { public void importTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiStaffFmyRelationExcel.class, "员工亲属关系信息"); EasyExcelUtil.importTemplateWithDictDropdown(
response,
CcdiStaffFmyRelationExcel.class,
"员工亲属关系信息",
CcdiAssetInfoExcel.class,
"亲属资产信息",
"员工亲属关系维护导入模板"
);
} }
/** /**
@@ -139,21 +135,25 @@ public class CcdiStaffFmyRelationController extends BaseController {
@Log(title = "员工亲属关系", businessType = BusinessType.IMPORT) @Log(title = "员工亲属关系", businessType = BusinessType.IMPORT)
@PostMapping("/importData") @PostMapping("/importData")
public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception { public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception {
List<CcdiStaffFmyRelationExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiStaffFmyRelationExcel.class); List<CcdiStaffFmyRelationExcel> relationList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiStaffFmyRelationExcel.class,
"员工亲属关系信息"
);
List<CcdiAssetInfoExcel> assetList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiAssetInfoExcel.class,
"亲属资产信息"
);
if (list == null || list.isEmpty()) { boolean hasRelationRows = relationList != null && !relationList.isEmpty();
boolean hasAssetRows = assetList != null && !assetList.isEmpty();
if (!hasRelationRows && !hasAssetRows) {
return error("至少需要一条数据"); return error("至少需要一条数据");
} }
// 提交异步任务 StaffFmyRelationImportSubmitResultVO result = relationService.importRelationWithAssets(relationList, assetList);
String taskId = relationService.importRelation(list);
// 立即返回,不等待后台任务完成
ImportResultVO result = new ImportResultVO();
result.setTaskId(taskId);
result.setStatus("PROCESSING");
result.setMessage("导入任务已提交,正在后台处理");
return AjaxResult.success("导入任务已提交,正在后台处理", result); return AjaxResult.success("导入任务已提交,正在后台处理", result);
} }
@@ -198,4 +198,5 @@ public class CcdiStaffFmyRelationController extends BaseController {
return getDataTable(pageData, failures.size()); return getDataTable(pageData, failures.size());
} }
} }

View File

@@ -64,26 +64,14 @@ public class CcdiStaffRecruitmentController extends BaseController {
return getDataTable(result.getRecords(), result.getTotal()); return getDataTable(result.getRecords(), result.getTotal());
} }
/**
* 导出招聘信息列表
*/
@Operation(summary = "导出招聘信息列表")
@PreAuthorize("@ss.hasPermi('ccdi:staffRecruitment:export')")
@Log(title = "员工招聘信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiStaffRecruitmentQueryDTO queryDTO) {
List<CcdiStaffRecruitmentExcel> list = recruitmentService.selectRecruitmentListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiStaffRecruitmentExcel.class, "员工招聘信息");
}
/** /**
* 获取招聘信息详细信息 * 获取招聘信息详细信息
*/ */
@Operation(summary = "获取招聘信息详细信息") @Operation(summary = "获取招聘信息详细信息")
@PreAuthorize("@ss.hasPermi('ccdi:staffRecruitment:query')") @PreAuthorize("@ss.hasPermi('ccdi:staffRecruitment:query')")
@GetMapping(value = "/{recruitId}") @GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable String recruitId) { public AjaxResult getInfo(@PathVariable Long id) {
return success(recruitmentService.selectRecruitmentById(recruitId)); return success(recruitmentService.selectRecruitmentById(id));
} }
/** /**
@@ -114,9 +102,9 @@ public class CcdiStaffRecruitmentController extends BaseController {
@Operation(summary = "删除招聘信息") @Operation(summary = "删除招聘信息")
@PreAuthorize("@ss.hasPermi('ccdi:staffRecruitment:remove')") @PreAuthorize("@ss.hasPermi('ccdi:staffRecruitment:remove')")
@Log(title = "员工招聘信息", businessType = BusinessType.DELETE) @Log(title = "员工招聘信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{recruitIds}") @DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] recruitIds) { public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(recruitmentService.deleteRecruitmentByIds(recruitIds)); return toAjax(recruitmentService.deleteRecruitmentByIds(ids));
} }
/** /**
@@ -126,16 +114,14 @@ public class CcdiStaffRecruitmentController extends BaseController {
@Operation(summary = "下载导入模板") @Operation(summary = "下载导入模板")
@PostMapping("/importTemplate") @PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) { public void importTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiStaffRecruitmentExcel.class, "员工招聘信息"); EasyExcelUtil.importTemplateWithDictDropdown(
} response,
CcdiStaffRecruitmentExcel.class,
/** "招聘信息",
* 下载历史工作经历导入模板 CcdiStaffRecruitmentWorkExcel.class,
*/ "历史工作经历",
@Operation(summary = "下载历史工作经历导入模板") "招聘信息管理导入模板"
@PostMapping("/workImportTemplate") );
public void workImportTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiStaffRecruitmentWorkExcel.class, "历史工作经历");
} }
/** /**
@@ -147,16 +133,25 @@ public class CcdiStaffRecruitmentController extends BaseController {
@Log(title = "员工招聘信息", businessType = BusinessType.IMPORT) @Log(title = "员工招聘信息", businessType = BusinessType.IMPORT)
@PostMapping("/importData") @PostMapping("/importData")
public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception { public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception {
List<CcdiStaffRecruitmentExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiStaffRecruitmentExcel.class); List<CcdiStaffRecruitmentExcel> recruitmentList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiStaffRecruitmentExcel.class,
"招聘信息"
);
List<CcdiStaffRecruitmentWorkExcel> workList = EasyExcelUtil.importExcel(
file.getInputStream(),
CcdiStaffRecruitmentWorkExcel.class,
"历史工作经历"
);
if (list == null || list.isEmpty()) { boolean hasRecruitmentRows = recruitmentList != null && !recruitmentList.isEmpty();
boolean hasWorkRows = workList != null && !workList.isEmpty();
if (!hasRecruitmentRows && !hasWorkRows) {
return error("至少需要一条数据"); return error("至少需要一条数据");
} }
// 提交异步任务 String taskId = recruitmentService.importRecruitment(recruitmentList, workList);
String taskId = recruitmentService.importRecruitment(list);
// 立即返回,不等待后台任务完成
ImportResultVO result = new ImportResultVO(); ImportResultVO result = new ImportResultVO();
result.setTaskId(taskId); result.setTaskId(taskId);
result.setStatus("PROCESSING"); result.setStatus("PROCESSING");
@@ -165,31 +160,6 @@ public class CcdiStaffRecruitmentController extends BaseController {
return AjaxResult.success("导入任务已提交,正在后台处理", result); return AjaxResult.success("导入任务已提交,正在后台处理", result);
} }
/**
* 异步导入历史工作经历
*/
@Operation(summary = "异步导入历史工作经历")
@Parameter(name = "file", description = "导入文件", required = true)
@PreAuthorize("@ss.hasPermi('ccdi:staffRecruitment:import')")
@Log(title = "员工招聘历史工作经历", businessType = BusinessType.IMPORT)
@PostMapping("/importWorkData")
public AjaxResult importWorkData(@Parameter(description = "导入文件") MultipartFile file) throws Exception {
List<CcdiStaffRecruitmentWorkExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiStaffRecruitmentWorkExcel.class);
if (list == null || list.isEmpty()) {
return error("至少需要一条数据");
}
String taskId = recruitmentService.importRecruitmentWork(list);
ImportResultVO result = new ImportResultVO();
result.setTaskId(taskId);
result.setStatus("PROCESSING");
result.setMessage("历史工作经历导入任务已提交,正在后台处理");
return AjaxResult.success("历史工作经历导入任务已提交,正在后台处理", result);
}
/** /**
* 查询导入状态 * 查询导入状态
*/ */

View File

@@ -63,18 +63,6 @@ public class CcdiStaffTransferController extends BaseController {
return getDataTable(result.getRecords(), result.getTotal()); return getDataTable(result.getRecords(), result.getTotal());
} }
/**
* 导出员工调动记录列表
*/
@Operation(summary = "导出员工调动记录列表")
@PreAuthorize("@ss.hasPermi('ccdi:staffTransfer:export')")
@Log(title = "员工调动记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, CcdiStaffTransferQueryDTO queryDTO) {
List<CcdiStaffTransferExcel> list = transferService.selectTransferListForExport(queryDTO);
EasyExcelUtil.exportExcel(response, list, CcdiStaffTransferExcel.class, "员工调动记录信息");
}
/** /**
* 获取员工调动记录详细信息 * 获取员工调动记录详细信息
*/ */

View File

@@ -94,6 +94,6 @@ public class CcdiEnterpriseBaseInfo implements Serializable {
/** 风险等级1-高风险, 2-中风险, 3-低风险 */ /** 风险等级1-高风险, 2-中风险, 3-低风险 */
private String riskLevel; private String riskLevel;
/** 企业来源GENERAL-一般企业, EMP_RELATION-员工关系人, CREDIT_CUSTOMER-信贷客户, INTERMEDIARY-中介, BOTH-兼有 */ /** 企业来源GENERAL-一般企业, EMP_RELATION-员工关系人, CREDIT_CUSTOMER-信贷客户, SUPPLIER-供应商, INTERMEDIARY-中介, BOTH-兼有 */
private String entSource; private String entSource;
} }

View File

@@ -0,0 +1,64 @@
package com.ruoyi.info.collection.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 招投标供应商明细对象 ccdi_purchase_transaction_supplier
*/
@Data
public class CcdiPurchaseTransactionSupplier implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
/** 采购事项ID */
private String purchaseId;
/** 供应商名称 */
private String supplierName;
/** 供应商统一信用代码 */
private String supplierUscc;
/** 供应商联系人 */
private String contactPerson;
/** 供应商联系电话 */
private String contactPhone;
/** 供应商银行账户 */
private String supplierBankAccount;
/** 是否中标1-是0-否 */
private Integer isBidWinner;
/** 排序 */
private Integer sortOrder;
/** 创建时间 */
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/** 更新时间 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/** 创建人 */
@TableField(fill = FieldFill.INSERT)
private String createdBy;
/** 更新人 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updatedBy;
}

View File

@@ -22,8 +22,11 @@ public class CcdiStaffRecruitment implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 主键ID */
@TableId(type = IdType.AUTO)
private Long id;
/** 招聘记录编号 */ /** 招聘记录编号 */
@TableId(type = IdType.INPUT)
private String recruitId; private String recruitId;
/** 招聘项目名称 */ /** 招聘项目名称 */

View File

@@ -28,6 +28,9 @@ public class CcdiStaffRecruitmentWork implements Serializable {
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
private Long id; private Long id;
/** 关联招聘信息主键ID */
private Long recruitmentId;
/** 关联招聘记录编号 */ /** 关联招聘记录编号 */
private String recruitId; private String recruitId;

View File

@@ -39,6 +39,10 @@ public class CcdiAccountInfoQueryDTO implements Serializable {
@Schema(description = "账户姓名") @Schema(description = "账户姓名")
private String accountName; private String accountName;
/** 账户号码 */
@Schema(description = "账户号码")
private String accountNo;
/** 账户类型 */ /** 账户类型 */
@Schema(description = "账户类型") @Schema(description = "账户类型")
private String accountType; private String accountType;

View File

@@ -33,6 +33,10 @@ public class CcdiCustFmyRelationQueryDTO implements Serializable {
@Schema(description = "关系人姓名") @Schema(description = "关系人姓名")
private String relationName; private String relationName;
/** 关系人身份证号 */
@Schema(description = "关系人身份证号")
private String relationCertNo;
/** 状态 */ /** 状态 */
@Schema(description = "状态0-无效1-有效") @Schema(description = "状态0-无效1-有效")
private Integer status; private Integer status;

View File

@@ -89,7 +89,6 @@ public class CcdiEnterpriseBaseInfoAddDTO implements Serializable {
private String shareholder5; private String shareholder5;
@Schema(description = "经营状态") @Schema(description = "经营状态")
@NotBlank(message = "经营状态不能为空")
@Size(max = 50, message = "经营状态长度不能超过50个字符") @Size(max = 50, message = "经营状态长度不能超过50个字符")
private String status; private String status;
@@ -102,6 +101,5 @@ public class CcdiEnterpriseBaseInfoAddDTO implements Serializable {
private String entSource; private String entSource;
@Schema(description = "数据来源") @Schema(description = "数据来源")
@NotBlank(message = "数据来源不能为空")
private String dataSource; private String dataSource;
} }

View File

@@ -89,7 +89,6 @@ public class CcdiEnterpriseBaseInfoEditDTO implements Serializable {
private String shareholder5; private String shareholder5;
@Schema(description = "经营状态") @Schema(description = "经营状态")
@NotBlank(message = "经营状态不能为空")
@Size(max = 50, message = "经营状态长度不能超过50个字符") @Size(max = 50, message = "经营状态长度不能超过50个字符")
private String status; private String status;

View File

@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*; import jakarta.validation.constraints.*;
import lombok.Data; import lombok.Data;
@@ -9,15 +10,16 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* 采购交易信息新增DTO * 招投标信息新增DTO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
*/ */
@Data @Data
@Schema(description = "采购交易信息新增") @Schema(description = "招投标信息新增")
public class CcdiPurchaseTransactionAddDTO implements Serializable { public class CcdiPurchaseTransactionAddDTO implements Serializable {
@Serial @Serial
@@ -88,30 +90,10 @@ public class CcdiPurchaseTransactionAddDTO implements Serializable {
@Schema(description = "采购方式") @Schema(description = "采购方式")
private String purchaseMethod; private String purchaseMethod;
/** 中标供应商名称 */ /** 供应商明细 */
@Size(max = 200, message = "中标供应商名称长度不能超过200个字符") @Valid
@Schema(description = "中标供应商名称") @Schema(description = "供应商明细列表")
private String supplierName; private List<CcdiPurchaseTransactionSupplierDTO> supplierList;
/** 供应商联系人 */
@Size(max = 50, message = "供应商联系人长度不能超过50个字符")
@Schema(description = "供应商联系人")
private String contactPerson;
/** 供应商联系电话 */
@Pattern(regexp = "^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$", message = "供应商联系电话格式不正确")
@Schema(description = "供应商联系电话")
private String contactPhone;
/** 供应商统一信用代码 */
@Pattern(regexp = "^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$", message = "供应商统一信用代码格式不正确")
@Schema(description = "供应商统一信用代码")
private String supplierUscc;
/** 供应商银行账户 */
@Size(max = 50, message = "供应商银行账户长度不能超过50个字符")
@Schema(description = "供应商银行账户")
private String supplierBankAccount;
/** 采购申请日期(或立项日期) */ /** 采购申请日期(或立项日期) */
@NotNull(message = "采购申请日期不能为空") @NotNull(message = "采购申请日期不能为空")

View File

@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*; import jakarta.validation.constraints.*;
import lombok.Data; import lombok.Data;
@@ -9,15 +10,16 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* 采购交易信息编辑DTO * 招投标信息编辑DTO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
*/ */
@Data @Data
@Schema(description = "采购交易信息编辑") @Schema(description = "招投标信息编辑")
public class CcdiPurchaseTransactionEditDTO implements Serializable { public class CcdiPurchaseTransactionEditDTO implements Serializable {
@Serial @Serial
@@ -88,30 +90,10 @@ public class CcdiPurchaseTransactionEditDTO implements Serializable {
@Schema(description = "采购方式") @Schema(description = "采购方式")
private String purchaseMethod; private String purchaseMethod;
/** 中标供应商名称 */ /** 供应商明细 */
@Size(max = 200, message = "中标供应商名称长度不能超过200个字符") @Valid
@Schema(description = "中标供应商名称") @Schema(description = "供应商明细列表")
private String supplierName; private List<CcdiPurchaseTransactionSupplierDTO> supplierList;
/** 供应商联系人 */
@Size(max = 50, message = "供应商联系人长度不能超过50个字符")
@Schema(description = "供应商联系人")
private String contactPerson;
/** 供应商联系电话 */
@Pattern(regexp = "^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$", message = "供应商联系电话格式不正确")
@Schema(description = "供应商联系电话")
private String contactPhone;
/** 供应商统一信用代码 */
@Pattern(regexp = "^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$", message = "供应商统一信用代码格式不正确")
@Schema(description = "供应商统一信用代码")
private String supplierUscc;
/** 供应商银行账户 */
@Size(max = 50, message = "供应商银行账户长度不能超过50个字符")
@Schema(description = "供应商银行账户")
private String supplierBankAccount;
/** 采购申请日期(或立项日期) */ /** 采购申请日期(或立项日期) */
@NotNull(message = "采购申请日期不能为空") @NotNull(message = "采购申请日期不能为空")

View File

@@ -9,13 +9,13 @@ import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** /**
* 采购交易信息查询DTO * 招投标信息查询DTO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
*/ */
@Data @Data
@Schema(description = "采购交易信息查询条件") @Schema(description = "招投标信息查询条件")
public class CcdiPurchaseTransactionQueryDTO implements Serializable { public class CcdiPurchaseTransactionQueryDTO implements Serializable {
@Serial @Serial

View File

@@ -0,0 +1,42 @@
package com.ruoyi.info.collection.domain.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 招投标供应商明细DTO
*/
@Data
@Schema(description = "招投标供应商明细")
public class CcdiPurchaseTransactionSupplierDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@NotBlank(message = "供应商名称不能为空")
@Schema(description = "供应商名称")
private String supplierName;
@NotBlank(message = "供应商统一信用代码不能为空")
@Schema(description = "供应商统一信用代码")
private String supplierUscc;
@Schema(description = "供应商联系人")
private String contactPerson;
@Schema(description = "供应商联系电话")
private String contactPhone;
@Schema(description = "供应商银行账户")
private String supplierBankAccount;
@Schema(description = "是否中标1-是0-否")
private Integer isBidWinner;
@Schema(description = "排序")
private Integer sortOrder;
}

View File

@@ -10,22 +10,22 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 员工实体关系信息新增DTO * 员工亲属实体关新增DTO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Data @Data
@Schema(description = "员工实体关系信息新增") @Schema(description = "员工亲属实体关新增")
public class CcdiStaffEnterpriseRelationAddDTO implements Serializable { public class CcdiStaffEnterpriseRelationAddDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 身份证号 */ /** 亲属身份证号 */
@NotBlank(message = "身份证号不能为空") @NotBlank(message = "亲属身份证号不能为空")
@Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$", message = "身份证号格式不正确") @Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$", message = "亲属身份证号格式不正确")
@Schema(description = "身份证号") @Schema(description = "亲属身份证号")
private String personId; private String personId;
/** 关联人在企业的职务 */ /** 关联人在企业的职务 */

View File

@@ -10,13 +10,13 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 员工实体关系信息编辑DTO * 员工亲属实体关编辑DTO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Data @Data
@Schema(description = "员工实体关系信息编辑") @Schema(description = "员工亲属实体关编辑")
public class CcdiStaffEnterpriseRelationEditDTO implements Serializable { public class CcdiStaffEnterpriseRelationEditDTO implements Serializable {
@Serial @Serial
@@ -27,8 +27,8 @@ public class CcdiStaffEnterpriseRelationEditDTO implements Serializable {
@Schema(description = "主键ID") @Schema(description = "主键ID")
private Long id; private Long id;
/** 身份证号 */ /** 亲属身份证号 */
@Schema(description = "身份证号(不可修改)") @Schema(description = "亲属身份证号(不可修改)")
private String personId; private String personId;
/** 关联人在企业的职务 */ /** 关联人在企业的职务 */

View File

@@ -7,22 +7,30 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 员工实体关系信息查询DTO * 员工亲属实体关查询DTO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Data @Data
@Schema(description = "员工实体关系信息查询条件") @Schema(description = "员工亲属实体关查询条件")
public class CcdiStaffEnterpriseRelationQueryDTO implements Serializable { public class CcdiStaffEnterpriseRelationQueryDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 身份证号 */ /** 亲属身份证号 */
@Schema(description = "身份证号") @Schema(description = "亲属身份证号")
private String personId; private String personId;
/** 亲属姓名 */
@Schema(description = "亲属姓名")
private String relationName;
/** 关联员工 */
@Schema(description = "关联员工")
private String staffPersonName;
/** 统一社会信用代码 */ /** 统一社会信用代码 */
@Schema(description = "统一社会信用代码") @Schema(description = "统一社会信用代码")
private String socialCreditCode; private String socialCreditCode;

View File

@@ -37,6 +37,10 @@ public class CcdiStaffFmyRelationQueryDTO implements Serializable {
@Schema(description = "关系人姓名") @Schema(description = "关系人姓名")
private String relationName; private String relationName;
/** 关系人身份证号 */
@Schema(description = "关系人身份证号")
private String relationCertNo;
/** 状态 */ /** 状态 */
@Schema(description = "状态0-无效1-有效") @Schema(description = "状态0-无效1-有效")
private Integer status; private Integer status;

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.dto;
import com.ruoyi.info.collection.annotation.EnumValid; import com.ruoyi.info.collection.annotation.EnumValid;
import com.ruoyi.info.collection.enums.AdmitStatus; import com.ruoyi.info.collection.enums.AdmitStatus;
import com.ruoyi.info.collection.enums.RecruitType; import com.ruoyi.info.collection.enums.RecruitType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
@@ -10,6 +11,7 @@ import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
/** /**
* 员工招聘信息新增DTO * 员工招聘信息新增DTO
@@ -102,4 +104,8 @@ public class CcdiStaffRecruitmentAddDTO implements Serializable {
/** 面试官2工号 */ /** 面试官2工号 */
@Size(max = 10, message = "面试官2工号长度不能超过10个字符") @Size(max = 10, message = "面试官2工号长度不能超过10个字符")
private String interviewerId2; private String interviewerId2;
/** 历史工作经历列表 */
@Valid
private List<CcdiStaffRecruitmentWorkEditDTO> workExperienceList;
} }

View File

@@ -7,10 +7,12 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import jakarta.validation.Valid;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
/** /**
* 员工招聘信息编辑DTO * 员工招聘信息编辑DTO
@@ -24,8 +26,13 @@ public class CcdiStaffRecruitmentEditDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 主键ID */
@NotNull(message = "招聘信息ID不能为空")
private Long id;
/** 招聘记录编号 */ /** 招聘记录编号 */
@NotNull(message = "招聘记录编号不能为空") @NotBlank(message = "招聘记录编号不能为空")
@Size(max = 32, message = "招聘记录编号长度不能超过32个字符")
private String recruitId; private String recruitId;
/** 招聘项目名称 */ /** 招聘项目名称 */
@@ -91,4 +98,8 @@ public class CcdiStaffRecruitmentEditDTO implements Serializable {
/** 面试官2工号 */ /** 面试官2工号 */
@Size(max = 10, message = "面试官2工号长度不能超过10个字符") @Size(max = 10, message = "面试官2工号长度不能超过10个字符")
private String interviewerId2; private String interviewerId2;
/** 历史工作经历列表 */
@Valid
private List<CcdiStaffRecruitmentWorkEditDTO> workExperienceList;
} }

View File

@@ -0,0 +1,56 @@
package com.ruoyi.info.collection.domain.dto;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 招聘记录历史工作经历编辑DTO
*
* @author ruoyi
* @date 2026-04-22
*/
@Data
public class CcdiStaffRecruitmentWorkEditDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 排序号 */
private Integer sortOrder;
/** 工作单位 */
@Size(max = 200, message = "工作单位长度不能超过200个字符")
private String companyName;
/** 所属部门 */
@Size(max = 100, message = "所属部门长度不能超过100个字符")
private String departmentName;
/** 岗位名称 */
@Size(max = 100, message = "岗位名称长度不能超过100个字符")
private String positionName;
/** 入职年月 */
@Pattern(regexp = "^$|^((19|20)\\d{2})-(0[1-9]|1[0-2])$", message = "入职年月格式不正确,应为YYYY-MM")
private String jobStartMonth;
/** 离职年月 */
@Pattern(regexp = "^$|^((19|20)\\d{2})-(0[1-9]|1[0-2])$", message = "离职年月格式不正确,应为YYYY-MM")
private String jobEndMonth;
/** 离职原因 */
@Size(max = 500, message = "离职原因长度不能超过500个字符")
private String departureReason;
/** 主要工作内容 */
@Size(max = 1000, message = "主要工作内容长度不能超过1000个字符")
private String workContent;
/** 备注 */
@Size(max = 500, message = "备注长度不能超过500个字符")
private String remark;
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -25,6 +26,7 @@ public class CcdiAccountInfoExcel implements Serializable {
@ExcelProperty(value = "证件号*", index = 1) @ExcelProperty(value = "证件号*", index = 1)
@ColumnWidth(24) @ColumnWidth(24)
@TextFormat
private String ownerId; private String ownerId;
@ExcelProperty(value = "账户姓名*", index = 2) @ExcelProperty(value = "账户姓名*", index = 2)
@@ -33,6 +35,7 @@ public class CcdiAccountInfoExcel implements Serializable {
@ExcelProperty(value = "账户号码*", index = 3) @ExcelProperty(value = "账户号码*", index = 3)
@ColumnWidth(28) @ColumnWidth(28)
@TextFormat
private String accountNo; private String accountNo;
@ExcelProperty(value = "账户类型*", index = 4) @ExcelProperty(value = "账户类型*", index = 4)
@@ -49,6 +52,7 @@ public class CcdiAccountInfoExcel implements Serializable {
@ExcelProperty(value = "银行代码", index = 7) @ExcelProperty(value = "银行代码", index = 7)
@ColumnWidth(16) @ColumnWidth(16)
@TextFormat
private String bankCode; private String bankCode;
@ExcelProperty(value = "币种", index = 8) @ExcelProperty(value = "币种", index = 8)

View File

@@ -26,14 +26,14 @@ public class CcdiBaseStaffAssetInfoExcel implements Serializable {
/** 员工身份证号 */ /** 员工身份证号 */
@ExcelProperty(value = "员工身份证号*", index = 0) @ExcelProperty(value = "员工身份证号*", index = 0)
@ColumnWidth(22) @ColumnWidth(24)
@Required @Required
@TextFormat @TextFormat
private String personId; private String personId;
/** 资产大类 */ /** 资产大类 */
@ExcelProperty(value = "资产大类*", index = 1) @ExcelProperty(value = "资产大类*", index = 1)
@ColumnWidth(16) @ColumnWidth(18)
@Required @Required
private String assetMainType; private String assetMainType;
@@ -51,39 +51,39 @@ public class CcdiBaseStaffAssetInfoExcel implements Serializable {
/** 产权占比 */ /** 产权占比 */
@ExcelProperty(value = "产权占比", index = 4) @ExcelProperty(value = "产权占比", index = 4)
@ColumnWidth(12) @ColumnWidth(14)
private BigDecimal ownershipRatio; private BigDecimal ownershipRatio;
/** 购买/评估日期 */ /** 购买/评估日期 */
@ExcelProperty(value = "购买/评估日期", index = 5) @ExcelProperty(value = "购买/评估日期", index = 5)
@ColumnWidth(16) @ColumnWidth(20)
private Date purchaseEvalDate; private Date purchaseEvalDate;
/** 资产原值 */ /** 资产原值 */
@ExcelProperty(value = "资产原值", index = 6) @ExcelProperty(value = "资产原值", index = 6)
@ColumnWidth(16) @ColumnWidth(18)
private BigDecimal originalValue; private BigDecimal originalValue;
/** 当前估值 */ /** 当前估值 */
@ExcelProperty(value = "当前估值*", index = 7) @ExcelProperty(value = "当前估值*", index = 7)
@ColumnWidth(16) @ColumnWidth(18)
@Required @Required
private BigDecimal currentValue; private BigDecimal currentValue;
/** 估值截止日期 */ /** 估值截止日期 */
@ExcelProperty(value = "估值截止日期", index = 8) @ExcelProperty(value = "估值截止日期", index = 8)
@ColumnWidth(16) @ColumnWidth(20)
private Date valuationDate; private Date valuationDate;
/** 资产状态 */ /** 资产状态 */
@ExcelProperty(value = "资产状态*", index = 9) @ExcelProperty(value = "资产状态*", index = 9)
@ColumnWidth(14) @ColumnWidth(16)
@DictDropdown(dictType = "ccdi_asset_status") @DictDropdown(dictType = "ccdi_asset_status")
@Required @Required
private String assetStatus; private String assetStatus;
/** 备注 */ /** 备注 */
@ExcelProperty(value = "备注", index = 10) @ExcelProperty(value = "备注", index = 10)
@ColumnWidth(28) @ColumnWidth(32)
private String remarks; private String remarks;
} }

View File

@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown; import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -25,54 +26,56 @@ public class CcdiBaseStaffExcel implements Serializable {
/** 姓名 */ /** 姓名 */
@ExcelProperty(value = "姓名", index = 0) @ExcelProperty(value = "姓名", index = 0)
@ColumnWidth(15) @ColumnWidth(16)
@Required @Required
private String name; private String name;
/** 员工ID */ /** 员工ID */
@ExcelProperty(value = "员工ID", index = 1) @ExcelProperty(value = "员工ID", index = 1)
@ColumnWidth(15) @ColumnWidth(18)
@Required @Required
private Long staffId; private Long staffId;
/** 所属部门ID */ /** 所属部门ID */
@ExcelProperty(value = "所属部门ID", index = 2) @ExcelProperty(value = "所属部门ID", index = 2)
@ColumnWidth(15) @ColumnWidth(20)
@Required @Required
private Long deptId; private Long deptId;
/** 身份证号 */ /** 身份证号 */
@ExcelProperty(value = "身份证号", index = 3) @ExcelProperty(value = "身份证号", index = 3)
@ColumnWidth(20) @ColumnWidth(24)
@Required @Required
@TextFormat
private String idCard; private String idCard;
/** 电话 */ /** 电话 */
@ExcelProperty(value = "电话", index = 4) @ExcelProperty(value = "电话", index = 4)
@ColumnWidth(15) @ColumnWidth(18)
@Required @Required
@TextFormat
private String phone; private String phone;
/** 年收入 */ /** 年收入 */
@ExcelProperty(value = "年收入(元/年)", index = 5) @ExcelProperty(value = "年收入(元/年)", index = 5)
@ColumnWidth(18) @ColumnWidth(20)
private BigDecimal annualIncome; private BigDecimal annualIncome;
/** 入职时间 */ /** 入职时间 */
@ExcelProperty(value = "入职时间", index = 6) @ExcelProperty(value = "入职时间", index = 6)
@ColumnWidth(15) @ColumnWidth(18)
private Date hireDate; private Date hireDate;
/** 是否党员 */ /** 是否党员 */
@ExcelProperty(value = "是否党员", index = 7) @ExcelProperty(value = "是否党员", index = 7)
@ColumnWidth(12) @ColumnWidth(16)
@DictDropdown(dictType = "ccdi_yes_no_flag") @DictDropdown(dictType = "ccdi_yes_no_flag")
@Required @Required
private Integer partyMember; private Integer partyMember;
/** 状态 */ /** 状态 */
@ExcelProperty(value = "状态", index = 8) @ExcelProperty(value = "状态", index = 8)
@ColumnWidth(10) @ColumnWidth(14)
@DictDropdown(dictType = "ccdi_employee_status") @DictDropdown(dictType = "ccdi_employee_status")
@Required @Required
private String status; private String status;

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -26,6 +27,7 @@ public class CcdiCustEnterpriseRelationExcel implements Serializable {
@ExcelProperty(value = "身份证号", index = 0) @ExcelProperty(value = "身份证号", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
@Schema(description = "身份证号") @Schema(description = "身份证号")
private String personId; private String personId;
@@ -33,6 +35,7 @@ public class CcdiCustEnterpriseRelationExcel implements Serializable {
@ExcelProperty(value = "统一社会信用代码", index = 1) @ExcelProperty(value = "统一社会信用代码", index = 1)
@ColumnWidth(25) @ColumnWidth(25)
@Required @Required
@TextFormat
@Schema(description = "统一社会信用代码") @Schema(description = "统一社会信用代码")
private String socialCreditCode; private String socialCreditCode;

View File

@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown; import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -26,6 +27,7 @@ public class CcdiCustFmyRelationExcel implements Serializable {
@ExcelProperty(value = "信贷客户身份证号*", index = 0) @ExcelProperty(value = "信贷客户身份证号*", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
private String personId; private String personId;
/** 关系类型 */ /** 关系类型 */
@@ -63,16 +65,19 @@ public class CcdiCustFmyRelationExcel implements Serializable {
@ExcelProperty(value = "关系人证件号码*", index = 6) @ExcelProperty(value = "关系人证件号码*", index = 6)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
private String relationCertNo; private String relationCertNo;
/** 手机号码1 */ /** 手机号码1 */
@ExcelProperty(value = "手机号码1", index = 7) @ExcelProperty(value = "手机号码1", index = 7)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String mobilePhone1; private String mobilePhone1;
/** 手机号码2 */ /** 手机号码2 */
@ExcelProperty(value = "手机号码2", index = 8) @ExcelProperty(value = "手机号码2", index = 8)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String mobilePhone2; private String mobilePhone2;
/** 微信名称1 */ /** 微信名称1 */

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown; import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -23,6 +24,7 @@ public class CcdiEnterpriseBaseInfoExcel implements Serializable {
@ExcelProperty(value = "统一社会信用代码*", index = 0) @ExcelProperty(value = "统一社会信用代码*", index = 0)
@ColumnWidth(24) @ColumnWidth(24)
@TextFormat
private String socialCreditCode; private String socialCreditCode;
@ExcelProperty(value = "企业名称*", index = 1) @ExcelProperty(value = "企业名称*", index = 1)
@@ -66,6 +68,7 @@ public class CcdiEnterpriseBaseInfoExcel implements Serializable {
@ExcelProperty(value = "法定代表人证件号码", index = 10) @ExcelProperty(value = "法定代表人证件号码", index = 10)
@ColumnWidth(24) @ColumnWidth(24)
@TextFormat
private String legalCertNo; private String legalCertNo;
@ExcelProperty(value = "股东1", index = 11) @ExcelProperty(value = "股东1", index = 11)
@@ -88,7 +91,7 @@ public class CcdiEnterpriseBaseInfoExcel implements Serializable {
@ColumnWidth(18) @ColumnWidth(18)
private String shareholder5; private String shareholder5;
@ExcelProperty(value = "经营状态*", index = 16) @ExcelProperty(value = "经营状态", index = 16)
@ColumnWidth(16) @ColumnWidth(16)
private String status; private String status;
@@ -99,8 +102,4 @@ public class CcdiEnterpriseBaseInfoExcel implements Serializable {
@ExcelProperty(value = "企业来源*", index = 18) @ExcelProperty(value = "企业来源*", index = 18)
@ColumnWidth(18) @ColumnWidth(18)
private String entSource; private String entSource;
@ExcelProperty(value = "数据来源*", index = 19)
@ColumnWidth(18)
private String dataSource;
} }

View File

@@ -2,6 +2,8 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -17,17 +19,21 @@ public class CcdiIntermediaryEnterpriseRelationExcel implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 中介本人证件号码 */ /** 中介本人证件号码 */
@ExcelProperty(value = "中介本人证件号码*", index = 0) @ExcelProperty(value = "中介本人证件号码", index = 0)
@ColumnWidth(24) @ColumnWidth(24)
@Required
@TextFormat
private String ownerPersonId; private String ownerPersonId;
/** 统一社会信用代码 */ /** 统一社会信用代码 */
@ExcelProperty(value = "统一社会信用代码*", index = 1) @ExcelProperty(value = "统一社会信用代码", index = 1)
@ColumnWidth(24) @ColumnWidth(24)
@Required
@TextFormat
private String socialCreditCode; private String socialCreditCode;
/** 关联职务 */ /** 关联职务 */
@ExcelProperty(value = "关联职务", index = 2) @ExcelProperty(value = "关联职务", index = 2)
@ColumnWidth(20) @ColumnWidth(20)
private String relationPersonPost; private String relationPersonPost;

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown; import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -29,6 +30,7 @@ public class CcdiIntermediaryEntityExcel implements Serializable {
/** 统一社会信用代码 */ /** 统一社会信用代码 */
@ExcelProperty(value = "统一社会信用代码*", index = 1) @ExcelProperty(value = "统一社会信用代码*", index = 1)
@ColumnWidth(20) @ColumnWidth(20)
@TextFormat
private String socialCreditCode; private String socialCreditCode;
/** 主体类型 */ /** 主体类型 */
@@ -77,6 +79,7 @@ public class CcdiIntermediaryEntityExcel implements Serializable {
/** 法定代表人证件号码 */ /** 法定代表人证件号码 */
@ExcelProperty(value = "法定代表人证件号码", index = 10) @ExcelProperty(value = "法定代表人证件号码", index = 10)
@ColumnWidth(20) @ColumnWidth(20)
@TextFormat
private String legalCertNo; private String legalCertNo;
/** 股东1 */ /** 股东1 */

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown; import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -52,11 +53,13 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
/** 证件号码 */ /** 证件号码 */
@ExcelProperty(value = "证件号码*", index = 5) @ExcelProperty(value = "证件号码*", index = 5)
@ColumnWidth(20) @ColumnWidth(20)
@TextFormat
private String personId; private String personId;
/** 手机号码 */ /** 手机号码 */
@ExcelProperty(value = "手机号码", index = 6) @ExcelProperty(value = "手机号码", index = 6)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String mobile; private String mobile;
/** 微信号 */ /** 微信号 */
@@ -77,6 +80,7 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
/** 企业统一信用码 */ /** 企业统一信用码 */
@ExcelProperty(value = "企业统一信用码", index = 10) @ExcelProperty(value = "企业统一信用码", index = 10)
@ColumnWidth(20) @ColumnWidth(20)
@TextFormat
private String socialCreditCode; private String socialCreditCode;
/** 职位 */ /** 职位 */
@@ -87,6 +91,7 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
/** 关联中介本人证件号码 */ /** 关联中介本人证件号码 */
@ExcelProperty(value = "关联中介本人证件号码", index = 12) @ExcelProperty(value = "关联中介本人证件号码", index = 12)
@ColumnWidth(24) @ColumnWidth(24)
@TextFormat
private String relatedNumId; private String relatedNumId;
/** 备注 */ /** 备注 */

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -11,7 +12,7 @@ import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
/** /**
* 采购交易信息Excel导入导出对象 * 招投标主信息Excel导入导出对象
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
@@ -26,6 +27,7 @@ public class CcdiPurchaseTransactionExcel implements Serializable {
@ExcelProperty(value = "采购事项ID", index = 0) @ExcelProperty(value = "采购事项ID", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
private String purchaseId; private String purchaseId;
/** 采购类别 */ /** 采购类别 */
@@ -88,107 +90,84 @@ public class CcdiPurchaseTransactionExcel implements Serializable {
@Required @Required
private String purchaseMethod; private String purchaseMethod;
/** 中标供应商名称 */
@ExcelProperty(value = "中标供应商名称", index = 12)
@ColumnWidth(25)
private String supplierName;
/** 供应商联系人 */
@ExcelProperty(value = "供应商联系人", index = 13)
@ColumnWidth(15)
private String contactPerson;
/** 供应商联系电话 */
@ExcelProperty(value = "供应商联系电话", index = 14)
@ColumnWidth(18)
private String contactPhone;
/** 供应商统一信用代码 */
@ExcelProperty(value = "供应商统一信用代码", index = 15)
@ColumnWidth(25)
private String supplierUscc;
/** 供应商银行账户 */
@ExcelProperty(value = "供应商银行账户", index = 16)
@ColumnWidth(20)
private String supplierBankAccount;
/** 采购申请日期(或立项日期) */ /** 采购申请日期(或立项日期) */
@ExcelProperty(value = "采购申请日期", index = 17) @ExcelProperty(value = "采购申请日期", index = 12)
@ColumnWidth(18) @ColumnWidth(18)
@Required @Required
private Date applyDate; private Date applyDate;
/** 采购计划批准日期 */ /** 采购计划批准日期 */
@ExcelProperty(value = "采购计划批准日期", index = 18) @ExcelProperty(value = "采购计划批准日期", index = 13)
@ColumnWidth(18) @ColumnWidth(18)
private Date planApproveDate; private Date planApproveDate;
/** 采购公告发布日期 */ /** 采购公告发布日期 */
@ExcelProperty(value = "采购公告发布日期", index = 19) @ExcelProperty(value = "采购公告发布日期", index = 14)
@ColumnWidth(18) @ColumnWidth(18)
private Date announceDate; private Date announceDate;
/** 开标日期 */ /** 开标日期 */
@ExcelProperty(value = "开标日期", index = 20) @ExcelProperty(value = "开标日期", index = 15)
@ColumnWidth(18) @ColumnWidth(18)
private Date bidOpenDate; private Date bidOpenDate;
/** 合同签订日期 */ /** 合同签订日期 */
@ExcelProperty(value = "合同签订日期", index = 21) @ExcelProperty(value = "合同签订日期", index = 16)
@ColumnWidth(18) @ColumnWidth(18)
private Date contractSignDate; private Date contractSignDate;
/** 预计交货日期 */ /** 预计交货日期 */
@ExcelProperty(value = "预计交货日期", index = 22) @ExcelProperty(value = "预计交货日期", index = 17)
@ColumnWidth(18) @ColumnWidth(18)
private Date expectedDeliveryDate; private Date expectedDeliveryDate;
/** 实际交货日期 */ /** 实际交货日期 */
@ExcelProperty(value = "实际交货日期", index = 23) @ExcelProperty(value = "实际交货日期", index = 18)
@ColumnWidth(18) @ColumnWidth(18)
private Date actualDeliveryDate; private Date actualDeliveryDate;
/** 验收日期 */ /** 验收日期 */
@ExcelProperty(value = "验收日期", index = 24) @ExcelProperty(value = "验收日期", index = 19)
@ColumnWidth(18) @ColumnWidth(18)
private Date acceptanceDate; private Date acceptanceDate;
/** 结算日期 */ /** 结算日期 */
@ExcelProperty(value = "结算日期", index = 25) @ExcelProperty(value = "结算日期", index = 20)
@ColumnWidth(18) @ColumnWidth(18)
private Date settlementDate; private Date settlementDate;
/** 申请人工号 */ /** 申请人工号 */
@ExcelProperty(value = "申请人工号", index = 26) @ExcelProperty(value = "申请人工号", index = 21)
@ColumnWidth(15) @ColumnWidth(15)
@Required @Required
@TextFormat
private String applicantId; private String applicantId;
/** 申请人姓名 */ /** 申请人姓名 */
@ExcelProperty(value = "申请人姓名", index = 27) @ExcelProperty(value = "申请人姓名", index = 22)
@ColumnWidth(15) @ColumnWidth(15)
@Required @Required
private String applicantName; private String applicantName;
/** 申请部门 */ /** 申请部门 */
@ExcelProperty(value = "申请部门", index = 28) @ExcelProperty(value = "申请部门", index = 23)
@ColumnWidth(18) @ColumnWidth(18)
@Required @Required
private String applyDepartment; private String applyDepartment;
/** 采购负责人工号 */ /** 采购负责人工号 */
@ExcelProperty(value = "采购负责人工号", index = 29) @ExcelProperty(value = "采购负责人工号", index = 24)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String purchaseLeaderId; private String purchaseLeaderId;
/** 采购负责人姓名 */ /** 采购负责人姓名 */
@ExcelProperty(value = "采购负责人姓名", index = 30) @ExcelProperty(value = "采购负责人姓名", index = 25)
@ColumnWidth(15) @ColumnWidth(15)
private String purchaseLeaderName; private String purchaseLeaderName;
/** 采购部门 */ /** 采购部门 */
@ExcelProperty(value = "采购部门", index = 31) @ExcelProperty(value = "采购部门", index = 26)
@ColumnWidth(18) @ColumnWidth(18)
private String purchaseDepartment; private String purchaseDepartment;
} }

View File

@@ -0,0 +1,60 @@
package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 招投标供应商明细Excel对象
*/
@Data
public class CcdiPurchaseTransactionSupplierExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "采购事项ID", index = 0)
@ColumnWidth(20)
@Required
@TextFormat
private String purchaseId;
@ExcelProperty(value = "供应商名称", index = 1)
@ColumnWidth(25)
@Required
private String supplierName;
@ExcelProperty(value = "供应商统一信用代码", index = 2)
@ColumnWidth(25)
@TextFormat
private String supplierUscc;
@ExcelProperty(value = "供应商联系人", index = 3)
@ColumnWidth(15)
private String contactPerson;
@ExcelProperty(value = "供应商联系电话", index = 4)
@ColumnWidth(18)
@TextFormat
private String contactPhone;
@ExcelProperty(value = "供应商银行账户", index = 5)
@ColumnWidth(20)
@TextFormat
private String supplierBankAccount;
@ExcelProperty(value = "是否中标", index = 6)
@ColumnWidth(12)
@DictDropdown(dictType = "ccdi_yes_no_flag")
private String isBidWinner;
@ExcelProperty(value = "排序", index = 7)
@ColumnWidth(10)
private Integer sortOrder;
}

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -10,29 +11,31 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 员工实体关系信息Excel导入导出对象 * 员工亲属实体关Excel导入导出对象
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Data @Data
@Schema(description = "员工实体关系信息Excel导入导出对象") @Schema(description = "员工亲属实体关Excel导入导出对象")
public class CcdiStaffEnterpriseRelationExcel implements Serializable { public class CcdiStaffEnterpriseRelationExcel implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 身份证号 */ /** 亲属身份证号 */
@ExcelProperty(value = "身份证号", index = 0) @ExcelProperty(value = "亲属身份证号", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@Schema(description = "身份证号") @TextFormat
@Schema(description = "亲属身份证号")
private String personId; private String personId;
/** 统一社会信用代码 */ /** 统一社会信用代码 */
@ExcelProperty(value = "统一社会信用代码", index = 1) @ExcelProperty(value = "统一社会信用代码", index = 1)
@ColumnWidth(25) @ColumnWidth(25)
@Required @Required
@TextFormat
@Schema(description = "统一社会信用代码") @Schema(description = "统一社会信用代码")
private String socialCreditCode; private String socialCreditCode;

View File

@@ -28,6 +28,7 @@ public class CcdiStaffFmyRelationExcel implements Serializable {
@ExcelProperty(value = "员工身份证号*", index = 0) @ExcelProperty(value = "员工身份证号*", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
private String personId; private String personId;
/** 关系类型 */ /** 关系类型 */
@@ -71,11 +72,13 @@ public class CcdiStaffFmyRelationExcel implements Serializable {
/** 手机号码1 */ /** 手机号码1 */
@ExcelProperty(value = "手机号码1", index = 7) @ExcelProperty(value = "手机号码1", index = 7)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String mobilePhone1; private String mobilePhone1;
/** 手机号码2 */ /** 手机号码2 */
@ExcelProperty(value = "手机号码2", index = 8) @ExcelProperty(value = "手机号码2", index = 8)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String mobilePhone2; private String mobilePhone2;
/** 家庭成员年收入 */ /** 家庭成员年收入 */

View File

@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown; import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -21,10 +22,11 @@ public class CcdiStaffRecruitmentExcel implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 招聘项目编号 */ /** 招聘记录编号 */
@ExcelProperty(value = "招聘项目编号", index = 0) @ExcelProperty(value = "招聘记录编号", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
private String recruitId; private String recruitId;
/** 招聘项目名称 */ /** 招聘项目名称 */
@@ -51,66 +53,76 @@ public class CcdiStaffRecruitmentExcel implements Serializable {
@Required @Required
private String posDesc; private String posDesc;
/** 应聘人员姓名 */
@ExcelProperty(value = "应聘人员姓名", index = 5)
@ColumnWidth(15)
@Required
private String candName;
/** 应聘人员学历 */
@ExcelProperty(value = "应聘人员学历", index = 6)
@ColumnWidth(15)
@Required
private String candEdu;
/** 应聘人员证件号码 */
@ExcelProperty(value = "应聘人员证件号码", index = 7)
@ColumnWidth(20)
@Required
private String candId;
/** 应聘人员毕业院校 */
@ExcelProperty(value = "应聘人员毕业院校", index = 8)
@ColumnWidth(20)
@Required
private String candSchool;
/** 应聘人员专业 */
@ExcelProperty(value = "应聘人员专业", index = 9)
@ColumnWidth(15)
@Required
private String candMajor;
/** 应聘人员毕业年月 */
@ExcelProperty(value = "应聘人员毕业年月", index = 10)
@ColumnWidth(15)
@Required
private String candGrad;
/** 录用情况 */ /** 录用情况 */
@ExcelProperty(value = "录用情况", index = 11) @ExcelProperty(value = "录用情况", index = 5)
@ColumnWidth(10) @ColumnWidth(10)
@DictDropdown(dictType = "ccdi_admit_status") @DictDropdown(dictType = "ccdi_admit_status")
@Required @Required
private String admitStatus; private String admitStatus;
/** 候选人姓名 */
@ExcelProperty(value = "候选人姓名", index = 6)
@ColumnWidth(15)
@Required
private String candName;
/** 招聘类型 */
@ExcelProperty(value = "招聘类型", index = 7)
@ColumnWidth(12)
@DictDropdown(dictType = "ccdi_recruit_type")
@Required
private String recruitType;
/** 应聘人员学历 */
@ExcelProperty(value = "学历", index = 8)
@ColumnWidth(15)
@Required
private String candEdu;
/** 应聘人员证件号码 */
@ExcelProperty(value = "证件号码", index = 9)
@ColumnWidth(20)
@Required
@TextFormat
private String candId;
/** 应聘人员毕业年月 */
@ExcelProperty(value = "毕业年月", index = 10)
@ColumnWidth(15)
@Required
private String candGrad;
/** 应聘人员毕业院校 */
@ExcelProperty(value = "毕业院校", index = 11)
@ColumnWidth(20)
@Required
private String candSchool;
/** 应聘人员专业 */
@ExcelProperty(value = "专业", index = 12)
@ColumnWidth(15)
@Required
private String candMajor;
/** 面试官1姓名 */ /** 面试官1姓名 */
@ExcelProperty(value = "面试官1姓名", index = 12) @ExcelProperty(value = "面试官1姓名", index = 13)
@ColumnWidth(15) @ColumnWidth(15)
private String interviewerName1; private String interviewerName1;
/** 面试官1工号 */ /** 面试官1工号 */
@ExcelProperty(value = "面试官1工号", index = 13) @ExcelProperty(value = "面试官1工号", index = 14)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String interviewerId1; private String interviewerId1;
/** 面试官2姓名 */ /** 面试官2姓名 */
@ExcelProperty(value = "面试官2姓名", index = 14) @ExcelProperty(value = "面试官2姓名", index = 15)
@ColumnWidth(15) @ColumnWidth(15)
private String interviewerName2; private String interviewerName2;
/** 面试官2工号 */ /** 面试官2工号 */
@ExcelProperty(value = "面试官2工号", index = 15) @ExcelProperty(value = "面试官2工号", index = 16)
@ColumnWidth(15) @ColumnWidth(15)
@TextFormat
private String interviewerId2; private String interviewerId2;
} }

View File

@@ -3,6 +3,7 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
@@ -24,6 +25,7 @@ public class CcdiStaffRecruitmentWorkExcel implements Serializable {
@ExcelProperty(value = "招聘记录编号", index = 0) @ExcelProperty(value = "招聘记录编号", index = 0)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
@TextFormat
private String recruitId; private String recruitId;
/** 候选人姓名 */ /** 候选人姓名 */
@@ -61,20 +63,20 @@ public class CcdiStaffRecruitmentWorkExcel implements Serializable {
@ColumnWidth(18) @ColumnWidth(18)
private String departmentName; private String departmentName;
/** 岗位 */ /** 岗位名称 */
@ExcelProperty(value = "岗位", index = 7) @ExcelProperty(value = "岗位名称", index = 7)
@ColumnWidth(20) @ColumnWidth(20)
@Required @Required
private String positionName; private String positionName;
/** 入职年月 */ /** 入职年月 */
@ExcelProperty(value = "入职年月", index = 8) @ExcelProperty(value = "入职时间", index = 8)
@ColumnWidth(12) @ColumnWidth(12)
@Required @Required
private String jobStartMonth; private String jobStartMonth;
/** 离职年月 */ /** 离职年月 */
@ExcelProperty(value = "离职年月", index = 9) @ExcelProperty(value = "离职时间", index = 9)
@ColumnWidth(12) @ColumnWidth(12)
private String jobEndMonth; private String jobEndMonth;
@@ -83,8 +85,8 @@ public class CcdiStaffRecruitmentWorkExcel implements Serializable {
@ColumnWidth(30) @ColumnWidth(30)
private String departureReason; private String departureReason;
/** 工作内容 */ /** 主要工作内容 */
@ExcelProperty(value = "工作内容", index = 11) @ExcelProperty(value = "主要工作内容", index = 11)
@ColumnWidth(35) @ColumnWidth(35)
private String workContent; private String workContent;

View File

@@ -15,6 +15,14 @@ import java.math.BigDecimal;
@Schema(description = "亲属资产信息导入失败记录") @Schema(description = "亲属资产信息导入失败记录")
public class AssetImportFailureVO { public class AssetImportFailureVO {
/** Sheet名称 */
@Schema(description = "Sheet名称")
private String sheetName;
/** Excel行号 */
@Schema(description = "Excel行号")
private Integer rowNum;
/** 亲属证件号 */ /** 亲属证件号 */
@Schema(description = "亲属证件号") @Schema(description = "亲属证件号")
private String personId; private String personId;

View File

@@ -15,6 +15,14 @@ import java.math.BigDecimal;
@Schema(description = "员工资产信息导入失败记录") @Schema(description = "员工资产信息导入失败记录")
public class BaseStaffAssetImportFailureVO { public class BaseStaffAssetImportFailureVO {
/** Sheet名称 */
@Schema(description = "Sheet名称")
private String sheetName;
/** Excel行号 */
@Schema(description = "Excel行号")
private Integer rowNum;
/** 员工身份证号 */ /** 员工身份证号 */
@Schema(description = "员工身份证号") @Schema(description = "员工身份证号")
private String personId; private String personId;

View File

@@ -0,0 +1,23 @@
package com.ruoyi.info.collection.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 员工双Sheet导入提交结果
*
* @author ruoyi
*/
@Data
@Schema(description = "员工双Sheet导入提交结果")
public class BaseStaffImportSubmitResultVO {
@Schema(description = "员工信息导入任务ID")
private String staffTaskId;
@Schema(description = "员工资产信息导入任务ID")
private String assetTaskId;
@Schema(description = "提交说明")
private String message;
}

View File

@@ -0,0 +1,45 @@
package com.ruoyi.info.collection.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 招投标供应商明细VO
*/
@Data
@Schema(description = "招投标供应商明细")
public class CcdiPurchaseTransactionSupplierVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
private Long id;
@Schema(description = "采购事项ID")
private String purchaseId;
@Schema(description = "供应商名称")
private String supplierName;
@Schema(description = "供应商统一信用代码")
private String supplierUscc;
@Schema(description = "供应商联系人")
private String contactPerson;
@Schema(description = "供应商联系电话")
private String contactPhone;
@Schema(description = "供应商银行账户")
private String supplierBankAccount;
@Schema(description = "是否中标1-是0-否")
private Integer isBidWinner;
@Schema(description = "排序")
private Integer sortOrder;
}

View File

@@ -8,15 +8,16 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* 采购交易信息VO * 招投标信息VO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
*/ */
@Data @Data
@Schema(description = "采购交易信息") @Schema(description = "招投标信息")
public class CcdiPurchaseTransactionVO implements Serializable { public class CcdiPurchaseTransactionVO implements Serializable {
@Serial @Serial
@@ -90,6 +91,14 @@ public class CcdiPurchaseTransactionVO implements Serializable {
@Schema(description = "供应商银行账户") @Schema(description = "供应商银行账户")
private String supplierBankAccount; private String supplierBankAccount;
/** 参与供应商数 */
@Schema(description = "参与供应商数")
private Integer supplierCount;
/** 供应商明细 */
@Schema(description = "供应商明细列表")
private List<CcdiPurchaseTransactionSupplierVO> supplierList;
/** 采购申请日期(或立项日期) */ /** 采购申请日期(或立项日期) */
@JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "采购申请日期") @Schema(description = "采购申请日期")

View File

@@ -0,0 +1,37 @@
package com.ruoyi.info.collection.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 员工亲属实体关联下拉选项VO
*
* @author ruoyi
* @date 2026-04-23
*/
@Data
@Schema(description = "员工亲属实体关联下拉选项")
public class CcdiStaffEnterpriseRelationOptionVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 亲属身份证号 */
@Schema(description = "亲属身份证号")
private String relationCertNo;
/** 亲属姓名 */
@Schema(description = "亲属姓名")
private String relationName;
/** 关联员工身份证号 */
@Schema(description = "关联员工身份证号")
private String staffPersonId;
/** 关联员工姓名 */
@Schema(description = "关联员工姓名")
private String staffPersonName;
}

View File

@@ -9,13 +9,13 @@ import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** /**
* 员工实体关系信息VO * 员工亲属实体关VO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Data @Data
@Schema(description = "员工实体关系信息") @Schema(description = "员工亲属实体关")
public class CcdiStaffEnterpriseRelationVO implements Serializable { public class CcdiStaffEnterpriseRelationVO implements Serializable {
@Serial @Serial
@@ -25,13 +25,21 @@ public class CcdiStaffEnterpriseRelationVO implements Serializable {
@Schema(description = "主键ID") @Schema(description = "主键ID")
private Long id; private Long id;
/** 身份证号 */ /** 亲属身份证号 */
@Schema(description = "身份证号") @Schema(description = "亲属身份证号")
private String personId; private String personId;
/** 员工姓名 */ /** 亲属姓名 */
@Schema(description = "员工姓名") @Schema(description = "亲属姓名")
private String personName; private String relationName;
/** 关联员工身份证号 */
@Schema(description = "关联员工身份证号")
private String staffPersonId;
/** 关联员工姓名 */
@Schema(description = "关联员工姓名")
private String staffPersonName;
/** 关联人在企业的职务 */ /** 关联人在企业的职务 */
@Schema(description = "关联人在企业的职务") @Schema(description = "关联人在企业的职务")

View File

@@ -19,6 +19,9 @@ public class CcdiStaffRecruitmentVO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 招聘记录编号 */ /** 招聘记录编号 */
private String recruitId; private String recruitId;

View File

@@ -14,8 +14,14 @@ import java.math.BigDecimal;
@Schema(description = "导入失败记录") @Schema(description = "导入失败记录")
public class ImportFailureVO { public class ImportFailureVO {
@Schema(description = "Sheet名称")
private String sheetName;
@Schema(description = "Excel行号")
private Integer rowNum;
@Schema(description = "柜员号") @Schema(description = "柜员号")
private Long employeeId; private Long staffId;
@Schema(description = "姓名") @Schema(description = "姓名")
private String name; private String name;

View File

@@ -22,7 +22,7 @@ public class IntermediaryEnterpriseRelationImportFailureVO implements Serializab
@Schema(description = "统一社会信用代码") @Schema(description = "统一社会信用代码")
private String socialCreditCode; private String socialCreditCode;
@Schema(description = "关联职务") @Schema(description = "关联职务")
private String relationPersonPost; private String relationPersonPost;
@Schema(description = "备注") @Schema(description = "备注")

View File

@@ -6,15 +6,23 @@ import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* 采购交易信息导入失败记录VO * 招投标信息导入失败记录VO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-06 * @date 2026-02-06
*/ */
@Data @Data
@Schema(description = "采购交易信息导入失败记录") @Schema(description = "招投标信息导入失败记录")
public class PurchaseTransactionImportFailureVO { public class PurchaseTransactionImportFailureVO {
/** 失败来源Sheet */
@Schema(description = "失败来源Sheet")
private String sheetName;
/** 失败行号 */
@Schema(description = "失败行号")
private String sheetRowNum;
/** 采购事项ID */ /** 采购事项ID */
@Schema(description = "采购事项ID") @Schema(description = "采购事项ID")
private String purchaseId; private String purchaseId;

View File

@@ -13,6 +13,12 @@ import lombok.Data;
@Schema(description = "招聘信息导入失败记录") @Schema(description = "招聘信息导入失败记录")
public class RecruitmentImportFailureVO { public class RecruitmentImportFailureVO {
@Schema(description = "失败Sheet")
private String sheetName;
@Schema(description = "失败行号")
private String sheetRowNum;
@Schema(description = "招聘项目编号") @Schema(description = "招聘项目编号")
private String recruitId; private String recruitId;

View File

@@ -7,22 +7,26 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 员工实体关系信息导入失败记录VO * 员工亲属实体关导入失败记录VO
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09
*/ */
@Data @Data
@Schema(description = "员工实体关系信息导入失败记录") @Schema(description = "员工亲属实体关导入失败记录")
public class StaffEnterpriseRelationImportFailureVO implements Serializable { public class StaffEnterpriseRelationImportFailureVO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 身份证号 */ /** 亲属身份证号 */
@Schema(description = "身份证号") @Schema(description = "亲属身份证号")
private String personId; private String personId;
/** 亲属姓名 */
@Schema(description = "亲属姓名")
private String relationName;
/** 统一社会信用代码 */ /** 统一社会信用代码 */
@Schema(description = "统一社会信用代码") @Schema(description = "统一社会信用代码")
private String socialCreditCode; private String socialCreditCode;

View File

@@ -15,6 +15,14 @@ import java.math.BigDecimal;
@Schema(description = "员工亲属关系信息导入失败记录") @Schema(description = "员工亲属关系信息导入失败记录")
public class StaffFmyRelationImportFailureVO { public class StaffFmyRelationImportFailureVO {
/** Sheet名称 */
@Schema(description = "Sheet名称")
private String sheetName;
/** Excel行号 */
@Schema(description = "Excel行号")
private Integer rowNum;
/** 员工身份证号 */ /** 员工身份证号 */
@Schema(description = "员工身份证号") @Schema(description = "员工身份证号")
private String personId; private String personId;

View File

@@ -0,0 +1,24 @@
package com.ruoyi.info.collection.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 员工亲属关系双Sheet导入提交结果
*
* @author ruoyi
* @date 2026-04-22
*/
@Data
@Schema(description = "员工亲属关系双Sheet导入提交结果")
public class StaffFmyRelationImportSubmitResultVO {
@Schema(description = "员工亲属关系导入任务ID")
private String relationTaskId;
@Schema(description = "亲属资产信息导入任务ID")
private String assetTaskId;
@Schema(description = "提交结果提示")
private String message;
}

View File

@@ -10,6 +10,7 @@ public enum EnterpriseSource {
GENERAL("GENERAL", "一般企业"), GENERAL("GENERAL", "一般企业"),
EMP_RELATION("EMP_RELATION", "员工关系人"), EMP_RELATION("EMP_RELATION", "员工关系人"),
CREDIT_CUSTOMER("CREDIT_CUSTOMER", "信贷客户"), CREDIT_CUSTOMER("CREDIT_CUSTOMER", "信贷客户"),
SUPPLIER("SUPPLIER", "供应商"),
INTERMEDIARY("INTERMEDIARY", "中介"), INTERMEDIARY("INTERMEDIARY", "中介"),
BOTH("BOTH", "兼有"); BOTH("BOTH", "兼有");

View File

@@ -1,15 +1,28 @@
package com.ruoyi.info.collection.handler; package com.ruoyi.info.collection.handler;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.handler.SheetWriteHandler; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.ruoyi.common.annotation.Required; import com.ruoyi.common.annotation.Required;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** /**
* EasyExcel必填字段标注处理器 * EasyExcel必填字段标注处理器
@@ -18,13 +31,18 @@ import java.util.*;
* @author ruoyi * @author ruoyi
*/ */
@Slf4j @Slf4j
public class RequiredFieldWriteHandler implements SheetWriteHandler { public class RequiredFieldWriteHandler implements CellWriteHandler {
/** /**
* 实体类Class对象 * 实体类Class对象
*/ */
private final Class<?> modelClass; private final Class<?> modelClass;
/**
* 必填字段列索引集合
*/
private final Set<Integer> requiredColumns;
/** /**
* 构造函数 * 构造函数
* *
@@ -32,39 +50,30 @@ public class RequiredFieldWriteHandler implements SheetWriteHandler {
*/ */
public RequiredFieldWriteHandler(Class<?> modelClass) { public RequiredFieldWriteHandler(Class<?> modelClass) {
this.modelClass = modelClass; this.modelClass = modelClass;
this.requiredColumns = parseRequiredFields();
} }
@Override @Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { public void afterCellDispose(WriteSheetHolder writeSheetHolder,
// 获取工作表 WriteTableHolder writeTableHolder,
Sheet sheet = writeSheetHolder.getSheet(); List<WriteCellData<?>> cellDataList,
Cell cell,
// 获取表头行第1行索引为0 Head head,
Row headerRow = sheet.getRow(0); Integer relativeRowIndex,
if (headerRow == null) { Boolean isHead) {
log.warn("表头行不存在,跳过必填字段标注"); if (!Boolean.TRUE.equals(isHead) || cell == null || !requiredColumns.contains(cell.getColumnIndex())) {
return; return;
} }
// 创建红色字体样式 Workbook workbook = cell.getSheet().getWorkbook();
Workbook workbook = writeWorkbookHolder.getWorkbook();
CellStyle redStyle = createRedFontStyle(workbook); CellStyle redStyle = createRedFontStyle(workbook);
// 解析实体类中的必填字段 String originalValue = cell.getStringCellValue();
Set<Integer> requiredColumns = parseRequiredFields(); if (originalValue != null && !originalValue.endsWith("*")) {
cell.setCellValue(originalValue + "*");
// 为必填字段的表头添加红色星号
for (Integer columnIndex : requiredColumns) {
Cell cell = headerRow.getCell(columnIndex);
if (cell != null) {
String originalValue = cell.getStringCellValue();
// 添加红色星号
cell.setCellValue(originalValue + "*");
// 应用红色样式到星号
cell.setCellStyle(redStyle);
log.info("为列[{}]的表头添加必填标记(*)", columnIndex);
}
} }
cell.setCellStyle(redStyle);
log.info("为列[{}]的表头添加必填标记(*)", cell.getColumnIndex());
} }
/** /**

View File

@@ -28,6 +28,14 @@ public interface CcdiBaseStaffMapper extends BaseMapper<CcdiBaseStaff> {
Page<CcdiBaseStaffVO> selectBaseStaffPageWithDept(@Param("page") Page<CcdiBaseStaffVO> page, Page<CcdiBaseStaffVO> selectBaseStaffPageWithDept(@Param("page") Page<CcdiBaseStaffVO> page,
@Param("query") CcdiBaseStaffQueryDTO queryDTO); @Param("query") CcdiBaseStaffQueryDTO queryDTO);
/**
* 查询员工详情(包含部门名称)
*
* @param staffId 员工ID
* @return 员工详情
*/
CcdiBaseStaffVO selectBaseStaffByIdWithDept(@Param("staffId") Long staffId);
int insertOrUpdateBatch(@Param("list") List<CcdiBaseStaff> list); int insertOrUpdateBatch(@Param("list") List<CcdiBaseStaff> list);
/** /**

View File

@@ -35,6 +35,14 @@ public interface CcdiPurchaseTransactionMapper extends BaseMapper<CcdiPurchaseTr
*/ */
CcdiPurchaseTransactionVO selectTransactionById(@Param("purchaseId") String purchaseId); CcdiPurchaseTransactionVO selectTransactionById(@Param("purchaseId") String purchaseId);
/**
* 删除指定采购事项ID对应的供应商明细
*
* @param purchaseIds 采购事项ID列表
* @return 删除行数
*/
int deleteSuppliersByPurchaseIds(@Param("purchaseIds") List<String> purchaseIds);
/** /**
* 批量插入采购交易数据 * 批量插入采购交易数据
* *

View File

@@ -0,0 +1,12 @@
package com.ruoyi.info.collection.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.info.collection.domain.CcdiPurchaseTransactionSupplier;
import org.apache.ibatis.annotations.Mapper;
/**
* 招投标供应商明细Mapper
*/
@Mapper
public interface CcdiPurchaseTransactionSupplierMapper extends BaseMapper<CcdiPurchaseTransactionSupplier> {
}

View File

@@ -4,6 +4,7 @@ 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.info.collection.domain.CcdiStaffEnterpriseRelation; import com.ruoyi.info.collection.domain.CcdiStaffEnterpriseRelation;
import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO;
import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationOptionVO;
import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -38,6 +39,14 @@ public interface CcdiStaffEnterpriseRelationMapper extends BaseMapper<CcdiStaffE
*/ */
CcdiStaffEnterpriseRelationVO selectRelationById(@Param("id") Long id); CcdiStaffEnterpriseRelationVO selectRelationById(@Param("id") Long id);
/**
* 查询有效员工亲属下拉选项
*
* @param query 搜索关键词
* @return 下拉选项
*/
List<CcdiStaffEnterpriseRelationOptionVO> selectFamilyOptions(@Param("query") String query);
/** /**
* 判断身份证号和统一社会信用代码的组合是否已存在 * 判断身份证号和统一社会信用代码的组合是否已存在
* *
@@ -57,6 +66,14 @@ public interface CcdiStaffEnterpriseRelationMapper extends BaseMapper<CcdiStaffE
*/ */
Set<String> batchExistsByCombinations(@Param("combinations") List<String> combinations); Set<String> batchExistsByCombinations(@Param("combinations") List<String> combinations);
/**
* 根据亲属身份证号批量置无效
*
* @param personId 亲属身份证号
* @return 影响行数
*/
int invalidateByFamilyCertNo(@Param("personId") String personId);
/** /**
* 批量插入员工实体关系数据 * 批量插入员工实体关系数据
* *

View File

@@ -30,10 +30,10 @@ public interface CcdiStaffRecruitmentMapper extends BaseMapper<CcdiStaffRecruitm
/** /**
* 查询招聘信息详情 * 查询招聘信息详情
* *
* @param recruitId 招聘项目编号 * @param id 主键ID
* @return 招聘信息VO * @return 招聘信息VO
*/ */
CcdiStaffRecruitmentVO selectRecruitmentById(@Param("recruitId") String recruitId); CcdiStaffRecruitmentVO selectRecruitmentById(@Param("id") Long id);
/** /**
* 批量插入招聘信息数据 * 批量插入招聘信息数据

View File

@@ -5,6 +5,8 @@ import com.ruoyi.info.collection.domain.vo.AssetImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 亲属资产信息异步导入 服务层 * 亲属资产信息异步导入 服务层
@@ -31,6 +33,19 @@ public interface ICcdiAssetInfoImportService {
*/ */
void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName); void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName);
/**
* 同步执行亲属资产导入可附加同一文件亲属关系Sheet成功导入的归属映射
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
* @param extraOwnerMappings 附加归属映射key为亲属证件号value为归属员工证件号集合
*/
void importAssetInfoSync(List<CcdiAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings);
/** /**
* 查询导入状态 * 查询导入状态
* *

View File

@@ -5,6 +5,8 @@ import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 员工资产信息异步导入 服务层 * 员工资产信息异步导入 服务层
@@ -31,6 +33,19 @@ public interface ICcdiBaseStaffAssetImportService {
*/ */
void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName); void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName);
/**
* 同步执行员工资产导入可附加同一文件员工Sheet成功导入的归属映射
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
* @param extraOwnerMappings 附加归属映射key为资产持有人证件号value为归属员工证件号集合
*/
void importAssetInfoSync(List<CcdiBaseStaffAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings);
/** /**
* 查询导入状态 * 查询导入状态
* *

View File

@@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.vo.ImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* @Author: wkc * @Author: wkc
@@ -15,10 +16,18 @@ public interface ICcdiBaseStaffImportService {
/** /**
* 异步导入员工数据 * 异步导入员工数据
* *
* @param excelList Excel数据列表 * @param excelList Excel数据列表
* @param isUpdateSupport 是否更新已存在的数据
*/ */
void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport, String taskId); void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, String taskId);
/**
* 同步执行员工导入并返回本轮成功员工身份证号
*
* @param excelList Excel数据列表
* @param taskId 任务ID
* @return 成功导入的身份证号集合
*/
Set<String> importBaseStaffSync(List<CcdiBaseStaffExcel> excelList, String taskId);
/** /**
* 查询导入状态 * 查询导入状态

View File

@@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.vo.BaseStaffImportSubmitResultVO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO;
@@ -78,11 +80,20 @@ public interface ICcdiBaseStaffService {
/** /**
* 导入员工数据 * 导入员工数据
* *
* @param excelList Excel实体列表 * @param excelList Excel实体列表
* @param isUpdateSupport 是否更新支持
* @return 结果 * @return 结果
*/ */
String importBaseStaff(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport); String importBaseStaff(List<CcdiBaseStaffExcel> excelList);
/**
* 导入员工信息和员工资产双Sheet数据
*
* @param staffList 员工信息Sheet
* @param assetList 员工资产Sheet
* @return 提交结果
*/
BaseStaffImportSubmitResultVO importBaseStaffWithAssets(List<CcdiBaseStaffExcel> staffList,
List<CcdiBaseStaffAssetInfoExcel> assetList);
/** /**
* 查询员工下拉列表 * 查询员工下拉列表

View File

@@ -1,6 +1,7 @@
package com.ruoyi.info.collection.service; package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel; import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExcel;
import com.ruoyi.info.collection.domain.vo.PurchaseTransactionImportFailureVO; import com.ruoyi.info.collection.domain.vo.PurchaseTransactionImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
@@ -17,11 +18,17 @@ public interface ICcdiPurchaseTransactionImportService {
/** /**
* 异步导入采购交易数据 * 异步导入采购交易数据
* *
* @param excelList Excel数据列表 * @param mainExcelList 主信息Excel数据列表
* @param taskId 任务ID * @param supplierExcelList 供应商明细Excel数据列表
* @param userName 当前用户名 * @param taskId 任务ID
* @param userName 当前用户名
*/ */
void importTransactionAsync(List<CcdiPurchaseTransactionExcel> excelList, String taskId, String userName); void importTransactionAsync(
List<CcdiPurchaseTransactionExcel> mainExcelList,
List<CcdiPurchaseTransactionSupplierExcel> supplierExcelList,
String taskId,
String userName
);
/** /**
* 查询导入状态 * 查询导入状态

View File

@@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel; import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExcel;
import com.ruoyi.info.collection.domain.vo.CcdiPurchaseTransactionVO; import com.ruoyi.info.collection.domain.vo.CcdiPurchaseTransactionVO;
import java.util.List; import java.util.List;
@@ -77,8 +78,12 @@ public interface ICcdiPurchaseTransactionService {
/** /**
* 导入采购交易数据(异步) * 导入采购交易数据(异步)
* *
* @param excelList Excel实体列表 * @param mainExcelList 主信息Excel实体列表
* @param supplierExcelList 供应商明细Excel实体列表
* @return 任务ID * @return 任务ID
*/ */
String importTransaction(List<CcdiPurchaseTransactionExcel> excelList); String importTransaction(
List<CcdiPurchaseTransactionExcel> mainExcelList,
List<CcdiPurchaseTransactionSupplierExcel> supplierExcelList
);
} }

View File

@@ -7,7 +7,7 @@ import com.ruoyi.info.collection.domain.vo.StaffEnterpriseRelationImportFailureV
import java.util.List; import java.util.List;
/** /**
* 员工实体关系信息异步导入服务层 * 员工亲属实体关异步导入服务层
* *
* @author ruoyi * @author ruoyi
* @date 2026-02-09 * @date 2026-02-09

View File

@@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffEnterpriseRelationQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationOptionVO;
import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffEnterpriseRelationVO;
import java.util.List; import java.util.List;
@@ -42,6 +43,14 @@ public interface ICcdiStaffEnterpriseRelationService {
*/ */
List<CcdiStaffEnterpriseRelationExcel> selectRelationListForExport(CcdiStaffEnterpriseRelationQueryDTO queryDTO); List<CcdiStaffEnterpriseRelationExcel> selectRelationListForExport(CcdiStaffEnterpriseRelationQueryDTO queryDTO);
/**
* 查询有效员工亲属下拉选项
*
* @param query 搜索关键词
* @return 下拉选项
*/
List<CcdiStaffEnterpriseRelationOptionVO> selectFamilyOptions(String query);
/** /**
* 查询员工实体关系详情 * 查询员工实体关系详情
* *

View File

@@ -5,6 +5,7 @@ import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO; import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 员工亲属关系异步导入 服务层 * 员工亲属关系异步导入 服务层
@@ -23,6 +24,16 @@ public interface ICcdiStaffFmyRelationImportService {
*/ */
void importRelationAsync(List<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName); void importRelationAsync(List<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName);
/**
* 同步执行员工亲属关系导入并返回本轮成功关系映射
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
* @return key为亲属证件号value为归属员工证件号
*/
Map<String, String> importRelationSync(List<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName);
/** /**
* 查询导入失败记录 * 查询导入失败记录
* *

View File

@@ -4,8 +4,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel; import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO;
import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportSubmitResultVO;
import java.util.List; import java.util.List;
@@ -81,4 +83,14 @@ public interface ICcdiStaffFmyRelationService {
* @return 任务ID * @return 任务ID
*/ */
String importRelation(List<CcdiStaffFmyRelationExcel> excelList); String importRelation(List<CcdiStaffFmyRelationExcel> excelList);
/**
* 导入员工亲属关系和亲属资产双Sheet数据
*
* @param relationList 员工亲属关系Sheet
* @param assetList 亲属资产Sheet
* @return 提交结果
*/
StaffFmyRelationImportSubmitResultVO importRelationWithAssets(List<CcdiStaffFmyRelationExcel> relationList,
List<CcdiAssetInfoExcel> assetList);
} }

View File

@@ -22,21 +22,11 @@ public interface ICcdiStaffRecruitmentImportService {
* @param taskId 任务ID * @param taskId 任务ID
* @param userName 用户名 * @param userName 用户名
*/ */
void importRecruitmentAsync(List<CcdiStaffRecruitmentExcel> excelList, void importRecruitmentAsync(List<CcdiStaffRecruitmentExcel> recruitmentList,
List<CcdiStaffRecruitmentWorkExcel> workList,
String taskId, String taskId,
String userName); String userName);
/**
* 异步导入招聘记录历史工作经历数据
*
* @param excelList Excel数据列表
* @param taskId 任务ID
* @param userName 用户名
*/
void importRecruitmentWorkAsync(List<CcdiStaffRecruitmentWorkExcel> excelList,
String taskId,
String userName);
/** /**
* 查询导入状态 * 查询导入状态
* *

View File

@@ -46,10 +46,10 @@ public interface ICcdiStaffRecruitmentService {
/** /**
* 查询招聘信息详情 * 查询招聘信息详情
* *
* @param recruitId 招聘项目编号 * @param id 主键ID
* @return 招聘信息VO * @return 招聘信息VO
*/ */
CcdiStaffRecruitmentVO selectRecruitmentById(String recruitId); CcdiStaffRecruitmentVO selectRecruitmentById(Long id);
/** /**
* 新增招聘信息 * 新增招聘信息
@@ -70,10 +70,10 @@ public interface ICcdiStaffRecruitmentService {
/** /**
* 批量删除招聘信息 * 批量删除招聘信息
* *
* @param recruitIds 需要删除的招聘项目编号 * @param ids 需要删除的招聘信息ID
* @return 结果 * @return 结果
*/ */
int deleteRecruitmentByIds(String[] recruitIds); int deleteRecruitmentByIds(Long[] ids);
/** /**
* 导入招聘信息数据 * 导入招聘信息数据
@@ -81,13 +81,6 @@ public interface ICcdiStaffRecruitmentService {
* @param excelList Excel实体列表 * @param excelList Excel实体列表
* @return 结果 * @return 结果
*/ */
String importRecruitment(List<CcdiStaffRecruitmentExcel> excelList); String importRecruitment(List<CcdiStaffRecruitmentExcel> recruitmentList,
List<CcdiStaffRecruitmentWorkExcel> workList);
/**
* 导入招聘记录历史工作经历数据(异步)
*
* @param excelList Excel实体列表
* @return 任务ID
*/
String importRecruitmentWork(List<CcdiStaffRecruitmentWorkExcel> excelList);
} }

View File

@@ -49,6 +49,10 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
private static final Set<String> ACCOUNT_TYPES = Set.of("BANK", "SECURITIES", "PAYMENT", "OTHER"); private static final Set<String> ACCOUNT_TYPES = Set.of("BANK", "SECURITIES", "PAYMENT", "OTHER");
private static final Set<String> BANK_SCOPES = Set.of("INTERNAL", "EXTERNAL"); private static final Set<String> BANK_SCOPES = Set.of("INTERNAL", "EXTERNAL");
private static final Set<String> LEVELS = Set.of("LOW", "MEDIUM", "HIGH"); private static final Set<String> LEVELS = Set.of("LOW", "MEDIUM", "HIGH");
private static final String OWNER_TYPE_INTERMEDIARY = "INTERMEDIARY";
private static final String BANK_SCOPE_EXTERNAL = "EXTERNAL";
private static final String RISK_LEVEL_LOW = "LOW";
private static final String RISK_LEVEL_HIGH = "HIGH";
private final List<AccountInfoImportFailureVO> latestImportFailures = new CopyOnWriteArrayList<>(); private final List<AccountInfoImportFailureVO> latestImportFailures = new CopyOnWriteArrayList<>();
@Resource @Resource
@@ -195,12 +199,12 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
private void validateOwner(String ownerType, String ownerId) { private void validateOwner(String ownerType, String ownerId) {
if (StringUtils.isEmpty(ownerId)) { if (StringUtils.isEmpty(ownerId)) {
if ("EXTERNAL".equals(ownerType) || "INTERMEDIARY".equals(ownerType)) { if ("EXTERNAL".equals(ownerType) || OWNER_TYPE_INTERMEDIARY.equals(ownerType)) {
throw new RuntimeException("证件号不能为空"); throw new RuntimeException("证件号不能为空");
} }
throw new RuntimeException("所属人不能为空"); throw new RuntimeException("所属人不能为空");
} }
if ("EXTERNAL".equals(ownerType) || "INTERMEDIARY".equals(ownerType)) { if ("EXTERNAL".equals(ownerType) || OWNER_TYPE_INTERMEDIARY.equals(ownerType)) {
return; return;
} }
if ("EMPLOYEE".equals(ownerType)) { if ("EMPLOYEE".equals(ownerType)) {
@@ -232,8 +236,9 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
} }
private void prepareAnalysisFields(CcdiAccountInfo accountInfo) { private void prepareAnalysisFields(CcdiAccountInfo accountInfo) {
if (!"EXTERNAL".equals(accountInfo.getBankScope())) { if (!BANK_SCOPE_EXTERNAL.equals(accountInfo.getBankScope())) {
clearAnalysisFields(accountInfo); clearAnalysisFields(accountInfo);
applyIntermediaryRiskLevel(accountInfo);
return; return;
} }
if (accountInfo.getIsActualControl() == null) { if (accountInfo.getIsActualControl() == null) {
@@ -249,8 +254,9 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
accountInfo.setTxnFrequencyLevel("MEDIUM"); accountInfo.setTxnFrequencyLevel("MEDIUM");
} }
if (StringUtils.isEmpty(accountInfo.getTxnRiskLevel())) { if (StringUtils.isEmpty(accountInfo.getTxnRiskLevel())) {
accountInfo.setTxnRiskLevel("LOW"); accountInfo.setTxnRiskLevel(RISK_LEVEL_LOW);
} }
applyIntermediaryRiskLevel(accountInfo);
} }
private void clearAnalysisFields(CcdiAccountInfo accountInfo) { private void clearAnalysisFields(CcdiAccountInfo accountInfo) {
@@ -265,6 +271,12 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
accountInfo.setTxnRiskLevel(null); accountInfo.setTxnRiskLevel(null);
} }
private void applyIntermediaryRiskLevel(CcdiAccountInfo accountInfo) {
if (OWNER_TYPE_INTERMEDIARY.equals(accountInfo.getOwnerType())) {
accountInfo.setTxnRiskLevel(RISK_LEVEL_HIGH);
}
}
private void validateAmount(BigDecimal amount, String fieldLabel) { private void validateAmount(BigDecimal amount, String fieldLabel) {
if (amount == null) { if (amount == null) {
return; return;
@@ -285,6 +297,7 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
addDTO.setTxnFrequencyLevel(toUpper(addDTO.getTxnFrequencyLevel())); addDTO.setTxnFrequencyLevel(toUpper(addDTO.getTxnFrequencyLevel()));
addDTO.setTxnRiskLevel(toUpper(addDTO.getTxnRiskLevel())); addDTO.setTxnRiskLevel(toUpper(addDTO.getTxnRiskLevel()));
addDTO.setOwnerId(normalizeOwnerId(addDTO.getOwnerId())); addDTO.setOwnerId(normalizeOwnerId(addDTO.getOwnerId()));
applyIntermediaryRiskLevel(addDTO);
} }
private void normalizeEditDto(CcdiAccountInfoEditDTO editDTO) { private void normalizeEditDto(CcdiAccountInfoEditDTO editDTO) {
@@ -295,6 +308,19 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
editDTO.setTxnFrequencyLevel(toUpper(editDTO.getTxnFrequencyLevel())); editDTO.setTxnFrequencyLevel(toUpper(editDTO.getTxnFrequencyLevel()));
editDTO.setTxnRiskLevel(toUpper(editDTO.getTxnRiskLevel())); editDTO.setTxnRiskLevel(toUpper(editDTO.getTxnRiskLevel()));
editDTO.setOwnerId(normalizeOwnerId(editDTO.getOwnerId())); editDTO.setOwnerId(normalizeOwnerId(editDTO.getOwnerId()));
applyIntermediaryRiskLevel(editDTO);
}
private void applyIntermediaryRiskLevel(CcdiAccountInfoAddDTO addDTO) {
if (OWNER_TYPE_INTERMEDIARY.equals(addDTO.getOwnerType())) {
addDTO.setTxnRiskLevel(RISK_LEVEL_HIGH);
}
}
private void applyIntermediaryRiskLevel(CcdiAccountInfoEditDTO editDTO) {
if (OWNER_TYPE_INTERMEDIARY.equals(editDTO.getOwnerType())) {
editDTO.setTxnRiskLevel(RISK_LEVEL_HIGH);
}
} }
private String normalizeCurrency(String currency) { private String normalizeCurrency(String currency) {
@@ -362,7 +388,8 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
private CcdiAccountInfoAddDTO toAddDto(CcdiAccountInfoExcel excel) { private CcdiAccountInfoAddDTO toAddDto(CcdiAccountInfoExcel excel) {
CcdiAccountInfoAddDTO dto = new CcdiAccountInfoAddDTO(); CcdiAccountInfoAddDTO dto = new CcdiAccountInfoAddDTO();
dto.setOwnerType(parseOwnerType(excel.getOwnerType())); String ownerType = parseOwnerType(excel.getOwnerType());
dto.setOwnerType(ownerType);
dto.setOwnerId(normalizeOwnerId(excel.getOwnerId())); dto.setOwnerId(normalizeOwnerId(excel.getOwnerId()));
dto.setAccountName(trimToNull(excel.getAccountName())); dto.setAccountName(trimToNull(excel.getAccountName()));
dto.setAccountNo(trimToNull(excel.getAccountNo())); dto.setAccountNo(trimToNull(excel.getAccountNo()));
@@ -382,7 +409,9 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
dto.setCreditSingleMaxAmount(parseDecimal(excel.getCreditSingleMaxAmount())); dto.setCreditSingleMaxAmount(parseDecimal(excel.getCreditSingleMaxAmount()));
dto.setDebitDailyMaxAmount(parseDecimal(excel.getDebitDailyMaxAmount())); dto.setDebitDailyMaxAmount(parseDecimal(excel.getDebitDailyMaxAmount()));
dto.setCreditDailyMaxAmount(parseDecimal(excel.getCreditDailyMaxAmount())); dto.setCreditDailyMaxAmount(parseDecimal(excel.getCreditDailyMaxAmount()));
dto.setTxnRiskLevel(parseLevel(excel.getTxnRiskLevel(), "风险等级")); dto.setTxnRiskLevel(OWNER_TYPE_INTERMEDIARY.equals(ownerType)
? RISK_LEVEL_HIGH
: parseLevel(excel.getTxnRiskLevel(), "风险等级"));
return dto; return dto;
} }
@@ -418,7 +447,7 @@ public class CcdiAccountInfoServiceImpl implements ICcdiAccountInfoService {
return "RELATION"; return "RELATION";
} }
if ("中介".equals(value)) { if ("中介".equals(value)) {
return "INTERMEDIARY"; return OWNER_TYPE_INTERMEDIARY;
} }
if ("外部人员".equals(value)) { if ("外部人员".equals(value)) {
return "EXTERNAL"; return "EXTERNAL";

View File

@@ -39,6 +39,8 @@ import java.util.concurrent.TimeUnit;
public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportService { public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportService {
private static final String STATUS_KEY_PREFIX = "import:assetInfo:"; private static final String STATUS_KEY_PREFIX = "import:assetInfo:";
private static final String SHEET_NAME = "亲属资产信息";
private static final int EXCEL_DATA_START_ROW = 2;
@Resource @Resource
private CcdiAssetInfoMapper assetInfoMapper; private CcdiAssetInfoMapper assetInfoMapper;
@@ -80,6 +82,15 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
@Async @Async
@Transactional @Transactional
public void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName) { public void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName) {
importAssetInfoSync(excelList, taskId, userName, Map.of());
}
@Override
@Transactional
public void importAssetInfoSync(List<CcdiAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings) {
List<CcdiAssetInfo> successList = new ArrayList<>(); List<CcdiAssetInfo> successList = new ArrayList<>();
List<AssetImportFailureVO> failures = new ArrayList<>(); List<AssetImportFailureVO> failures = new ArrayList<>();
@@ -90,8 +101,10 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
.toList(); .toList();
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds); Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
mergeOwnerMappings(ownerMap, extraOwnerMappings);
for (CcdiAssetInfoExcel excel : excelList) { for (int i = 0; i < excelList.size(); i++) {
CcdiAssetInfoExcel excel = excelList.get(i);
try { try {
validateExcel(excel); validateExcel(excel);
Set<String> familyIds = ownerMap.get(excel.getPersonId()); Set<String> familyIds = ownerMap.get(excel.getPersonId());
@@ -111,6 +124,8 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
} catch (Exception e) { } catch (Exception e) {
AssetImportFailureVO failureVO = new AssetImportFailureVO(); AssetImportFailureVO failureVO = new AssetImportFailureVO();
BeanUtils.copyProperties(excel, failureVO); BeanUtils.copyProperties(excel, failureVO);
failureVO.setSheetName(SHEET_NAME);
failureVO.setRowNum(i + EXCEL_DATA_START_ROW);
failureVO.setErrorMessage(e.getMessage()); failureVO.setErrorMessage(e.getMessage());
failures.add(failureVO); failures.add(failureVO);
} }
@@ -184,6 +199,18 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
} }
} }
private void mergeOwnerMappings(Map<String, Set<String>> result, Map<String, Set<String>> mappings) {
if (mappings == null || mappings.isEmpty()) {
return;
}
for (Map.Entry<String, Set<String>> entry : mappings.entrySet()) {
if (StringUtils.isEmpty(entry.getKey()) || entry.getValue() == null || entry.getValue().isEmpty()) {
continue;
}
result.computeIfAbsent(entry.getKey(), key -> new java.util.LinkedHashSet<>()).addAll(entry.getValue());
}
}
private void validateExcel(CcdiAssetInfoExcel excel) { private void validateExcel(CcdiAssetInfoExcel excel) {
if (StringUtils.isEmpty(excel.getPersonId())) { if (StringUtils.isEmpty(excel.getPersonId())) {
throw new RuntimeException("亲属证件号不能为空"); throw new RuntimeException("亲属证件号不能为空");

View File

@@ -1,6 +1,7 @@
package com.ruoyi.info.collection.service.impl; package com.ruoyi.info.collection.service.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.info.collection.domain.CcdiAssetInfo; import com.ruoyi.info.collection.domain.CcdiAssetInfo;
@@ -80,6 +81,15 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
@Async @Async
@Transactional @Transactional
public void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName) { public void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName) {
importAssetInfoSync(excelList, taskId, userName, Map.of());
}
@Override
@Transactional
public void importAssetInfoSync(List<CcdiBaseStaffAssetInfoExcel> excelList,
String taskId,
String userName,
Map<String, Set<String>> extraOwnerMappings) {
List<CcdiAssetInfo> successList = new ArrayList<>(); List<CcdiAssetInfo> successList = new ArrayList<>();
List<BaseStaffAssetImportFailureVO> failures = new ArrayList<>(); List<BaseStaffAssetImportFailureVO> failures = new ArrayList<>();
@@ -90,14 +100,25 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
.toList(); .toList();
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds); Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
mergeOwnerMappings(ownerMap, extraOwnerMappings);
Set<String> existingAssetKeys = buildExistingAssetKeys(personIds);
Set<String> importedAssetKeys = new java.util.LinkedHashSet<>();
for (CcdiBaseStaffAssetInfoExcel excel : excelList) { for (int i = 0; i < excelList.size(); i++) {
CcdiBaseStaffAssetInfoExcel excel = excelList.get(i);
try { try {
validateExcel(excel); validateExcel(excel);
Set<String> familyIds = ownerMap.get(excel.getPersonId()); Set<String> familyIds = ownerMap.get(excel.getPersonId());
if (familyIds == null || familyIds.isEmpty()) { if (familyIds == null || familyIds.isEmpty()) {
throw new RuntimeException("员工资产导入仅支持员工本人证件号"); throw new RuntimeException("员工资产导入仅支持员工本人证件号");
} }
String assetKey = buildAssetKey(excel.getPersonId(), excel.getAssetMainType(), excel.getAssetSubType(), excel.getAssetName());
if (existingAssetKeys.contains(assetKey)) {
throw new RuntimeException("资产记录已存在");
}
if (!importedAssetKeys.add(assetKey)) {
throw new RuntimeException("资产记录在导入文件中重复");
}
CcdiAssetInfo assetInfo = new CcdiAssetInfo(); CcdiAssetInfo assetInfo = new CcdiAssetInfo();
BeanUtils.copyProperties(excel, assetInfo); BeanUtils.copyProperties(excel, assetInfo);
@@ -109,6 +130,8 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
} catch (Exception e) { } catch (Exception e) {
BaseStaffAssetImportFailureVO failureVO = new BaseStaffAssetImportFailureVO(); BaseStaffAssetImportFailureVO failureVO = new BaseStaffAssetImportFailureVO();
BeanUtils.copyProperties(excel, failureVO); BeanUtils.copyProperties(excel, failureVO);
failureVO.setSheetName("员工资产信息");
failureVO.setRowNum(i + 2);
failureVO.setErrorMessage(e.getMessage()); failureVO.setErrorMessage(e.getMessage());
failures.add(failureVO); failures.add(failureVO);
} }
@@ -168,6 +191,18 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
return result; return result;
} }
private Set<String> buildExistingAssetKeys(List<String> personIds) {
if (personIds == null || personIds.isEmpty()) {
return Set.of();
}
LambdaQueryWrapper<CcdiAssetInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CcdiAssetInfo::getPersonId, personIds);
return assetInfoMapper.selectList(wrapper).stream()
.filter(asset -> StringUtils.equals(asset.getFamilyId(), asset.getPersonId()))
.map(asset -> buildAssetKey(asset.getPersonId(), asset.getAssetMainType(), asset.getAssetSubType(), asset.getAssetName()))
.collect(java.util.stream.Collectors.toCollection(java.util.LinkedHashSet::new));
}
private void mergeOwnerMappings(Map<String, Set<String>> result, List<Map<String, String>> mappings) { private void mergeOwnerMappings(Map<String, Set<String>> result, List<Map<String, String>> mappings) {
if (mappings == null) { if (mappings == null) {
return; return;
@@ -182,6 +217,18 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
} }
} }
private void mergeOwnerMappings(Map<String, Set<String>> result, Map<String, Set<String>> mappings) {
if (mappings == null || mappings.isEmpty()) {
return;
}
for (Map.Entry<String, Set<String>> entry : mappings.entrySet()) {
if (StringUtils.isEmpty(entry.getKey()) || entry.getValue() == null || entry.getValue().isEmpty()) {
continue;
}
result.computeIfAbsent(entry.getKey(), key -> new java.util.LinkedHashSet<>()).addAll(entry.getValue());
}
}
private void validateExcel(CcdiBaseStaffAssetInfoExcel excel) { private void validateExcel(CcdiBaseStaffAssetInfoExcel excel) {
if (StringUtils.isEmpty(excel.getPersonId())) { if (StringUtils.isEmpty(excel.getPersonId())) {
throw new RuntimeException("员工身份证号不能为空"); throw new RuntimeException("员工身份证号不能为空");
@@ -203,6 +250,14 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
} }
} }
private String buildAssetKey(String personId, String assetMainType, String assetSubType, String assetName) {
return String.join("|",
StringUtils.nvl(personId, ""),
StringUtils.nvl(assetMainType, ""),
StringUtils.nvl(assetSubType, ""),
StringUtils.nvl(assetName, ""));
}
private void updateImportStatus(String taskId, String status, ImportResult result) { private void updateImportStatus(String taskId, String status, ImportResult result) {
Map<String, Object> statusData = new HashMap<>(); Map<String, Object> statusData = new HashMap<>();
statusData.put("status", status); statusData.put("status", status);

View File

@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.service.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.info.collection.domain.CcdiBaseStaff; import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
@@ -13,6 +14,7 @@ import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.info.collection.utils.ImportLogUtils;
import com.ruoyi.common.utils.IdCardUtil; import com.ruoyi.common.utils.IdCardUtil;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.SysDeptMapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -21,6 +23,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
@@ -43,16 +46,24 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Resource
private SysDeptMapper deptMapper;
@Override @Override
@Async @Async
public void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport, String taskId) { public void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, String taskId) {
importBaseStaffSync(excelList, taskId);
}
@Override
@Transactional
public Set<String> importBaseStaffSync(List<CcdiBaseStaffExcel> excelList, String taskId) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 记录导入开始 // 记录导入开始
ImportLogUtils.logImportStart(log, taskId, "员工基础信息", excelList.size(), "系统"); ImportLogUtils.logImportStart(log, taskId, "员工基础信息", excelList.size(), "系统");
List<CcdiBaseStaff> newRecords = new ArrayList<>(); List<CcdiBaseStaff> newRecords = new ArrayList<>();
List<CcdiBaseStaff> updateRecords = new ArrayList<>();
List<ImportFailureVO> failures = new ArrayList<>(); List<ImportFailureVO> failures = new ArrayList<>();
// 批量查询已存在的员工ID和身份证号 // 批量查询已存在的员工ID和身份证号
@@ -75,13 +86,12 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
CcdiBaseStaffAddDTO addDTO = new CcdiBaseStaffAddDTO(); CcdiBaseStaffAddDTO addDTO = new CcdiBaseStaffAddDTO();
BeanUtils.copyProperties(excel, addDTO); BeanUtils.copyProperties(excel, addDTO);
// 验证数据(支持更新模式) validateStaffData(addDTO, existingIds, existingIdCards);
validateStaffData(addDTO, isUpdateSupport, existingIds, existingIdCards);
CcdiBaseStaff staff = new CcdiBaseStaff(); CcdiBaseStaff staff = new CcdiBaseStaff();
BeanUtils.copyProperties(excel, staff); BeanUtils.copyProperties(excel, staff);
// 统一检查Excel内重复(更新和新增两个分支都需要检查) // 统一检查Excel内重复
if (processedStaffIds.contains(excel.getStaffId())) { if (processedStaffIds.contains(excel.getStaffId())) {
throw new RuntimeException(String.format("员工ID[%d]在导入文件中重复,已跳过此条记录", excel.getStaffId())); throw new RuntimeException(String.format("员工ID[%d]在导入文件中重复,已跳过此条记录", excel.getStaffId()));
} }
@@ -90,20 +100,7 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard())); throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
} }
// 检查员工ID是否在数据库中已存在 newRecords.add(staff);
if (existingIds.contains(excel.getStaffId())) {
// 员工ID已存在于数据库
if (!isUpdateSupport) {
throw new RuntimeException("员工ID已存在且未启用更新支持");
}
// 通过检查,添加到更新列表
updateRecords.add(staff);
} else {
// 员工ID不存在,添加到新增列表
newRecords.add(staff);
}
// 统一标记为已处理(只有成功添加到列表后才会执行到这里) // 统一标记为已处理(只有成功添加到列表后才会执行到这里)
if (excel.getStaffId() != null) { if (excel.getStaffId() != null) {
@@ -115,11 +112,13 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
// 记录进度 // 记录进度
ImportLogUtils.logProgress(log, taskId, i + 1, excelList.size(), ImportLogUtils.logProgress(log, taskId, i + 1, excelList.size(),
newRecords.size() + updateRecords.size(), failures.size()); newRecords.size(), failures.size());
} catch (Exception e) { } catch (Exception e) {
ImportFailureVO failure = new ImportFailureVO(); ImportFailureVO failure = new ImportFailureVO();
BeanUtils.copyProperties(excel, failure); BeanUtils.copyProperties(excel, failure);
failure.setSheetName("员工信息");
failure.setRowNum(i + 2);
failure.setErrorMessage(e.getMessage()); failure.setErrorMessage(e.getMessage());
failures.add(failure); failures.add(failure);
@@ -137,13 +136,6 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
saveBatch(newRecords, 500); saveBatch(newRecords, 500);
} }
// 批量更新已有数据(先删除再插入)
if (!updateRecords.isEmpty() && isUpdateSupport) {
ImportLogUtils.logBatchOperationStart(log, taskId, "更新",
(updateRecords.size() + 499) / 500, 500);
baseStaffMapper.insertOrUpdateBatch(updateRecords);
}
// 保存失败记录到Redis // 保存失败记录到Redis
if (!failures.isEmpty()) { if (!failures.isEmpty()) {
try { try {
@@ -157,7 +149,7 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
ImportResult result = new ImportResult(); ImportResult result = new ImportResult();
result.setTotalCount(excelList.size()); result.setTotalCount(excelList.size());
result.setSuccessCount(newRecords.size() + updateRecords.size()); result.setSuccessCount(newRecords.size());
result.setFailureCount(failures.size()); result.setFailureCount(failures.size());
// 更新最终状态 // 更新最终状态
@@ -168,6 +160,11 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
long duration = System.currentTimeMillis() - startTime; long duration = System.currentTimeMillis() - startTime;
ImportLogUtils.logImportComplete(log, taskId, "员工基础信息", ImportLogUtils.logImportComplete(log, taskId, "员工基础信息",
excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration); excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration);
return newRecords.stream()
.map(CcdiBaseStaff::getIdCard)
.filter(StringUtils::isNotEmpty)
.collect(Collectors.toCollection(LinkedHashSet::new));
} }
/** /**
@@ -299,11 +296,10 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
* 验证员工数据 * 验证员工数据
* *
* @param addDTO 新增DTO * @param addDTO 新增DTO
* @param isUpdateSupport 是否支持更新
* @param existingIds 已存在的员工ID集合(导入场景使用,传null表示单条新增) * @param existingIds 已存在的员工ID集合(导入场景使用,传null表示单条新增)
* @param existingIdCards 已存在的身份证号集合(导入场景使用,传null表示单条新增) * @param existingIdCards 已存在的身份证号集合(导入场景使用,传null表示单条新增)
*/ */
public void validateStaffData(CcdiBaseStaffAddDTO addDTO, Boolean isUpdateSupport, Set<Long> existingIds, Set<String> existingIdCards) { public void validateStaffData(CcdiBaseStaffAddDTO addDTO, Set<Long> existingIds, Set<String> existingIdCards) {
// 验证必填字段 // 验证必填字段
if (StringUtils.isEmpty(addDTO.getName())) { if (StringUtils.isEmpty(addDTO.getName())) {
throw new RuntimeException("姓名不能为空"); throw new RuntimeException("姓名不能为空");
@@ -326,6 +322,7 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
if (StringUtils.isEmpty(addDTO.getStatus())) { if (StringUtils.isEmpty(addDTO.getStatus())) {
throw new RuntimeException("状态不能为空"); throw new RuntimeException("状态不能为空");
} }
validateDeptId(addDTO.getDeptId());
// 验证身份证号格式 // 验证身份证号格式
String idCardError = IdCardUtil.getErrorMessage(addDTO.getIdCard()); String idCardError = IdCardUtil.getErrorMessage(addDTO.getIdCard());
@@ -347,12 +344,11 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
throw new RuntimeException("该身份证号已存在"); throw new RuntimeException("该身份证号已存在");
} }
} else { } else {
// 导入场景:如果员工ID不存在,才检查身份证号唯一性 if (existingIds.contains(addDTO.getStaffId())) {
if (!existingIds.contains(addDTO.getStaffId())) { throw new RuntimeException("该员工ID已存在");
// 使用批量查询的结果检查身份证号唯一性 }
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) { if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
throw new RuntimeException("该身份证号已存在"); throw new RuntimeException("该身份证号已存在");
}
} }
} }
@@ -378,4 +374,11 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
throw new RuntimeException(fieldLabel + "最多保留2位小数"); throw new RuntimeException(fieldLabel + "最多保留2位小数");
} }
} }
private void validateDeptId(Long deptId) {
SysDept dept = deptMapper.selectDeptById(deptId);
if (dept == null || !"0".equals(dept.getStatus()) || !"0".equals(dept.getDelFlag())) {
throw new RuntimeException(String.format("所属部门ID[%d]不存在或已停用/删除,请检查机构号", deptId));
}
}
} }

View File

@@ -6,15 +6,19 @@ import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel; import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.vo.BaseStaffImportSubmitResultVO;
import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO; import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO; import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO;
import com.ruoyi.info.collection.enums.EmployeeStatus; import com.ruoyi.info.collection.enums.EmployeeStatus;
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper; import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
import com.ruoyi.info.collection.service.ICcdiAssetInfoService; import com.ruoyi.info.collection.service.ICcdiAssetInfoService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService; import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffService; import com.ruoyi.info.collection.service.ICcdiBaseStaffService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@@ -46,6 +50,12 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
@Resource @Resource
private ICcdiAssetInfoService assetInfoService; private ICcdiAssetInfoService assetInfoService;
@Resource
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
@Resource
private CcdiDualSheetImportOrchestrationService dualSheetImportOrchestrationService;
/** /**
* 查询员工列表 * 查询员工列表
* *
@@ -109,10 +119,10 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
*/ */
@Override @Override
public CcdiBaseStaffVO selectBaseStaffById(Long staffId) { public CcdiBaseStaffVO selectBaseStaffById(Long staffId) {
CcdiBaseStaff staff = baseStaffMapper.selectById(staffId); CcdiBaseStaffVO vo = baseStaffMapper.selectBaseStaffByIdWithDept(staffId);
CcdiBaseStaffVO vo = convertToVO(staff); if (vo != null) {
if (staff != null) { vo.setStatusDesc(EmployeeStatus.getDescByCode(vo.getStatus()));
vo.setAssetInfoList(assetInfoService.selectByFamilyIdAndPersonId(staff.getIdCard(), staff.getIdCard()).stream().map(asset -> { vo.setAssetInfoList(assetInfoService.selectByFamilyIdAndPersonId(vo.getIdCard(), vo.getIdCard()).stream().map(asset -> {
CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO(); CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO();
BeanUtils.copyProperties(asset, assetInfoVO); BeanUtils.copyProperties(asset, assetInfoVO);
return assetInfoVO; return assetInfoVO;
@@ -211,36 +221,59 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
/** /**
* 导入员工数据 * 导入员工数据
* *
* @param excelList Excel实体列表 * @param excelList Excel实体列表
* @param isUpdateSupport 是否更新支持
* @return 结果 * @return 结果
*/ */
@Override @Override
@Transactional @Transactional
public String importBaseStaff(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport) { public String importBaseStaff(List<CcdiBaseStaffExcel> excelList) {
String taskId = UUID.randomUUID().toString(); String taskId = UUID.randomUUID().toString();
long startTime = System.currentTimeMillis(); initializeImportStatus("import:baseStaff:", taskId, excelList.size());
// 初始化Redis状态 importAsyncService.importBaseStaffAsync(excelList, taskId);
String statusKey = "import:baseStaff:" + taskId;
Map<String, Object> statusData = new HashMap<>();
statusData.put("taskId", taskId);
statusData.put("status", "PROCESSING");
statusData.put("totalCount", excelList.size());
statusData.put("successCount", 0);
statusData.put("failureCount", 0);
statusData.put("progress", 0);
statusData.put("startTime", startTime);
statusData.put("message", "正在处理...");
redisTemplate.opsForHash().putAll(statusKey, statusData);
redisTemplate.expire(statusKey, 7, java.util.concurrent.TimeUnit.DAYS);
importAsyncService.importBaseStaffAsync(excelList, isUpdateSupport, taskId);
return taskId; return taskId;
} }
@Override
@Transactional
public BaseStaffImportSubmitResultVO importBaseStaffWithAssets(List<CcdiBaseStaffExcel> staffList,
List<CcdiBaseStaffAssetInfoExcel> assetList) {
boolean hasStaffRows = staffList != null && !staffList.isEmpty();
boolean hasAssetRows = assetList != null && !assetList.isEmpty();
if (!hasStaffRows && !hasAssetRows) {
throw new RuntimeException("至少需要一条数据");
}
BaseStaffImportSubmitResultVO result = new BaseStaffImportSubmitResultVO();
result.setMessage(buildImportSubmitMessage(hasStaffRows, hasAssetRows));
if (hasStaffRows && !hasAssetRows) {
result.setStaffTaskId(importBaseStaff(staffList));
return result;
}
if (!hasStaffRows) {
result.setAssetTaskId(baseStaffAssetImportService.importAssetInfo(assetList));
return result;
}
String staffTaskId = UUID.randomUUID().toString();
String assetTaskId = UUID.randomUUID().toString();
initializeImportStatus("import:baseStaff:", staffTaskId, staffList.size());
initializeImportStatus("import:baseStaffAsset:", assetTaskId, assetList.size());
result.setStaffTaskId(staffTaskId);
result.setAssetTaskId(assetTaskId);
dualSheetImportOrchestrationService.importBaseStaffWithAssetsAsync(
staffList,
staffTaskId,
assetList,
assetTaskId,
currentUserName()
);
return result;
}
/** /**
* 查询员工下拉列表 * 查询员工下拉列表
* 支持按员工ID或姓名模糊搜索只返回在职员工 * 支持按员工ID或姓名模糊搜索只返回在职员工
@@ -253,6 +286,40 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
return baseStaffMapper.selectStaffOptions(query); return baseStaffMapper.selectStaffOptions(query);
} }
private void initializeImportStatus(String keyPrefix, String taskId, int totalCount) {
Map<String, Object> statusData = new HashMap<>();
statusData.put("taskId", taskId);
statusData.put("status", "PROCESSING");
statusData.put("totalCount", totalCount);
statusData.put("successCount", 0);
statusData.put("failureCount", 0);
statusData.put("progress", 0);
statusData.put("startTime", System.currentTimeMillis());
statusData.put("message", "正在处理...");
String statusKey = keyPrefix + taskId;
redisTemplate.opsForHash().putAll(statusKey, statusData);
redisTemplate.expire(statusKey, 7, java.util.concurrent.TimeUnit.DAYS);
}
private String buildImportSubmitMessage(boolean hasStaffRows, boolean hasAssetRows) {
if (hasStaffRows && hasAssetRows) {
return "已提交员工信息和员工资产信息导入任务";
}
if (hasStaffRows) {
return "已提交员工信息导入任务";
}
return "已提交员工资产信息导入任务";
}
private String currentUserName() {
try {
return SecurityUtils.getUsername();
} catch (Exception e) {
return "system";
}
}
/** /**
* 构建查询条件 * 构建查询条件
*/ */

View File

@@ -14,8 +14,10 @@ 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;
@@ -23,8 +25,6 @@ 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,9 +36,16 @@ 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;
@@ -141,37 +148,20 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
} }
} }
private void handleSingleFile(MultipartFile multipartFile, String userName) throws IOException { private void handleSingleFile(MultipartFile multipartFile, String userName) throws Exception {
File tempFile = createTempFile(multipartFile); CreditHtmlStorageService.StoredCreditHtml storedHtml = creditHtmlStorageService.save(multipartFile);
try { CreditParseInvokeResponse response = creditParseClient.parse(storedHtml.remotePath());
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")); String personName = stringValue(header.get("query_cust_name"));
String personName = stringValue(header.get("query_cust_name")); LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time")));
LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time"))); ensurePersonIdPresent(personId);
ensurePersonIdPresent(personId); ensureLatestQueryDate(personId, queryDate);
ensureLatestQueryDate(personId, queryDate);
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) {
@@ -185,14 +175,41 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
} }
} }
private CreditParseResponse requireResponse(CreditParseResponse response) { private CreditParseResponse requireResponse(CreditParseInvokeResponse response) {
if (response == null || response.getPayload() == null) { if (response == null || response.getData() == null || response.getData().getMappingOutputFields() == null) {
throw new RuntimeException("征信解析结果为空"); throw new RuntimeException("征信解析结果为空");
} }
if (!"0".equals(response.getStatusCode())) { CreditParseResponse mappingOutputFields = response.getData().getMappingOutputFields();
throw new RuntimeException(stringValue(response.getMessage(), "征信解析失败")); if (!Boolean.TRUE.equals(response.getSuccess())) {
throw new RuntimeException(stringValue(mappingOutputFields.getMessage(), "征信解析平台调用失败"));
} }
return response; if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_CODE).equals(response.getCode())) {
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) {

View File

@@ -7,8 +7,11 @@ import com.ruoyi.info.collection.domain.excel.CcdiCustEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.CustEnterpriseRelationImportFailureVO; import com.ruoyi.info.collection.domain.vo.CustEnterpriseRelationImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportResult;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.enums.DataSource;
import com.ruoyi.info.collection.enums.EnterpriseSource;
import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper; import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper;
import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationImportService; import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationImportService;
import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService;
import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.info.collection.utils.ImportLogUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -43,6 +46,9 @@ public class CcdiCustEnterpriseRelationImportServiceImpl implements ICcdiCustEnt
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Resource
private EnterpriseAutoFillService enterpriseAutoFillService;
@Override @Override
@Async @Async
@Transactional @Transactional
@@ -127,6 +133,15 @@ public class CcdiCustEnterpriseRelationImportServiceImpl implements ICcdiCustEnt
// 批量插入新数据 // 批量插入新数据
if (!newRecords.isEmpty()) { if (!newRecords.isEmpty()) {
enterpriseAutoFillService.ensureExistsBatch(newRecords.stream()
.map(item -> new EnterpriseAutoFillService.EnterpriseFillItem(
item.getSocialCreditCode(),
item.getEnterpriseName(),
EnterpriseSource.CREDIT_CUSTOMER.getCode(),
DataSource.IMPORT.getCode(),
userName
))
.toList());
ImportLogUtils.logBatchOperationStart(log, taskId, "插入", ImportLogUtils.logBatchOperationStart(log, taskId, "插入",
(newRecords.size() + 499) / 500, 500); (newRecords.size() + 499) / 500, 500);
saveBatch(newRecords, 500); saveBatch(newRecords, 500);

View File

@@ -8,9 +8,12 @@ import com.ruoyi.info.collection.domain.dto.CcdiCustEnterpriseRelationEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiCustEnterpriseRelationQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiCustEnterpriseRelationQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiCustEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.excel.CcdiCustEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.CcdiCustEnterpriseRelationVO; import com.ruoyi.info.collection.domain.vo.CcdiCustEnterpriseRelationVO;
import com.ruoyi.info.collection.enums.DataSource;
import com.ruoyi.info.collection.enums.EnterpriseSource;
import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper; import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper;
import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationImportService; import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationImportService;
import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationService; import com.ruoyi.info.collection.service.ICcdiCustEnterpriseRelationService;
import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -43,6 +46,9 @@ public class CcdiCustEnterpriseRelationServiceImpl implements ICcdiCustEnterpris
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Resource
private EnterpriseAutoFillService enterpriseAutoFillService;
/** /**
* 查询信贷客户实体关联列表 * 查询信贷客户实体关联列表
* *
@@ -135,6 +141,14 @@ public class CcdiCustEnterpriseRelationServiceImpl implements ICcdiCustEnterpris
relation.setDataSource("MANUAL"); relation.setDataSource("MANUAL");
} }
enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem(
addDTO.getSocialCreditCode(),
addDTO.getEnterpriseName(),
EnterpriseSource.CREDIT_CUSTOMER.getCode(),
DataSource.MANUAL.getCode(),
SecurityUtils.getUsername()
));
int result = relationMapper.insert(relation); int result = relationMapper.insert(relation);
return result; return result;

View File

@@ -0,0 +1,90 @@
package com.ruoyi.info.collection.service.impl;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
import com.ruoyi.info.collection.service.ICcdiAssetInfoImportService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService;
import jakarta.annotation.Resource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 双Sheet导入后台顺序编排。
*/
@Service
public class CcdiDualSheetImportOrchestrationService {
@Resource
private ICcdiBaseStaffImportService baseStaffImportService;
@Resource
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
@Resource
private ICcdiStaffFmyRelationImportService relationImportService;
@Resource
private ICcdiAssetInfoImportService assetInfoImportService;
@Async
public void importBaseStaffWithAssetsAsync(List<CcdiBaseStaffExcel> staffList,
String staffTaskId,
List<CcdiBaseStaffAssetInfoExcel> assetList,
String assetTaskId,
String userName) {
Set<String> successIdCards = baseStaffImportService.importBaseStaffSync(staffList, staffTaskId);
baseStaffAssetImportService.importAssetInfoSync(
assetList,
assetTaskId,
userName,
buildSelfOwnerMappings(successIdCards)
);
}
@Async
public void importRelationWithAssetsAsync(List<CcdiStaffFmyRelationExcel> relationList,
String relationTaskId,
List<CcdiAssetInfoExcel> assetList,
String assetTaskId,
String userName) {
Map<String, String> successRelationMappings = relationImportService.importRelationSync(relationList, relationTaskId, userName);
assetInfoImportService.importAssetInfoSync(
assetList,
assetTaskId,
userName,
buildRelationOwnerMappings(successRelationMappings)
);
}
private Map<String, Set<String>> buildSelfOwnerMappings(Set<String> idCards) {
Map<String, Set<String>> result = new LinkedHashMap<>();
if (idCards == null || idCards.isEmpty()) {
return result;
}
for (String idCard : idCards) {
result.computeIfAbsent(idCard, key -> new LinkedHashSet<>()).add(idCard);
}
return result;
}
private Map<String, Set<String>> buildRelationOwnerMappings(Map<String, String> relationMappings) {
Map<String, Set<String>> result = new LinkedHashMap<>();
if (relationMappings == null || relationMappings.isEmpty()) {
return result;
}
for (Map.Entry<String, String> entry : relationMappings.entrySet()) {
result.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>()).add(entry.getValue());
}
return result;
}
}

View File

@@ -131,21 +131,17 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
if (!excel.getSocialCreditCode().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) { if (!excel.getSocialCreditCode().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) {
throw new RuntimeException("统一社会信用代码格式不正确"); throw new RuntimeException("统一社会信用代码格式不正确");
} }
if (StringUtils.isEmpty(excel.getStatus())) {
throw new RuntimeException("经营状态不能为空");
}
String riskLevel = EnterpriseRiskLevel.resolveCode(StringUtils.trim(excel.getRiskLevel()));
if (riskLevel == null) {
throw new RuntimeException("风险等级不在允许范围内");
}
String entSource = EnterpriseSource.resolveCode(StringUtils.trim(excel.getEntSource())); String entSource = EnterpriseSource.resolveCode(StringUtils.trim(excel.getEntSource()));
if (entSource == null) { if (entSource == null) {
throw new RuntimeException("企业来源不在允许范围内"); throw new RuntimeException("企业来源不在允许范围内");
} }
String dataSource = resolveDataSourceCode(StringUtils.trim(excel.getDataSource())); String riskLevel = EnterpriseRiskLevel.resolveCode(StringUtils.trim(excel.getRiskLevel()));
if (dataSource == null) { if (riskLevel == null) {
throw new RuntimeException("数据来源不在允许范围内"); if (EnterpriseSource.INTERMEDIARY.getCode().equals(entSource) && StringUtils.isEmpty(excel.getRiskLevel())) {
riskLevel = "1";
} else {
throw new RuntimeException("风险等级不在允许范围内");
}
} }
if (existingCreditCodes.contains(excel.getSocialCreditCode())) { if (existingCreditCodes.contains(excel.getSocialCreditCode())) {
@@ -159,8 +155,8 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
BeanUtils.copyProperties(excel, entity); BeanUtils.copyProperties(excel, entity);
entity.setRiskLevel(riskLevel); entity.setRiskLevel(riskLevel);
entity.setEntSource(entSource); entity.setEntSource(entSource);
entity.setDataSource(dataSource); entity.setDataSource(DataSource.IMPORT.getCode());
entity.setStatus(StringUtils.trim(excel.getStatus())); entity.setStatus(trimToNull(excel.getStatus()));
entity.setCreatedBy(userName); entity.setCreatedBy(userName);
entity.setUpdatedBy(userName); entity.setUpdatedBy(userName);
return entity; return entity;
@@ -206,15 +202,6 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
redisTemplate.opsForHash().putAll(buildStatusKey(taskId), statusData); redisTemplate.opsForHash().putAll(buildStatusKey(taskId), statusData);
} }
private String resolveDataSourceCode(String value) {
for (DataSource source : DataSource.values()) {
if (source.getCode().equals(value) || source.getDesc().equals(value)) {
return source.getCode();
}
}
return null;
}
private String buildStatusKey(String taskId) { private String buildStatusKey(String taskId) {
return "import:enterpriseBaseInfo:" + taskId; return "import:enterpriseBaseInfo:" + taskId;
} }
@@ -222,4 +209,11 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
private String buildFailuresKey(String taskId) { private String buildFailuresKey(String taskId) {
return "import:enterpriseBaseInfo:" + taskId + ":failures"; return "import:enterpriseBaseInfo:" + taskId + ":failures";
} }
private String trimToNull(String value) {
if (StringUtils.isEmpty(value)) {
return null;
}
return value.trim();
}
} }

View File

@@ -85,10 +85,12 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
if (enterpriseBaseInfoMapper.selectById(addDTO.getSocialCreditCode()) != null) { if (enterpriseBaseInfoMapper.selectById(addDTO.getSocialCreditCode()) != null) {
throw new RuntimeException("该统一社会信用代码已存在"); throw new RuntimeException("该统一社会信用代码已存在");
} }
validateEnumFields(addDTO.getStatus(), addDTO.getRiskLevel(), addDTO.getEntSource(), addDTO.getDataSource()); validateRiskLevelAndEnterpriseSource(addDTO.getRiskLevel(), addDTO.getEntSource());
CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo(); CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
BeanUtils.copyProperties(addDTO, entity); BeanUtils.copyProperties(addDTO, entity);
entity.setStatus(trimToNull(addDTO.getStatus()));
entity.setDataSource(DataSource.MANUAL.getCode());
return enterpriseBaseInfoMapper.insert(entity); return enterpriseBaseInfoMapper.insert(entity);
} }
@@ -103,6 +105,8 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo(); CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
BeanUtils.copyProperties(editDTO, entity); BeanUtils.copyProperties(editDTO, entity);
entity.setStatus(trimToNull(editDTO.getStatus()));
entity.setDataSource(existing.getDataSource());
return enterpriseBaseInfoMapper.updateById(entity); return enterpriseBaseInfoMapper.updateById(entity);
} }
@@ -176,18 +180,22 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
} }
private void validateEnumFields(String status, String riskLevel, String entSource, String dataSource) { private void validateEnumFields(String status, String riskLevel, String entSource, String dataSource) {
if (StringUtils.isEmpty(status)) { validateRiskLevelAndEnterpriseSource(riskLevel, entSource);
throw new RuntimeException("经营状态不能为空"); if (StringUtils.isNotEmpty(status) && StringUtils.trim(status).length() > 50) {
throw new RuntimeException("经营状态长度不能超过50个字符");
} }
if (!containsDataSource(dataSource)) {
throw new RuntimeException("数据来源不在允许范围内");
}
}
private void validateRiskLevelAndEnterpriseSource(String riskLevel, String entSource) {
if (!EnterpriseRiskLevel.contains(riskLevel)) { if (!EnterpriseRiskLevel.contains(riskLevel)) {
throw new RuntimeException("风险等级不在允许范围内"); throw new RuntimeException("风险等级不在允许范围内");
} }
if (!EnterpriseSource.contains(entSource)) { if (!EnterpriseSource.contains(entSource)) {
throw new RuntimeException("企业来源不在允许范围内"); throw new RuntimeException("企业来源不在允许范围内");
} }
if (!containsDataSource(dataSource)) {
throw new RuntimeException("数据来源不在允许范围内");
}
} }
private boolean containsDataSource(String code) { private boolean containsDataSource(String code) {
@@ -199,6 +207,13 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
return false; return false;
} }
private String trimToNull(String value) {
if (StringUtils.isEmpty(value)) {
return null;
}
return value.trim();
}
private void validateDeleteRelations(String socialCreditCode) { private void validateDeleteRelations(String socialCreditCode) {
StringJoiner relationTypes = new StringJoiner(""); StringJoiner relationTypes = new StringJoiner("");
if (staffEnterpriseRelationMapper.selectCount(new LambdaQueryWrapper<CcdiStaffEnterpriseRelation>() if (staffEnterpriseRelationMapper.selectCount(new LambdaQueryWrapper<CcdiStaffEnterpriseRelation>()

View File

@@ -3,16 +3,17 @@ package com.ruoyi.info.collection.service.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.info.collection.domain.CcdiBizIntermediary; import com.ruoyi.info.collection.domain.CcdiBizIntermediary;
import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo;
import com.ruoyi.info.collection.domain.CcdiIntermediaryEnterpriseRelation; import com.ruoyi.info.collection.domain.CcdiIntermediaryEnterpriseRelation;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel; import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportResult;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO; import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO;
import com.ruoyi.info.collection.enums.DataSource;
import com.ruoyi.info.collection.enums.EnterpriseSource;
import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper; import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper;
import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper; import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper;
import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImportService; import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImportService;
import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService;
import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.info.collection.utils.ImportLogUtils;
import com.ruoyi.common.utils.IdCardUtil; import com.ruoyi.common.utils.IdCardUtil;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
@@ -54,10 +55,10 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
private CcdiBizIntermediaryMapper intermediaryMapper; private CcdiBizIntermediaryMapper intermediaryMapper;
@Resource @Resource
private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper; private RedisTemplate<String, Object> redisTemplate;
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private EnterpriseAutoFillService enterpriseAutoFillService;
@Override @Override
@Async @Async
@@ -67,7 +68,6 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
ImportLogUtils.logImportStart(log, taskId, "中介实体关联关系", excelList.size(), userName); ImportLogUtils.logImportStart(log, taskId, "中介实体关联关系", excelList.size(), userName);
Map<String, String> ownerBizIdByPersonId = getOwnerBizIdByPersonId(excelList); Map<String, String> ownerBizIdByPersonId = getOwnerBizIdByPersonId(excelList);
Set<String> existingEnterpriseCodes = getExistingEnterpriseCodes(excelList);
Set<String> existingCombinations = getExistingRelationCombinations(ownerBizIdByPersonId, excelList); Set<String> existingCombinations = getExistingRelationCombinations(ownerBizIdByPersonId, excelList);
List<CcdiIntermediaryEnterpriseRelation> successRecords = new ArrayList<>(); List<CcdiIntermediaryEnterpriseRelation> successRecords = new ArrayList<>();
@@ -79,15 +79,14 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
try { try {
validateExcel(excel); validateExcel(excel);
String ownerBizId = ownerBizIdByPersonId.get(excel.getOwnerPersonId()); String ownerPersonId = trim(excel.getOwnerPersonId());
String socialCreditCode = trim(excel.getSocialCreditCode());
String ownerBizId = ownerBizIdByPersonId.get(ownerPersonId);
if (StringUtils.isEmpty(ownerBizId)) { if (StringUtils.isEmpty(ownerBizId)) {
throw new RuntimeException("中介本人不存在,请先导入或维护中介本人信息"); throw new RuntimeException("中介本人不存在,请先导入或维护中介本人信息");
} }
if (!existingEnterpriseCodes.contains(excel.getSocialCreditCode())) {
throw new RuntimeException("统一社会信用代码不存在于系统机构表");
}
String combination = ownerBizId + "|" + excel.getSocialCreditCode(); String combination = ownerBizId + "|" + socialCreditCode;
if (existingCombinations.contains(combination)) { if (existingCombinations.contains(combination)) {
throw new RuntimeException("中介实体关联关系已存在,请勿重复导入"); throw new RuntimeException("中介实体关联关系已存在,请勿重复导入");
} }
@@ -98,6 +97,9 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation(); CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation();
BeanUtils.copyProperties(excel, relation); BeanUtils.copyProperties(excel, relation);
relation.setIntermediaryBizId(ownerBizId); relation.setIntermediaryBizId(ownerBizId);
relation.setSocialCreditCode(socialCreditCode);
relation.setRelationPersonPost(trim(excel.getRelationPersonPost()));
relation.setRemark(trim(excel.getRemark()));
relation.setCreatedBy(userName); relation.setCreatedBy(userName);
relation.setUpdatedBy(userName); relation.setUpdatedBy(userName);
successRecords.add(relation); successRecords.add(relation);
@@ -109,6 +111,15 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
} }
if (!successRecords.isEmpty()) { if (!successRecords.isEmpty()) {
enterpriseAutoFillService.ensureExistsBatch(successRecords.stream()
.map(item -> new EnterpriseAutoFillService.EnterpriseFillItem(
item.getSocialCreditCode(),
null,
EnterpriseSource.INTERMEDIARY.getCode(),
DataSource.IMPORT.getCode(),
userName
))
.toList());
saveBatch(successRecords, 500); saveBatch(successRecords, 500);
} }
if (!failures.isEmpty()) { if (!failures.isEmpty()) {
@@ -159,6 +170,7 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
private Map<String, String> getOwnerBizIdByPersonId(List<CcdiIntermediaryEnterpriseRelationExcel> excelList) { private Map<String, String> getOwnerBizIdByPersonId(List<CcdiIntermediaryEnterpriseRelationExcel> excelList) {
List<String> ownerPersonIds = excelList.stream() List<String> ownerPersonIds = excelList.stream()
.map(CcdiIntermediaryEnterpriseRelationExcel::getOwnerPersonId) .map(CcdiIntermediaryEnterpriseRelationExcel::getOwnerPersonId)
.map(this::trim)
.filter(StringUtils::isNotEmpty) .filter(StringUtils::isNotEmpty)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
@@ -173,32 +185,16 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
.collect(Collectors.toMap(CcdiBizIntermediary::getPersonId, CcdiBizIntermediary::getBizId, (left, right) -> left)); .collect(Collectors.toMap(CcdiBizIntermediary::getPersonId, CcdiBizIntermediary::getBizId, (left, right) -> left));
} }
private Set<String> getExistingEnterpriseCodes(List<CcdiIntermediaryEnterpriseRelationExcel> excelList) {
List<String> socialCreditCodes = excelList.stream()
.map(CcdiIntermediaryEnterpriseRelationExcel::getSocialCreditCode)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (socialCreditCodes.isEmpty()) {
return Collections.emptySet();
}
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, socialCreditCodes);
return enterpriseBaseInfoMapper.selectList(wrapper).stream()
.map(CcdiEnterpriseBaseInfo::getSocialCreditCode)
.collect(Collectors.toSet());
}
private Set<String> getExistingRelationCombinations(Map<String, String> ownerBizIdByPersonId, private Set<String> getExistingRelationCombinations(Map<String, String> ownerBizIdByPersonId,
List<CcdiIntermediaryEnterpriseRelationExcel> excelList) { List<CcdiIntermediaryEnterpriseRelationExcel> excelList) {
List<String> combinations = excelList.stream() List<String> combinations = excelList.stream()
.map(excel -> { .map(excel -> {
String ownerBizId = ownerBizIdByPersonId.get(excel.getOwnerPersonId()); String ownerBizId = ownerBizIdByPersonId.get(trim(excel.getOwnerPersonId()));
if (StringUtils.isEmpty(ownerBizId) || StringUtils.isEmpty(excel.getSocialCreditCode())) { String socialCreditCode = trim(excel.getSocialCreditCode());
if (StringUtils.isEmpty(ownerBizId) || StringUtils.isEmpty(socialCreditCode)) {
return null; return null;
} }
return ownerBizId + "|" + excel.getSocialCreditCode(); return ownerBizId + "|" + socialCreditCode;
}) })
.filter(StringUtils::isNotEmpty) .filter(StringUtils::isNotEmpty)
.distinct() .distinct()
@@ -210,24 +206,33 @@ public class CcdiIntermediaryEnterpriseRelationImportServiceImpl implements ICcd
} }
private void validateExcel(CcdiIntermediaryEnterpriseRelationExcel excel) { private void validateExcel(CcdiIntermediaryEnterpriseRelationExcel excel) {
if (StringUtils.isEmpty(excel.getOwnerPersonId())) { String ownerPersonId = trim(excel.getOwnerPersonId());
String socialCreditCode = trim(excel.getSocialCreditCode());
String relationPersonPost = trim(excel.getRelationPersonPost());
String remark = trim(excel.getRemark());
if (StringUtils.isEmpty(ownerPersonId)) {
throw new RuntimeException("中介本人证件号码不能为空"); throw new RuntimeException("中介本人证件号码不能为空");
} }
if (StringUtils.isEmpty(excel.getSocialCreditCode())) { if (StringUtils.isEmpty(socialCreditCode)) {
throw new RuntimeException("统一社会信用代码不能为空"); throw new RuntimeException("统一社会信用代码不能为空");
} }
String ownerPersonIdError = IdCardUtil.getErrorMessage(excel.getOwnerPersonId()); String ownerPersonIdError = IdCardUtil.getErrorMessage(ownerPersonId);
if (ownerPersonIdError != null) { if (ownerPersonIdError != null) {
throw new RuntimeException("中介本人证件号码" + ownerPersonIdError); throw new RuntimeException("中介本人证件号码" + ownerPersonIdError);
} }
if (StringUtils.isNotEmpty(excel.getRelationPersonPost()) && excel.getRelationPersonPost().length() > 100) { if (StringUtils.isNotEmpty(relationPersonPost) && relationPersonPost.length() > 100) {
throw new RuntimeException("关联职务长度不能超过100个字符"); throw new RuntimeException("关联职务长度不能超过100个字符");
} }
if (StringUtils.isNotEmpty(excel.getRemark()) && excel.getRemark().length() > 500) { if (StringUtils.isNotEmpty(remark) && remark.length() > 500) {
throw new RuntimeException("备注长度不能超过500个字符"); throw new RuntimeException("备注长度不能超过500个字符");
} }
} }
private String trim(String value) {
return value == null ? null : value.trim();
}
private IntermediaryEnterpriseRelationImportFailureVO createFailureVO(CcdiIntermediaryEnterpriseRelationExcel excel, private IntermediaryEnterpriseRelationImportFailureVO createFailureVO(CcdiIntermediaryEnterpriseRelationExcel excel,
String errorMessage) { String errorMessage) {
IntermediaryEnterpriseRelationImportFailureVO failure = new IntermediaryEnterpriseRelationImportFailureVO(); IntermediaryEnterpriseRelationImportFailureVO failure = new IntermediaryEnterpriseRelationImportFailureVO();

View File

@@ -14,6 +14,8 @@ import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryEntityDetailVO;
import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryPersonDetailVO; import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryPersonDetailVO;
import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryRelativeVO; import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryRelativeVO;
import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryVO; import com.ruoyi.info.collection.domain.vo.CcdiIntermediaryVO;
import com.ruoyi.info.collection.enums.DataSource;
import com.ruoyi.info.collection.enums.EnterpriseSource;
import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper; import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper;
import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper; import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper; import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper;
@@ -22,6 +24,7 @@ import com.ruoyi.info.collection.service.ICcdiIntermediaryEnterpriseRelationImpo
import com.ruoyi.info.collection.service.ICcdiIntermediaryEntityImportService; import com.ruoyi.info.collection.service.ICcdiIntermediaryEntityImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryPersonImportService; import com.ruoyi.info.collection.service.ICcdiIntermediaryPersonImportService;
import com.ruoyi.info.collection.service.ICcdiIntermediaryService; import com.ruoyi.info.collection.service.ICcdiIntermediaryService;
import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -69,6 +72,9 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Resource
private EnterpriseAutoFillService enterpriseAutoFillService;
/** /**
* 分页查询中介列表 * 分页查询中介列表
* 使用XML联合查询实现,支持个人中介和实体中介的灵活查询 * 使用XML联合查询实现,支持个人中介和实体中介的灵活查询
@@ -302,6 +308,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation(); CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation();
BeanUtils.copyProperties(addDTO, relation); BeanUtils.copyProperties(addDTO, relation);
relation.setIntermediaryBizId(owner.getBizId()); relation.setIntermediaryBizId(owner.getBizId());
enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem(
addDTO.getSocialCreditCode(),
null,
EnterpriseSource.INTERMEDIARY.getCode(),
DataSource.MANUAL.getCode(),
SecurityUtils.getUsername()
));
return enterpriseRelationMapper.insert(relation); return enterpriseRelationMapper.insert(relation);
} }
@@ -317,6 +330,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation(); CcdiIntermediaryEnterpriseRelation relation = new CcdiIntermediaryEnterpriseRelation();
BeanUtils.copyProperties(editDTO, relation); BeanUtils.copyProperties(editDTO, relation);
relation.setIntermediaryBizId(existing.getIntermediaryBizId()); relation.setIntermediaryBizId(existing.getIntermediaryBizId());
enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem(
editDTO.getSocialCreditCode(),
null,
EnterpriseSource.INTERMEDIARY.getCode(),
DataSource.MANUAL.getCode(),
SecurityUtils.getUsername()
));
return enterpriseRelationMapper.updateById(relation); return enterpriseRelationMapper.updateById(relation);
} }
@@ -520,9 +540,6 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
private void validateEnterpriseRelation(String bizId, String socialCreditCode, Long excludeId) { private void validateEnterpriseRelation(String bizId, String socialCreditCode, Long excludeId) {
requireIntermediaryPerson(bizId); requireIntermediaryPerson(bizId);
if (enterpriseBaseInfoMapper.selectById(socialCreditCode) == null) {
throw new RuntimeException("关联机构不存在");
}
boolean exists = enterpriseRelationMapper.existsByIntermediaryBizIdAndSocialCreditCode(bizId, socialCreditCode); boolean exists = enterpriseRelationMapper.existsByIntermediaryBizIdAndSocialCreditCode(bizId, socialCreditCode);
if (exists) { if (exists) {
if (excludeId == null) { if (excludeId == null) {

View File

@@ -2,13 +2,19 @@ package com.ruoyi.info.collection.service.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.ruoyi.info.collection.domain.CcdiPurchaseTransaction; import com.ruoyi.info.collection.domain.CcdiPurchaseTransaction;
import com.ruoyi.info.collection.domain.CcdiPurchaseTransactionSupplier;
import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiPurchaseTransactionAddDTO;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel; import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExcel;
import com.ruoyi.info.collection.domain.vo.ImportResult; import com.ruoyi.info.collection.domain.vo.ImportResult;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.domain.vo.PurchaseTransactionImportFailureVO; import com.ruoyi.info.collection.domain.vo.PurchaseTransactionImportFailureVO;
import com.ruoyi.info.collection.enums.DataSource;
import com.ruoyi.info.collection.enums.EnterpriseSource;
import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionMapper; import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionMapper;
import com.ruoyi.info.collection.mapper.CcdiPurchaseTransactionSupplierMapper;
import com.ruoyi.info.collection.service.ICcdiPurchaseTransactionImportService; import com.ruoyi.info.collection.service.ICcdiPurchaseTransactionImportService;
import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService;
import com.ruoyi.info.collection.utils.ImportLogUtils; import com.ruoyi.info.collection.utils.ImportLogUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -37,83 +43,159 @@ import java.util.stream.Collectors;
public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTransactionImportService { public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTransactionImportService {
private static final Logger log = LoggerFactory.getLogger(CcdiPurchaseTransactionImportServiceImpl.class); private static final Logger log = LoggerFactory.getLogger(CcdiPurchaseTransactionImportServiceImpl.class);
private static final String MAIN_SHEET_NAME = "招投标主信息";
private static final String SUPPLIER_SHEET_NAME = "供应商明细";
private static final int EXCEL_DATA_START_ROW = 2;
@Resource @Resource
private CcdiPurchaseTransactionMapper transactionMapper; private CcdiPurchaseTransactionMapper transactionMapper;
@Resource
private CcdiPurchaseTransactionSupplierMapper supplierMapper;
@Resource @Resource
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Resource
private EnterpriseAutoFillService enterpriseAutoFillService;
@Override @Override
@Async @Async
@Transactional @Transactional
public void importTransactionAsync(List<CcdiPurchaseTransactionExcel> excelList, String taskId, String userName) { public void importTransactionAsync(
List<CcdiPurchaseTransactionExcel> mainExcelList,
List<CcdiPurchaseTransactionSupplierExcel> supplierExcelList,
String taskId,
String userName
) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
List<CcdiPurchaseTransactionExcel> safeMainList = mainExcelList == null ? List.of() : mainExcelList;
List<CcdiPurchaseTransactionSupplierExcel> safeSupplierList = supplierExcelList == null ? List.of() : supplierExcelList;
List<MainImportRow> indexedMainRows = buildMainImportRows(safeMainList);
List<SupplierImportRow> indexedSupplierRows = buildSupplierImportRows(safeSupplierList);
int totalCount = countImportUnits(safeMainList, safeSupplierList);
// 记录导入开始 // 记录导入开始
ImportLogUtils.logImportStart(log, taskId, "采购交易信息", excelList.size(), userName); ImportLogUtils.logImportStart(log, taskId, "招投标信息维护", totalCount, userName);
List<CcdiPurchaseTransaction> newRecords = new ArrayList<>(); List<CcdiPurchaseTransaction> newTransactions = new ArrayList<>();
List<CcdiPurchaseTransactionSupplier> newSuppliers = new ArrayList<>();
List<PurchaseTransactionImportFailureVO> failures = new ArrayList<>(); List<PurchaseTransactionImportFailureVO> failures = new ArrayList<>();
// 批量查询已存在的采购事项ID // 批量查询已存在的采购事项ID
ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的采购事项ID", excelList.size()); ImportLogUtils.logBatchQueryStart(log, taskId, "已存在的采购事项ID", safeMainList.size());
Set<String> existingIds = getExistingPurchaseIds(excelList); Set<String> existingIds = getExistingPurchaseIds(safeMainList);
ImportLogUtils.logBatchQueryComplete(log, taskId, "采购事项ID", existingIds.size()); ImportLogUtils.logBatchQueryComplete(log, taskId, "采购事项ID", existingIds.size());
// 用于跟踪Excel文件内已处理的采购事项ID Map<String, List<MainImportRow>> mainGroupMap = indexedMainRows.stream()
Set<String> processedIds = new HashSet<>(); .filter(item -> StringUtils.isNotEmpty(item.data().getPurchaseId()))
.collect(Collectors.groupingBy(
item -> item.data().getPurchaseId(),
LinkedHashMap::new,
Collectors.toList()
));
Map<String, List<SupplierImportRow>> supplierGroupMap = indexedSupplierRows.stream()
.filter(item -> StringUtils.isNotEmpty(item.data().getPurchaseId()))
.collect(Collectors.groupingBy(
item -> item.data().getPurchaseId(),
LinkedHashMap::new,
Collectors.toList()
));
LinkedHashSet<String> purchaseIds = new LinkedHashSet<>();
purchaseIds.addAll(mainGroupMap.keySet());
purchaseIds.addAll(supplierGroupMap.keySet());
// 分类数据 for (SupplierImportRow supplierExcel : indexedSupplierRows) {
for (int i = 0; i < excelList.size(); i++) { if (StringUtils.isEmpty(supplierExcel.data().getPurchaseId())) {
CcdiPurchaseTransactionExcel excel = excelList.get(i); failures.add(buildFailure(
null,
null,
SUPPLIER_SHEET_NAME,
String.valueOf(supplierExcel.sheetRowNum()),
"供应商明细Sheet中的采购事项ID不能为空"
));
}
}
int index = 0;
for (String purchaseId : purchaseIds) {
index++;
List<MainImportRow> mainRows = mainGroupMap.getOrDefault(purchaseId, List.of());
List<SupplierImportRow> supplierRows = supplierGroupMap.getOrDefault(purchaseId, List.of());
try { try {
// 转换为AddDTO进行验证 if (existingIds.contains(purchaseId)) {
CcdiPurchaseTransactionAddDTO addDTO = new CcdiPurchaseTransactionAddDTO(); throw buildValidationException(
BeanUtils.copyProperties(excel, addDTO); MAIN_SHEET_NAME,
extractMainRowNums(mainRows),
// 验证数据 String.format("采购事项ID[%s]已存在,请勿重复导入", purchaseId)
validateTransactionData(addDTO, existingIds); );
}
CcdiPurchaseTransaction transaction = new CcdiPurchaseTransaction(); if (mainRows.isEmpty()) {
BeanUtils.copyProperties(excel, transaction); throw buildValidationException(
SUPPLIER_SHEET_NAME,
if (existingIds.contains(excel.getPurchaseId())) { extractSupplierRowNums(supplierRows),
// 采购事项ID已存在直接报错 String.format("采购事项ID[%s]缺少招投标主信息", purchaseId)
throw new RuntimeException(String.format("采购事项ID[%s]已存在,请勿重复导入", excel.getPurchaseId())); );
} else if (processedIds.contains(excel.getPurchaseId())) { }
// Excel文件内部重复 if (mainRows.size() > 1) {
throw new RuntimeException(String.format("采购事项ID[%s]在导入文件中重复,已跳过此条记录", excel.getPurchaseId())); throw buildValidationException(
} else { MAIN_SHEET_NAME,
transaction.setCreatedBy(userName); extractMainRowNums(mainRows),
transaction.setUpdatedBy(userName); String.format("采购事项ID[%s]在招投标主信息Sheet中重复", purchaseId)
newRecords.add(transaction); );
processedIds.add(excel.getPurchaseId()); // 标记为已处理
} }
MainImportRow mainRow = mainRows.getFirst();
CcdiPurchaseTransactionExcel mainExcel = mainRow.data();
CcdiPurchaseTransactionAddDTO addDTO = new CcdiPurchaseTransactionAddDTO();
BeanUtils.copyProperties(mainExcel, addDTO);
validateTransactionData(addDTO, mainRow.sheetRowNum());
List<CcdiPurchaseTransactionSupplier> suppliers = buildSupplierEntities(purchaseId, supplierRows, userName);
CcdiPurchaseTransaction transaction = new CcdiPurchaseTransaction();
BeanUtils.copyProperties(mainExcel, transaction);
fillWinnerSummary(transaction, suppliers);
transaction.setCreatedBy(userName);
transaction.setUpdatedBy(userName);
newTransactions.add(transaction);
newSuppliers.addAll(suppliers);
// 记录进度 // 记录进度
ImportLogUtils.logProgress(log, taskId, i + 1, excelList.size(), ImportLogUtils.logProgress(log, taskId, index, Math.max(totalCount, purchaseIds.size()),
newRecords.size(), failures.size()); newTransactions.size(), failures.size());
} catch (Exception e) { } catch (Exception e) {
PurchaseTransactionImportFailureVO failure = new PurchaseTransactionImportFailureVO(); MainImportRow mainRow = mainRows.isEmpty() ? null : mainRows.getFirst();
BeanUtils.copyProperties(excel, failure); CcdiPurchaseTransactionExcel mainExcel = mainRow == null ? null : mainRow.data();
failure.setErrorMessage(e.getMessage()); FailureMeta failureMeta = resolveFailureMeta(e, mainRows, supplierRows);
failures.add(failure); failures.add(buildFailure(
mainExcel,
purchaseId,
failureMeta.sheetName(),
failureMeta.sheetRowNum(),
e.getMessage()
));
// 记录验证失败日志 // 记录验证失败日志
String keyData = String.format("采购事项ID=%s, 采购类别=%s, 标的物=%s", String keyData = String.format("采购事项ID=%s, 采购类别=%s, 标的物=%s",
excel.getPurchaseId(), excel.getPurchaseCategory(), excel.getSubjectName()); purchaseId,
ImportLogUtils.logValidationError(log, taskId, i + 1, e.getMessage(), keyData); mainExcel == null ? "" : mainExcel.getPurchaseCategory(),
mainExcel == null ? "" : mainExcel.getSubjectName());
ImportLogUtils.logValidationError(log, taskId, index, e.getMessage(), keyData);
} }
} }
// 批量插入新数据 // 批量插入新数据
if (!newRecords.isEmpty()) { if (!newTransactions.isEmpty()) {
autoFillSupplierEnterprises(newSuppliers, userName);
ImportLogUtils.logBatchOperationStart(log, taskId, "插入", ImportLogUtils.logBatchOperationStart(log, taskId, "插入",
(newRecords.size() + 499) / 500, 500); (newTransactions.size() + 499) / 500, 500);
saveBatch(newRecords, 500); saveBatch(newTransactions, 500);
}
if (!newSuppliers.isEmpty()) {
saveSupplierBatch(newSuppliers, 500);
} }
// 保存失败记录到Redis // 保存失败记录到Redis
@@ -128,8 +210,8 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
} }
ImportResult result = new ImportResult(); ImportResult result = new ImportResult();
result.setTotalCount(excelList.size()); result.setTotalCount(totalCount);
result.setSuccessCount(newRecords.size()); result.setSuccessCount(newTransactions.size());
result.setFailureCount(failures.size()); result.setFailureCount(failures.size());
// 更新最终状态 // 更新最终状态
@@ -138,8 +220,8 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
// 记录导入完成 // 记录导入完成
long duration = System.currentTimeMillis() - startTime; long duration = System.currentTimeMillis() - startTime;
ImportLogUtils.logImportComplete(log, taskId, "采购交易信息", ImportLogUtils.logImportComplete(log, taskId, "招投标信息维护",
excelList.size(), result.getSuccessCount(), result.getFailureCount(), duration); totalCount, result.getSuccessCount(), result.getFailureCount(), duration);
} }
/** /**
@@ -243,71 +325,351 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
} }
} }
private void saveSupplierBatch(List<CcdiPurchaseTransactionSupplier> list, int batchSize) {
for (int i = 0; i < list.size(); i += batchSize) {
int end = Math.min(i + batchSize, list.size());
List<CcdiPurchaseTransactionSupplier> subList = list.subList(i, end);
for (CcdiPurchaseTransactionSupplier supplier : subList) {
supplierMapper.insert(supplier);
}
}
}
private void autoFillSupplierEnterprises(List<CcdiPurchaseTransactionSupplier> supplierList, String userName) {
enterpriseAutoFillService.ensureExistsBatch(supplierList.stream()
.filter(item -> StringUtils.isNotEmpty(item.getSupplierUscc()))
.map(item -> new EnterpriseAutoFillService.EnterpriseFillItem(
item.getSupplierUscc(),
item.getSupplierName(),
EnterpriseSource.SUPPLIER.getCode(),
DataSource.IMPORT.getCode(),
userName
))
.toList());
}
/** /**
* 验证采购交易数据 * 验证采购交易数据
* *
* @param addDTO 新增DTO * @param addDTO 新增DTO
* @param existingIds 已存在的采购事项ID集合
*/ */
private void validateTransactionData(CcdiPurchaseTransactionAddDTO addDTO, Set<String> existingIds) { private void validateTransactionData(CcdiPurchaseTransactionAddDTO addDTO, int sheetRowNum) {
// 验证必填字段 // 验证必填字段
if (StringUtils.isEmpty(addDTO.getPurchaseId())) { if (StringUtils.isEmpty(addDTO.getPurchaseId())) {
throw new RuntimeException("采购事项ID不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购事项ID不能为空");
} }
if (StringUtils.isEmpty(addDTO.getPurchaseCategory())) { if (StringUtils.isEmpty(addDTO.getPurchaseCategory())) {
throw new RuntimeException("采购类别不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购类别不能为空");
} }
if (StringUtils.isEmpty(addDTO.getSubjectName())) { if (StringUtils.isEmpty(addDTO.getSubjectName())) {
throw new RuntimeException("标的物名称不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "标的物名称不能为空");
} }
if (addDTO.getPurchaseQty() == null) { if (addDTO.getPurchaseQty() == null) {
throw new RuntimeException("采购数量不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购数量不能为空");
} }
if (addDTO.getBudgetAmount() == null) { if (addDTO.getBudgetAmount() == null) {
throw new RuntimeException("预算金额不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "预算金额不能为空");
} }
if (StringUtils.isEmpty(addDTO.getPurchaseMethod())) { if (StringUtils.isEmpty(addDTO.getPurchaseMethod())) {
throw new RuntimeException("采购方式不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购方式不能为空");
} }
if (addDTO.getApplyDate() == null) { if (addDTO.getApplyDate() == null) {
throw new RuntimeException("采购申请日期不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购申请日期不能为空");
} }
if (StringUtils.isEmpty(addDTO.getApplicantId())) { if (StringUtils.isEmpty(addDTO.getApplicantId())) {
throw new RuntimeException("申请人工号不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请人工号不能为空");
} }
if (StringUtils.isEmpty(addDTO.getApplicantName())) { if (StringUtils.isEmpty(addDTO.getApplicantName())) {
throw new RuntimeException("申请人姓名不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请人姓名不能为空");
} }
if (StringUtils.isEmpty(addDTO.getApplyDepartment())) { if (StringUtils.isEmpty(addDTO.getApplyDepartment())) {
throw new RuntimeException("申请部门不能为空"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请部门不能为空");
} }
// 验证工号格式7位数字 // 验证工号格式7位数字
if (!addDTO.getApplicantId().matches("^\\d{7}$")) { if (!addDTO.getApplicantId().matches("^\\d{7}$")) {
throw new RuntimeException("申请人工号必须为7位数字"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请人工号必须为7位数字");
} }
if (StringUtils.isNotEmpty(addDTO.getPurchaseLeaderId()) && !addDTO.getPurchaseLeaderId().matches("^\\d{7}$")) { if (StringUtils.isNotEmpty(addDTO.getPurchaseLeaderId()) && !addDTO.getPurchaseLeaderId().matches("^\\d{7}$")) {
throw new RuntimeException("采购负责人工号必须为7位数字"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购负责人工号必须为7位数字");
} }
// 验证金额非负 // 验证金额非负
if (addDTO.getPurchaseQty().compareTo(BigDecimal.ZERO) <= 0) { if (addDTO.getPurchaseQty().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("采购数量必须大于0"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购数量必须大于0");
} }
if (addDTO.getBudgetAmount().compareTo(BigDecimal.ZERO) <= 0) { if (addDTO.getBudgetAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("预算金额必须大于0"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "预算金额必须大于0");
} }
if (addDTO.getBidAmount() != null && addDTO.getBidAmount().compareTo(BigDecimal.ZERO) <= 0) { if (addDTO.getBidAmount() != null && addDTO.getBidAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("中标金额必须大于0"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "中标金额必须大于0");
} }
if (addDTO.getActualAmount() != null && addDTO.getActualAmount().compareTo(BigDecimal.ZERO) <= 0) { if (addDTO.getActualAmount() != null && addDTO.getActualAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("实际采购金额必须大于0"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "实际采购金额必须大于0");
} }
if (addDTO.getContractAmount() != null && addDTO.getContractAmount().compareTo(BigDecimal.ZERO) <= 0) { if (addDTO.getContractAmount() != null && addDTO.getContractAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("合同金额必须大于0"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "合同金额必须大于0");
} }
if (addDTO.getSettlementAmount() != null && addDTO.getSettlementAmount().compareTo(BigDecimal.ZERO) <= 0) { if (addDTO.getSettlementAmount() != null && addDTO.getSettlementAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("结算金额必须大于0"); throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "结算金额必须大于0");
}
}
private List<CcdiPurchaseTransactionSupplier> buildSupplierEntities(
String purchaseId,
List<SupplierImportRow> supplierRows,
String userName
) {
List<SupplierImportRow> normalizedRows = supplierRows == null
? List.of()
: supplierRows.stream()
.filter(Objects::nonNull)
.filter(item -> hasAnySupplierValue(item.data()))
.toList();
List<Integer> winnerRowNums = new ArrayList<>();
Map<String, Integer> supplierKeyRowMap = new LinkedHashMap<>();
List<CcdiPurchaseTransactionSupplier> result = new ArrayList<>();
for (SupplierImportRow supplierImportRow : normalizedRows) {
CcdiPurchaseTransactionSupplierExcel supplierRow = supplierImportRow.data();
int isBidWinner = validateSupplierRow(supplierImportRow);
if (isBidWinner == 1) {
winnerRowNums.add(supplierImportRow.sheetRowNum());
}
String duplicateKey = StringUtils.trimToEmpty(supplierRow.getSupplierName()) + "|"
+ StringUtils.trimToEmpty(supplierRow.getSupplierUscc());
Integer firstRowNum = supplierKeyRowMap.putIfAbsent(duplicateKey, supplierImportRow.sheetRowNum());
if (firstRowNum != null) {
throw buildValidationException(
SUPPLIER_SHEET_NAME,
List.of(firstRowNum, supplierImportRow.sheetRowNum()),
String.format("采购事项ID[%s]存在重复供应商", purchaseId)
);
}
CcdiPurchaseTransactionSupplier supplier = new CcdiPurchaseTransactionSupplier();
supplier.setPurchaseId(purchaseId);
supplier.setSupplierName(StringUtils.trim(supplierRow.getSupplierName()));
supplier.setSupplierUscc(StringUtils.trimToNull(supplierRow.getSupplierUscc()));
supplier.setContactPerson(StringUtils.trimToNull(supplierRow.getContactPerson()));
supplier.setContactPhone(StringUtils.trimToNull(supplierRow.getContactPhone()));
supplier.setSupplierBankAccount(StringUtils.trimToNull(supplierRow.getSupplierBankAccount()));
supplier.setIsBidWinner(isBidWinner);
supplier.setSortOrder(supplierRow.getSortOrder() == null ? result.size() + 1 : supplierRow.getSortOrder());
supplier.setCreatedBy(userName);
supplier.setUpdatedBy(userName);
result.add(supplier);
}
if (winnerRowNums.size() > 1) {
throw buildValidationException(
SUPPLIER_SHEET_NAME,
winnerRowNums,
String.format("采购事项ID[%s]存在多条中标供应商", purchaseId)
);
}
return result;
}
private int validateSupplierRow(SupplierImportRow supplierImportRow) {
CcdiPurchaseTransactionSupplierExcel supplierRow = supplierImportRow.data();
if (StringUtils.isEmpty(supplierRow.getSupplierName())) {
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商名称不能为空");
}
if (StringUtils.length(supplierRow.getSupplierName()) > 200) {
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商名称长度不能超过200个字符");
}
if (StringUtils.length(supplierRow.getContactPerson()) > 50) {
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商联系人长度不能超过50个字符");
}
if (StringUtils.length(supplierRow.getSupplierBankAccount()) > 50) {
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商银行账户长度不能超过50个字符");
}
if (StringUtils.isNotEmpty(supplierRow.getContactPhone())
&& !supplierRow.getContactPhone().matches("^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$")) {
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商联系电话格式不正确");
}
if (StringUtils.isNotEmpty(supplierRow.getSupplierUscc())
&& !supplierRow.getSupplierUscc().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) {
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商统一信用代码格式不正确");
}
return parseIsBidWinner(supplierRow.getIsBidWinner(), supplierImportRow.sheetRowNum());
}
private boolean hasAnySupplierValue(CcdiPurchaseTransactionSupplierExcel supplierRow) {
return StringUtils.isNotEmpty(supplierRow.getPurchaseId())
|| StringUtils.isNotEmpty(supplierRow.getSupplierName())
|| StringUtils.isNotEmpty(supplierRow.getSupplierUscc())
|| StringUtils.isNotEmpty(supplierRow.getContactPerson())
|| StringUtils.isNotEmpty(supplierRow.getContactPhone())
|| StringUtils.isNotEmpty(supplierRow.getSupplierBankAccount())
|| StringUtils.isNotEmpty(supplierRow.getIsBidWinner())
|| supplierRow.getSortOrder() != null;
}
private int parseIsBidWinner(String rawValue, Integer sheetRowNum) {
if (StringUtils.isEmpty(rawValue)) {
return 0;
}
String normalized = StringUtils.trim(rawValue);
if ("1".equals(normalized) || "".equals(normalized) || "Y".equalsIgnoreCase(normalized)
|| "TRUE".equalsIgnoreCase(normalized)) {
return 1;
}
if ("0".equals(normalized) || "".equals(normalized) || "N".equalsIgnoreCase(normalized)
|| "FALSE".equalsIgnoreCase(normalized)) {
return 0;
}
throw buildValidationException(
SUPPLIER_SHEET_NAME,
sheetRowNum == null ? List.of() : List.of(sheetRowNum),
"是否中标仅支持填写“是/否”或“1/0”"
);
}
private void fillWinnerSummary(
CcdiPurchaseTransaction transaction,
List<CcdiPurchaseTransactionSupplier> supplierList
) {
CcdiPurchaseTransactionSupplier winner = supplierList.stream()
.filter(item -> Objects.equals(item.getIsBidWinner(), 1))
.findFirst()
.orElse(null);
if (winner == null) {
transaction.setSupplierName(null);
transaction.setSupplierUscc(null);
transaction.setContactPerson(null);
transaction.setContactPhone(null);
transaction.setSupplierBankAccount(null);
return;
}
transaction.setSupplierName(winner.getSupplierName());
transaction.setSupplierUscc(winner.getSupplierUscc());
transaction.setContactPerson(winner.getContactPerson());
transaction.setContactPhone(winner.getContactPhone());
transaction.setSupplierBankAccount(winner.getSupplierBankAccount());
}
private PurchaseTransactionImportFailureVO buildFailure(
CcdiPurchaseTransactionExcel mainExcel,
String purchaseId,
String sheetName,
String sheetRowNum,
String errorMessage
) {
PurchaseTransactionImportFailureVO failure = new PurchaseTransactionImportFailureVO();
if (mainExcel != null) {
BeanUtils.copyProperties(mainExcel, failure);
}
failure.setSheetName(sheetName);
failure.setSheetRowNum(sheetRowNum);
if (StringUtils.isNotEmpty(purchaseId)) {
failure.setPurchaseId(purchaseId);
}
failure.setErrorMessage(errorMessage);
return failure;
}
private int countImportUnits(
List<CcdiPurchaseTransactionExcel> mainExcelList,
List<CcdiPurchaseTransactionSupplierExcel> supplierExcelList
) {
LinkedHashSet<String> purchaseIds = new LinkedHashSet<>();
purchaseIds.addAll(
mainExcelList.stream()
.map(CcdiPurchaseTransactionExcel::getPurchaseId)
.filter(StringUtils::isNotEmpty)
.collect(Collectors.toCollection(LinkedHashSet::new))
);
purchaseIds.addAll(
supplierExcelList.stream()
.map(CcdiPurchaseTransactionSupplierExcel::getPurchaseId)
.filter(StringUtils::isNotEmpty)
.collect(Collectors.toCollection(LinkedHashSet::new))
);
return purchaseIds.size();
}
private List<MainImportRow> buildMainImportRows(List<CcdiPurchaseTransactionExcel> mainExcelList) {
List<MainImportRow> rows = new ArrayList<>();
for (int i = 0; i < mainExcelList.size(); i++) {
rows.add(new MainImportRow(mainExcelList.get(i), i + EXCEL_DATA_START_ROW));
}
return rows;
}
private List<SupplierImportRow> buildSupplierImportRows(List<CcdiPurchaseTransactionSupplierExcel> supplierExcelList) {
List<SupplierImportRow> rows = new ArrayList<>();
for (int i = 0; i < supplierExcelList.size(); i++) {
rows.add(new SupplierImportRow(supplierExcelList.get(i), i + EXCEL_DATA_START_ROW));
}
return rows;
}
private List<Integer> extractMainRowNums(List<MainImportRow> rows) {
return rows.stream().map(MainImportRow::sheetRowNum).toList();
}
private List<Integer> extractSupplierRowNums(List<SupplierImportRow> rows) {
return rows.stream().map(SupplierImportRow::sheetRowNum).toList();
}
private ImportValidationException buildValidationException(String sheetName, List<Integer> rowNums, String message) {
return new ImportValidationException(sheetName, formatSheetRowNum(rowNums), message);
}
private FailureMeta resolveFailureMeta(
Exception exception,
List<MainImportRow> mainRows,
List<SupplierImportRow> supplierRows
) {
if (exception instanceof ImportValidationException validationException) {
return new FailureMeta(validationException.getSheetName(), validationException.getSheetRowNum());
}
if (!mainRows.isEmpty()) {
return new FailureMeta(MAIN_SHEET_NAME, formatSheetRowNum(extractMainRowNums(mainRows)));
}
if (!supplierRows.isEmpty()) {
return new FailureMeta(SUPPLIER_SHEET_NAME, formatSheetRowNum(extractSupplierRowNums(supplierRows)));
}
return new FailureMeta("", "");
}
private String formatSheetRowNum(List<Integer> rowNums) {
if (rowNums == null || rowNums.isEmpty()) {
return "";
}
return rowNums.stream()
.filter(Objects::nonNull)
.distinct()
.sorted()
.map(String::valueOf)
.collect(Collectors.joining(""));
}
private record MainImportRow(CcdiPurchaseTransactionExcel data, int sheetRowNum) {}
private record SupplierImportRow(CcdiPurchaseTransactionSupplierExcel data, int sheetRowNum) {}
private record FailureMeta(String sheetName, String sheetRowNum) {}
private static class ImportValidationException extends RuntimeException {
private final String sheetName;
private final String sheetRowNum;
private ImportValidationException(String sheetName, String sheetRowNum, String message) {
super(message);
this.sheetName = sheetName;
this.sheetRowNum = sheetRowNum;
}
public String getSheetName() {
return sheetName;
}
public String getSheetRowNum() {
return sheetRowNum;
} }
} }
} }

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