Compare commits

...

112 Commits

Author SHA1 Message Date
lg
57855f4307 bug修改 2025-10-25 22:18:10 +08:00
6784eafe6e 供应商-客户中间表引用表的新增修改删除修改 2025-10-25 22:17:26 +08:00
0b42c1d6a6 Merge remote-tracking branch 'origin/dev' into dev 2025-10-25 22:16:07 +08:00
4b37a7327f 10-25-修改 2025-10-25 22:16:00 +08:00
lg
0287f1e4ce bug修改 2025-10-25 21:58:47 +08:00
5d8af1cab8 10-25-供应商删除修改 2025-10-25 21:57:13 +08:00
123896f08b Merge remote-tracking branch 'origin/dev' into dev 2025-10-25 21:25:40 +08:00
a8a198b51f 10-25-增加校验 2025-10-25 21:25:33 +08:00
lg
f00b98714a Merge remote-tracking branch 'origin/dev' into dev 2025-10-25 21:24:39 +08:00
lg
4ff87f3996 bug修改 2025-10-25 21:23:58 +08:00
5f3ae0f9f1 10-25-供应商地域绑定及搜索 2025-10-25 21:22:36 +08:00
786c864a27 供应商-客户中间表引用表的新增修改删除修改 2025-10-25 21:21:48 +08:00
lg
c61e802b85 bug修改 2025-10-25 21:09:26 +08:00
lg
9a568799f4 bug修改 2025-10-25 21:05:26 +08:00
8aa38063bf 供应商-客户中间表引用表的新增修改删除 2025-10-25 20:58:07 +08:00
c4a11ec245 10-25-解除继承 2025-10-25 20:28:40 +08:00
lg
169b76589c bug修改 2025-10-25 20:23:02 +08:00
lg
199f51ea21 bug修改 2025-10-25 20:19:21 +08:00
lg
23572dfc07 bug修改 2025-10-25 20:14:03 +08:00
570b0ce316 10-25-供应商新增、删除添加校验 2025-10-25 20:11:14 +08:00
lg
f953a96c36 bug修改 2025-10-25 20:05:43 +08:00
zt
25c4eee464 bug 2025-10-25 20:01:28 +08:00
lg
b209ef1fab 中间表数据添加 2025-10-25 19:20:32 +08:00
lg
77e9f4d9a2 中间表数据添加 2025-10-25 19:18:09 +08:00
lg
e73c808bc3 xzd前缀添加 2025-10-25 17:21:16 +08:00
zt
edf0d1a5db 设计导出 2025-10-25 17:10:43 +08:00
lcj
cc23a308c1 修改配置,车辆管理 2025-10-25 17:01:15 +08:00
bc891327c9 10-25-字段调整 2025-10-25 16:51:17 +08:00
39bedfeb92 10-25-字段调整 2025-10-25 16:44:33 +08:00
56fc4ff83e 10-24-修复 2025-10-24 22:16:26 +08:00
524ed30728 Merge remote-tracking branch 'origin/dev' into dev 2025-10-24 22:16:13 +08:00
584304e744 供应商-客户中间表初始化 2025-10-24 22:07:16 +08:00
zt
56418600c5 1 2025-10-24 20:17:03 +08:00
40e57b18cb 10-24-修复 2025-10-24 20:05:45 +08:00
lg
ced6cb219c deptid添加 2025-10-24 20:01:03 +08:00
218ec5ea95 gps接口修改 2025-10-24 19:40:54 +08:00
zt
1c601bd68e 设计导出 2025-10-24 19:32:20 +08:00
lg
7d6eba719b 投标保证金收回 2025-10-24 17:50:15 +08:00
8cfc34dbcb Merge remote-tracking branch 'origin/dev' into dev 2025-10-24 17:46:29 +08:00
23a749973d 10-24-修复 2025-10-24 17:45:59 +08:00
4b96702dc9 成本预算添加部门id 2025-10-24 17:41:37 +08:00
a1af8711ef 添加id转name注解方法 2025-10-24 17:05:11 +08:00
9e8cff931b 10-24-修复 2025-10-24 17:00:15 +08:00
lg
5bccd71bdc 投标保证金缴纳 2025-10-24 16:59:24 +08:00
lg
544f35a601 投标管理修改 2025-10-24 16:04:38 +08:00
f920d4976e 10-24-修复 2025-10-24 15:22:31 +08:00
cdcd665d43 添加id转name注解方法 2025-10-24 11:55:19 +08:00
lg
08de61e455 trans添加客户供应商 2025-10-24 10:48:06 +08:00
lcj
025c3115b7 接入ai,识别 2025-10-24 09:23:40 +08:00
9e366554b7 10-23-修复 2025-10-23 22:35:04 +08:00
zt
d934abf0fe bug修改 2025-10-23 19:36:49 +08:00
lg
e16e9133e2 采购合同修改 2025-10-23 17:42:42 +08:00
d7854a35d7 10-23-修复 2025-10-23 17:36:00 +08:00
97e0dd467f Merge remote-tracking branch 'origin/dev' into dev 2025-10-23 17:24:08 +08:00
e58a99e696 10-23-修复 2025-10-23 17:24:00 +08:00
lg
0d5a9eb505 采购合同修改 2025-10-23 17:18:36 +08:00
lg
45ac1817e1 成本预算修改 2025-10-23 16:08:17 +08:00
lg
f2d4ff4237 客户信息变更 2025-10-23 15:04:42 +08:00
a4cc5c14f3 Merge remote-tracking branch 'origin/dev' into dev 2025-10-23 14:27:41 +08:00
5be571cc30 10-23-修复 2025-10-23 14:27:32 +08:00
ff656dd046 富文本配置修改 2025-10-23 11:28:29 +08:00
lg
20574d0037 bug修改 2025-10-23 11:25:12 +08:00
42cf396e93 10-23-xss规则 2025-10-23 11:22:34 +08:00
2a30b05dd7 新中大综合服务合同优化 2025-10-23 11:21:46 +08:00
4ecee185bf 10-22-bug修复 2025-10-23 11:14:21 +08:00
lg
48ab59c67d bug修改 2025-10-23 09:22:27 +08:00
lcj
806e8f3391 修改配置文件 2025-10-22 20:19:40 +08:00
61244321a7 10-22-bug修复 2025-10-22 20:10:10 +08:00
zt
ff1e613316 设计导出 2025-10-22 18:58:30 +08:00
03161751fc 10-22-bug修复 2025-10-22 18:51:50 +08:00
9ebf4a3e5f 10-22-新增供应商空值校验 2025-10-22 18:38:48 +08:00
lcj
3ba13e3ef8 新部署一个项目 2025-10-22 16:07:10 +08:00
lg
8d3853fe6b bug修改 2025-10-22 15:08:30 +08:00
lg
dbc09a62ea 后端修改 2025-10-22 09:29:20 +08:00
zt
f3fa78475c bug 2025-10-21 20:05:55 +08:00
c565771283 10-21-修复 2025-10-21 20:04:59 +08:00
bac8488244 新中大综合服务合同修改,建管大屏接口优化、gps接口优化 2025-10-21 18:35:07 +08:00
lcj
a5f661b558 承包合同,安全质量大屏 2025-10-21 18:29:06 +08:00
zt
ceecec97c7 打印天气 2025-10-21 18:25:52 +08:00
c6ae8a4c00 10-21-收票与开票-收票登记 2025-10-21 17:31:00 +08:00
zt
37d0c776c0 设计计划 2025-10-21 15:53:56 +08:00
f8eea0f63f 修改查询语句 2025-10-21 10:36:52 +08:00
lg
59c749ab2a 表名修改 2025-10-21 10:09:59 +08:00
eeeba2bf4b 10-21-修复 2025-10-21 10:07:55 +08:00
lg
a9ce42101f 补充 2025-10-20 20:16:44 +08:00
lcj
13de88265f 识别逻辑 2025-10-20 20:09:51 +08:00
a464a1236d 综合服务进度、采购进度、竣工、竣工调整添加底部计量结算内容 2025-10-20 20:07:40 +08:00
fce5d0e7fc 10-20-修复字段 2025-10-20 19:57:27 +08:00
66ba43d030 Merge remote-tracking branch 'origin/dev' into dev 2025-10-20 19:26:18 +08:00
1aa77e5eda 10-20-决算清 2025-10-20 19:26:08 +08:00
zt
037016fc13 人脸对接 2025-10-20 19:24:36 +08:00
99f0026552 大屏接口添加计划容量 2025-10-20 17:12:06 +08:00
lg
535262d721 枚举 2025-10-20 16:32:32 +08:00
fbcb9ca3f2 10-20-决算清单(调整)初始化 2025-10-20 16:19:00 +08:00
2a7a20b966 Merge remote-tracking branch 'origin/dev' into dev 2025-10-20 16:10:14 +08:00
98fdab0dba 10-20-承包合同进度结算补齐 2025-10-20 16:10:03 +08:00
lg
bd71335ae6 分包合同枚举 2025-10-20 15:53:15 +08:00
zt
7f746fc250 考勤机 2025-10-20 15:41:43 +08:00
lg
901c8785fe 合同类型字段补充 2025-10-20 15:30:34 +08:00
e4e9718acb 10-20-初始化 2025-10-20 15:08:29 +08:00
738101f374 10-20-初始化 2025-10-20 15:05:58 +08:00
lg
a7befd7278 字段修改补充 2025-10-20 14:56:38 +08:00
lg
52e968c313 字段修改 2025-10-20 14:47:03 +08:00
lg
de9d7d34d6 部门修改和变更增加清单,合同内清单 2025-10-20 14:29:59 +08:00
zt
9f0105d88a 角色优化 2025-10-20 14:21:18 +08:00
lcj
80ec8ff86d 结算 2025-10-20 14:19:43 +08:00
f3473fe5d5 阶段成本分解校验修改 2025-10-18 21:05:20 +08:00
lcj
856f3f334b 修改日期 2025-10-18 20:35:50 +08:00
lcj
0b216a4101 修改配置 2025-10-18 20:19:35 +08:00
c7338b45ad 大屏项目 2025-10-18 20:09:38 +08:00
c93b1b752e Merge remote-tracking branch 'origin/dev' into dev 2025-10-18 20:08:43 +08:00
e38074bb25 大屏项目 2025-10-18 20:08:17 +08:00
543 changed files with 20637 additions and 2783 deletions

View File

@ -1,3 +1,7 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8899
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
@ -32,6 +36,12 @@ snail-job:
--- # 数据源配置
spring:
ai:
dashscope:
api-key: sk-8d8df92fcbac4bd2922edba30b0bb8fa
chat:
options:
model: qwen-plus
datasource:
type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
@ -52,6 +62,9 @@ spring:
url: jdbc:mysql://192.168.110.2:13386/xinnengyuandev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: xinnengyuandev
password: StRWCZdZirysNSs2
# url: jdbc:mysql://192.168.110.2:13386/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: xinnengyuan
# password: mEZPC5Sdf3r2HENi
# 从库数据源
slave:
lazy: true
@ -71,9 +84,16 @@ spring:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgdev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgdev
password: JhYxREf25AXdy3h8
url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgc
password: nWKDKRNRT48tFBdh
# slave:
# lazy: true
# type: ${spring.datasource.type}
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://192.168.110.2:13386/zmkgprod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: zmkgprod
# password: MaY8nehwWkJriWPm
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
@ -116,7 +136,7 @@ spring.data:
# 端口默认为6379
port: 9287
# 数据库索引
database: 10
database: 16
# redis 密码必须配置
password: syar23rdsaagdrsa
# 连接超时时间
@ -344,3 +364,7 @@ qrCode:
# 无人机大图
drone:
url: http://192.168.110.2:9512
# 聊天服务
chat:
server:
port: 19099

View File

@ -1,3 +1,7 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 9918
--- # 临时文件存储位置 避免临时文件被系统清理报错
spring.servlet.multipart.location: /ruoyi/server/temp
@ -35,6 +39,12 @@ snail-job:
--- # 数据源配置
spring:
ai:
dashscope:
api-key: xxx
chat:
options:
model: qwen-plus
datasource:
type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
@ -105,7 +115,7 @@ spring.data:
# 端口默认为6379
port: 9287
# 数据库索引
database: 6
database: 17
# redis 密码必须配置
password: syar23rdsaagdrsa
# 连接超时时间
@ -164,7 +174,7 @@ sms:
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
restricted: true
restricted: false
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
@ -183,12 +193,45 @@ sms:
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
# 登录
supplier: tencent
access-key-id: 您的accessKey
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
access-key-id: AKIDb3JK5dx4wa0DCxWqvxlKejvysZ3ITVJv
access-key-secret: c5LPFsJI8k7GDxTkoeFj4A1ukQr66rPi
signature: 重庆远界大数据研究院
sdk-app-id: 1401018866
template-id: 2491779
config3:
# 注册
supplier: tencent
access-key-id: AKIDb3JK5dx4wa0DCxWqvxlKejvysZ3ITVJv
access-key-secret: c5LPFsJI8k7GDxTkoeFj4A1ukQr66rPi
signature: 重庆远界大数据研究院
sdk-app-id: 1401018866
template-id: 2491776
config4:
# 质量工单逾期
supplier: tencent
access-key-id: AKIDb3JK5dx4wa0DCxWqvxlKejvysZ3ITVJv
access-key-secret: c5LPFsJI8k7GDxTkoeFj4A1ukQr66rPi
signature: 重庆远界大数据研究院
sdk-app-id: 1401018866
template-id: 2534747
config5:
# 设计图纸
supplier: tencent
access-key-id: AKIDb3JK5dx4wa0DCxWqvxlKejvysZ3ITVJv
access-key-secret: c5LPFsJI8k7GDxTkoeFj4A1ukQr66rPi
signature: 重庆远界大数据研究院
sdk-app-id: 1401018866
template-id: 2534750
config6:
# 安全工单
supplier: tencent
access-key-id: AKIDb3JK5dx4wa0DCxWqvxlKejvysZ3ITVJv
access-key-secret: c5LPFsJI8k7GDxTkoeFj4A1ukQr66rPi
signature: 重庆远界大数据研究院
sdk-app-id: 1401018866
template-id: 2534848
--- # 三方授权
justauth:
@ -275,8 +318,11 @@ weather:
dxf2GeoJson:
file-name: main
ys7:
app-key: 3acf9f1a43dc4209841e0893003db0a2
app-secret: 4bbf3e9394f55d3af6e3af27b2d3db36
app-key: xxx
app-secret: xxx
job:
capture-enabled: false # 控制是否启用萤石抓拍任务
device-sync-enabled: false # 控制是否同步萤石设备
# 斯巴达算法
sparta:
url: http://119.3.204.120:8040
@ -286,10 +332,14 @@ sparta:
id-card:
encrypt-key: 7ae260d150a14027d2238a1cf80a48ef
recognizer:
url: http://192.168.110.5:50070
url: http://192.168.110.5:50071
qrCode:
url: http://xny.yj-3d.com:7171
url: http://xny.yj-3d.com:7788
# 无人机大图
drone:
url: http://192.168.110.2:9512
# 聊天服务
chat:
server:
port: 18088

View File

@ -1,3 +1,7 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8899
--- # 临时文件存储位置 避免临时文件被系统清理报错
spring.servlet.multipart.location: /ruoyi/server/temp
@ -35,6 +39,12 @@ snail-job:
--- # 数据源配置
spring:
ai:
dashscope:
api-key: xxx
chat:
options:
model: qwen-plus
datasource:
type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
@ -55,21 +65,21 @@ spring:
url: jdbc:mysql://192.168.110.2:13386/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: xinnengyuan
password: mEZPC5Sdf3r2HENi
# 从库数据源
slave:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgc
password: nWKDKRNRT48tFBdh
slave1:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgprod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgprod
password: MaY8nehwWkJriWPm
# # 从库数据源
# slave:
# lazy: true
# type: ${spring.datasource.type}
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: zmkgc
# password: nWKDKRNRT48tFBdh
# slave1:
# lazy: true
# type: ${spring.datasource.type}
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://192.168.110.2:13386/zmkgprod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: zmkgprod
# password: MaY8nehwWkJriWPm
# # 从库数据源
# slave:
# lazy: true
@ -326,7 +336,7 @@ ys7:
app-key: 3acf9f1a43dc4209841e0893003db0a2
app-secret: 09e29c70ae1161fbc3ce2030fc09ba2e
job:
capture-enabled: false # 控制是否启用萤石抓拍任务
capture-enabled: true # 控制是否启用萤石抓拍任务
device-sync-enabled: true # 控制是否同步萤石设备
# 斯巴达算法
sparta:
@ -344,3 +354,7 @@ qrCode:
# 无人机大图
drone:
url: http://192.168.110.2:9512
# 聊天服务
chat:
server:
port: 19099

View File

@ -22,7 +22,7 @@ captcha:
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8899
# port: 8899
servlet:
# 应用的访问路径
context-path: /
@ -106,6 +106,10 @@ sa-token:
is-share: false
# jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
# token有效期单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
active-timeout: 86400
# security配置
security:
@ -277,8 +281,10 @@ springdoc:
packages-to-scan: org.dromara.gps
- group: 24.招标模块
packages-to-scan: org.dromara.tender
- group: 25.app版本模块
packages-to-scan: org.dromara.app
# - group: 25.app版本模块
# packages-to-scan: org.dromara.app
- group: 25.数据迁移模块
packages-to-scan: org.dromara.transferData
- group: 26.netty消息模块
packages-to-scan: org.dromara.websocket
- group: 27.新中大模块
@ -298,6 +304,14 @@ xss:
- /system/notice
- /warm-flow/save-xml
- /project/project
- /xzd/contractDetails/**
- /xzd/contractChange/**
- /comprehensive/csContractChange/**
- /comprehensive/csContractInformation/**
- /hetongbiangeng/**
- /fenbaohetongbiangg/**
- /fenbaohetongxinxi/**
- /contractManagement/**
# 全局线程池相关配置
# 如使用JDK21请直接使用虚拟线程 不要开启此配置

View File

@ -5,7 +5,6 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -30,6 +29,8 @@ import org.dromara.manager.ys7manager.Ys7Constant;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.manager.ys7manager.vo.Ys7ResponseVo;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCreateByCapture;
import org.dromara.other.service.IOthYs7DeviceImgService;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.out.domain.OutConstructionValue;
import org.dromara.out.domain.OutConstructionValueRange;
@ -56,6 +57,8 @@ import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@ -113,6 +116,9 @@ public class DemoTest {
@Resource
private IOthYs7DeviceService ys7DeviceService;
@Resource
private IOthYs7DeviceImgService ys7DeviceImgService;
@Resource
private Ys7Manager ys7Manager;
@ -129,7 +135,7 @@ public class DemoTest {
log.info("执行定时任务:同步 {}至{} 计划详情到施工产值", lastMonday, lastSunday);
Boolean synced = progressPlanDetailService.syncPlanDetail2ConstructionValue(lastMonday, lastSunday, null);*/
LocalDate start = LocalDate.of(2024, 1, 1); // 起始时间2024-01-01
LocalDate end = LocalDate.of(2025, 10, 13); // 截止时间2025-09-15
LocalDate end = LocalDate.of(2025, 10, 20); // 截止时间2025-09-15
// 如果起始不是周一,调整到当周的周一
if (start.getDayOfWeek() != DayOfWeek.MONDAY) {
@ -149,7 +155,7 @@ public class DemoTest {
.ge(PgsProgressPlanDetail::getDate, monday)
.le(PgsProgressPlanDetail::getDate, sunday)
.ne(PgsProgressPlanDetail::getFinishedNumber, BigDecimal.ZERO)
.eq(PgsProgressPlanDetail::getStatus, "1")
// .eq(PgsProgressPlanDetail::getStatus, "1")
.list();
if (CollUtil.isEmpty(planDetailList)) {
// 下一周
@ -244,8 +250,8 @@ public class DemoTest {
value.setOutValue(constructionValue);
value.setOwnerValue(ownerValue);
// 统计总产值
allConstructionValue = allConstructionValue.add(constructionValue).setScale(4, RoundingMode.HALF_UP);
allOwnerValue = allOwnerValue.add(ownerValue).setScale(4, RoundingMode.HALF_UP);
allConstructionValue = allConstructionValue.add(constructionValue);
allOwnerValue = allOwnerValue.add(ownerValue);
// 添加需要修改状态的计划详情
PgsProgressPlanDetail update = new PgsProgressPlanDetail();
update.setId(planDetail.getId());
@ -253,8 +259,8 @@ public class DemoTest {
updateList.add(update);
saveList.add(value);
}
range.setOutValue(allConstructionValue);
range.setOwnerValue(allOwnerValue);
range.setOutValue(allConstructionValue.setScale(4, RoundingMode.HALF_UP));
range.setOwnerValue(allOwnerValue.setScale(4, RoundingMode.HALF_UP));
// 如果产值都为0则不保存
if (allConstructionValue.compareTo(BigDecimal.ZERO) == 0 && allOwnerValue.compareTo(BigDecimal.ZERO) == 0) {
return null;
@ -548,4 +554,38 @@ public class DemoTest {
}
}
@Test
void capturePic() {
List<String> urlList = List.of(
"http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/23/db8a379b456142459852b654f20d5f97.png",
"http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/23/913dbcf0f7244c8b878e84b5525bec4b.png",
"http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/23/2b904765f03f40b2ad0ddbf6ddeadb45.png"
);
for (String url : urlList) {
Pattern pattern = Pattern.compile(".*/device/img/([^/]+)/.*");
Matcher matcher = pattern.matcher(url);
OthYs7Device ys7Device = ys7DeviceService.lambdaQuery()
.eq(OthYs7Device::getDeviceSerial, "GA1730672")
.last("limit 1")
.one();
if (ys7Device == null) {
throw new ServiceException("设备不存在", HttpStatus.ERROR);
}
String deviceSerial = ys7Device.getDeviceSerial();
// 如果没有预置位,则直接对默认通道抓图
OthYs7DeviceImgCreateByCapture img = new OthYs7DeviceImgCreateByCapture();
img.setProjectId(ys7Device.getProjectId());
img.setDeviceSerial(deviceSerial);
img.setDeviceName(ys7Device.getDeviceName());
// String url = "http://xny.yj-3d.com:9000/xinnengyuan/ys7/device/img/GA1044315/2025-10-13_859fdfb7dde540608356f29cb9e3d63e.jpg";
// String url = "http://xny.yj-3d.com:9000/xinnengyuan/ys7/device/img/GA1044315/2025-10-12_2801707255b84004acb5fee2a75299b2.jpg";
img.setCreateTime(new Date());
img.setUrl(url);
log.info("图片:{},识别中", url);
ys7DeviceImgService.saveCapturePic(List.of(img));
}
}
}

View File

@ -23,7 +23,7 @@ public class RecognizerTest {
@Test
void test() {
RecognizeVo recognize = recognizerManager.recognize("http://xny.yj-3d.com:7363/file/tif/20250625160218orthophoto.png", List.of(RecognizerTypeEnum.SOLAR));
RecognizeVo recognize = recognizerManager.recognize("http://xny.yj-3d.com:7363/file/tif/20250625160218orthophoto.png", List.of(RecognizerTypeEnum.PANEL));
log.info("recognize: {}", recognize);
}
}

View File

@ -1,9 +1,17 @@
package org.dromara.test;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
import org.dromara.safety.service.IHseViolationRecordService;
import org.dromara.system.domain.SysMenu;
import org.dromara.system.domain.vo.SysMenuVo;
import org.dromara.system.mapper.SysMenuMapper;
import org.dromara.system.service.ISysMenuService;
import org.hamcrest.core.Is;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
@ -20,6 +28,9 @@ public class ViolationRecordTest {
@Resource
private IHseViolationRecordService violationRecordService;
@Autowired
private SysMenuMapper sysMenuMapper;
@Test
void test() {
List<HseViolationRecordCreateDto> list = new ArrayList<>();
@ -55,4 +66,26 @@ public class ViolationRecordTest {
list.add(dto5);
violationRecordService.insertByMonitor(list);
}
@Test
void contextLoads() {
SysMenuVo sysMenuVo = sysMenusByList(1972500768346673154L);
System.out.println(sysMenuVo.toString());
}
SysMenuVo sysMenusByList(Long id) {
SysMenuVo sysMenus = new SysMenuVo();
List<SysMenu> res = sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, id));
if (res != null && res.size() > 0 ) {
sysMenus.setChildren(MapstructUtils.convert(res, SysMenuVo.class));
res.forEach(sysMenu -> {
sysMenusByList(sysMenu.getMenuId());
});
}
return sysMenus;
}
}

View File

@ -0,0 +1,32 @@
package org.dromara.common.core.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class XzdCustomerSupplierVos implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
private Long id;
/**
* 类型1、供应商2、客户
*/
private String type;
/**
* 供应商-客户id
*/
private Long cSId;
}

View File

@ -0,0 +1,6 @@
package org.dromara.common.core.service;
public interface XzdCsContractInformationService {
String selectNameByIds(String ids);
}

View File

@ -0,0 +1,14 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.vo.XzdCustomerSupplierVos;
public interface XzdCustomerSupplierService {
/**
* 查询供应商-客户中间
*
* @param id 主键
* @return 供应商-客户中间
*/
XzdCustomerSupplierVos queryByIdone(Long id);
}

View File

@ -0,0 +1,8 @@
package org.dromara.common.core.service;
public interface XzdCustomerinformationService {
String selectNmaeByIds(String id);
String selectNmaeById(Long id);
}

View File

@ -0,0 +1,6 @@
package org.dromara.common.core.service;
public interface XzdJsCgJungonService {
String selectNameByIds(String ids);
}

View File

@ -0,0 +1,6 @@
package org.dromara.common.core.service;
public interface XzdProjectService {
String selectNmaeByIds(String ids);
}

View File

@ -0,0 +1,5 @@
package org.dromara.common.core.service;
public interface XzdPurchaseContractInformationService {
String selectNameByIds(String ids);
}

View File

@ -0,0 +1,9 @@
package org.dromara.common.core.service;
public interface XzdSupplierInfoService {
String selectNmaeByIds(String ids);
String selectNmaeById(Long id);
}

View File

@ -0,0 +1,6 @@
package org.dromara.common.core.service;
public interface XzdSupplierOpenBankService {
String selectNameByIds(String ids);
}

View File

@ -37,4 +37,36 @@ public interface TransConstant {
*/
String PROJECT_ID_TO_NAME = "project_id_to_name";
/**
* 客户id转名称
*/
String XZD_KHXX_ID_TO_NAME = "khxx_id_to_name";
// /**
// * 供应商id转名称
// */
// String XZD_KHXX_ID_TO_NAME = "gysxx_id_to_name";
/**
* 新中大项目id转名称
*/
String XZD_PROJECT_ID_TO_NAME = "xzd_project_id_to_name";
/**
* 采购合同id转名称
*/
String XZD_PURCHASE_CONTRACT_ID_TO_NAME = "xzd_purchase_contract_id_to_name";
/**
* 综合服务合同id转名称
*/
String XZD_CS_CONTRACT_INFORMATION_ID_TO_NAME = "xzd_cs_contract_information_id_to_name";
/**
* 结算-采购合同竣工结算id转名称
*/
String XZD_JS_CG_JUNGON_ID_TO_NAME = "xzd_js_cg_jungon_id_to_name";
/**
* 新中大供应商信息-开户银行id转银行名称
*/
String XZD_SUPPLIER_OPEN_BANK_ID_TO_NAME = "xzd_supplier_open_bank_id_to_name";
}

View File

@ -0,0 +1,26 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdCsContractInformationService;
import org.dromara.common.core.service.XzdPurchaseContractInformationService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
@TranslationType(type = TransConstant.XZD_CS_CONTRACT_INFORMATION_ID_TO_NAME)
public class XzdCsContractInformationImpl implements TranslationInterface<String> {
private final XzdCsContractInformationService xzdCsContractInformationService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdCsContractInformationService.selectNameByIds(ids);
} else if (key instanceof Long id) {
return xzdCsContractInformationService.selectNameByIds(id.toString());
}
return null;
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdCustomerinformationService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
@TranslationType(type = TransConstant.XZD_KHXX_ID_TO_NAME)
public class XzdCustomerinformationImpl implements TranslationInterface<String> {
private final XzdCustomerinformationService xzdCustomerinformationService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdCustomerinformationService.selectNmaeByIds(ids);
} else if (key instanceof Long id) {
return xzdCustomerinformationService.selectNmaeByIds(id.toString());
}
return null;
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdJsCgJungonService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
@TranslationType(type = TransConstant.XZD_JS_CG_JUNGON_ID_TO_NAME)
public class XzdJsCgJungonImpl implements TranslationInterface<String> {
private final XzdJsCgJungonService xzdJsCgJungonService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdJsCgJungonService.selectNameByIds(ids);
} else if (key instanceof Long id) {
return xzdJsCgJungonService.selectNameByIds(id.toString());
}
return null;
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdProjectService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
@TranslationType(type = TransConstant.XZD_PROJECT_ID_TO_NAME)
public class XzdProjectImpl implements TranslationInterface<String> {
private final XzdProjectService xzdProjectService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdProjectService.selectNmaeByIds(ids);
} else if (key instanceof Long id) {
return xzdProjectService.selectNmaeByIds(id.toString());
}
return null;
}
}

View File

@ -0,0 +1,26 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdPurchaseContractInformationService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
@TranslationType(type = TransConstant.XZD_PURCHASE_CONTRACT_ID_TO_NAME)
public class XzdPurchaseContractInformationImpl implements TranslationInterface<String> {
private final XzdPurchaseContractInformationService xzdCustomerinformationService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdCustomerinformationService.selectNameByIds(ids);
} else if (key instanceof Long id) {
return xzdCustomerinformationService.selectNameByIds(id.toString());
}
return null;
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdSupplierInfoService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
//@TranslationType(type = TransConstant.XZD_KHXX_ID_TO_NAME)
public class XzdSupplierInfoImpl implements TranslationInterface<String> {
private final XzdSupplierInfoService xzdSupplierInfoService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdSupplierInfoService.selectNmaeByIds(ids);
} else if (key instanceof Long id) {
return xzdSupplierInfoService.selectNmaeByIds(id.toString());
}
return null;
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.XzdSupplierOpenBankService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
@AllArgsConstructor
@TranslationType(type = TransConstant.XZD_SUPPLIER_OPEN_BANK_ID_TO_NAME)
public class XzdSupplierOpenBankImpl implements TranslationInterface<String> {
private final XzdSupplierOpenBankService xzdSupplierOpenBankService;
@Override
public String translation(Object key, String other) {
if (key instanceof String ids) {
return xzdSupplierOpenBankService.selectNameByIds(ids);
} else if (key instanceof Long id) {
return xzdSupplierOpenBankService.selectNameByIds(id.toString());
}
return null;
}
}

View File

@ -5,3 +5,10 @@ org.dromara.common.translation.core.impl.OssUrlTranslationImpl
org.dromara.common.translation.core.impl.UserNameTranslationImpl
org.dromara.common.translation.core.impl.NicknameTranslationImpl
org.dromara.common.translation.core.impl.ProjectNameTranslationImpl
org.dromara.common.translation.core.impl.XzdCustomerinformationImpl
org.dromara.common.translation.core.impl.XzdProjectImpl
org.dromara.common.translation.core.impl.XzdPurchaseContractInformationImpl
org.dromara.common.translation.core.impl.XzdSupplierInfoImpl
org.dromara.common.translation.core.impl.XzdJsCgJungonImpl
org.dromara.common.translation.core.impl.XzdCsContractInformationImpl
org.dromara.common.translation.core.impl.XzdSupplierOpenBankImpl

View File

@ -15,16 +15,31 @@
system系统模块
</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>1.0.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Java WebSocket 标准API -->
<!-- <dependency>-->
<!-- <groupId>javax.websocket</groupId>-->
<!-- <artifactId>javax.websocket-api</artifactId>-->
<!-- <version>1.1</version>-->
<!-- <scope>provided</scope>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>javax.websocket</groupId>-->
<!-- <artifactId>javax.websocket-api</artifactId>-->
<!-- <version>1.1</version>-->
<!-- <scope>provided</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
@ -36,18 +51,18 @@
</exclusions>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.drewnoakes</groupId>-->
<!-- <artifactId>metadata-extractor</artifactId>-->
<!-- <version>2.18.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.drewnoakes</groupId>-->
<!-- <artifactId>metadata-extractor</artifactId>-->
<!-- <version>2.18.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>technology.tabula</groupId>-->
<!-- <artifactId>tabula</artifactId>-->
<!-- <version>1.0.4</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>technology.tabula</groupId>-->
<!-- <artifactId>tabula</artifactId>-->
<!-- <version>1.0.4</version>-->
<!-- </dependency>-->
<!-- JSON解析FastJSON -->
@ -119,19 +134,18 @@
</dependency>
<!-- 支持中文字体 -->
<!-- <dependency>-->
<!-- <groupId>com.itextpdf</groupId>-->
<!-- <artifactId>itext-asian</artifactId>-->
<!-- <version>5.2.0</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; iText &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.itextpdf</groupId>-->
<!-- <artifactId>itextpdf</artifactId>-->
<!-- <version>5.5.13.3</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.itextpdf</groupId>-->
<!-- <artifactId>itext-asian</artifactId>-->
<!-- <version>5.2.0</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; iText &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.itextpdf</groupId>-->
<!-- <artifactId>itextpdf</artifactId>-->
<!-- <version>5.5.13.3</version>-->
<!-- </dependency>-->
<!-- ZXing -->
<dependency>
<groupId>com.google.zxing</groupId>
@ -280,6 +294,13 @@
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version> <!-- 最新版本可自行调整 -->
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,51 @@
package org.dromara.ai.controller;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
/**
* @author lilemy
* @date 2025-10-23 11:32
*/
@Validated
@RestController
@RequestMapping("/ai")
public class AIController {
private static final String DEFAULT_PROMPT = "你是一个博学的智能聊天助手,请根据用户提问回答!";
private final ChatClient dashScopeChatClient;
public AIController(ChatClient.Builder chatClientBuilder) {
this.dashScopeChatClient = chatClientBuilder
.defaultSystem(DEFAULT_PROMPT)
// 实现 Logger 的 Advisor
.defaultAdvisors(
new SimpleLoggerAdvisor()
)
// 设置 ChatClient 中 ChatModel 的 Options 参数
.defaultOptions(
DashScopeChatOptions.builder()
.withTopP(0.7)
.build()
)
.build();
}
/**
* ChatClient 流式调用
*/
@GetMapping("/stream/chat")
public Flux<String> streamChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?") String query, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return dashScopeChatClient.prompt(query).stream().content();
}
}

View File

@ -1,6 +1,5 @@
package org.dromara.bigscreen.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
@ -62,7 +61,7 @@ public class MoneyBigScreenController {
/**
* 查询项目位置列表
*/
@SaCheckPermission("money:bigScreen:projectGis")
// @SaCheckPermission("money:bigScreen:projectGis")
@GetMapping("/project/gis")
public R<List<BusProjectGisVo>> getProjectGis() {
return R.ok(moneyBigScreenService.getProjectGis());
@ -545,7 +544,7 @@ public class MoneyBigScreenController {
/**
* 查询项目天气
*/
@SaCheckPermission("project:bigScreen:weather")
// @SaCheckPermission("project:bigScreen:weather")
@GetMapping("/weather/{projectId}")
public R<List<WeatherVo>> getProjectWeather(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
@ -555,7 +554,7 @@ public class MoneyBigScreenController {
/**
* 查询项目安全天数
*/
@SaCheckPermission("project:bigScreen:safetyDay")
// @SaCheckPermission("project:bigScreen:safetyDay")
@GetMapping("/safetyDay/{projectId}")
public R<BusProjectSafetyDayVo> getProjectSafetyDay(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {

View File

@ -1,6 +1,5 @@
package org.dromara.bigscreen.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -10,10 +9,7 @@ import lombok.RequiredArgsConstructor;
import org.dromara.bigscreen.domain.dto.ProjectUpdateDto;
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
import org.dromara.bigscreen.domain.dto.Ys7DeviceUpdateReq;
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
import org.dromara.bigscreen.domain.vo.ProjectLandVo;
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
import org.dromara.bigscreen.domain.vo.ProjectSafetyInspectionVo;
import org.dromara.bigscreen.domain.vo.*;
import org.dromara.bigscreen.service.ProjectBigScreenService;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.domain.R;
@ -21,7 +17,12 @@ import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.facility.domain.bo.FacRedLineBo;
import org.dromara.facility.domain.vo.redline.FacRedLineVo;
import org.dromara.facility.service.IFacRedLineService;
import org.dromara.gps.domain.bo.GpsEquipmentBo;
import org.dromara.land.domain.BusLandBlock;
import org.dromara.land.domain.BusLandTransferLedger;
@ -34,6 +35,7 @@ import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectVo;
import org.dromara.project.domain.vo.projectnews.BusProjectNewsVo;
import org.dromara.project.service.IBusProjectNewsService;
import org.dromara.project.service.IBusProjectService;
@ -87,6 +89,8 @@ public class ProjectBigScreenController extends BaseController {
private final BusLandTransferLedgerSonServiceImpl busLandTransferLedgerSonService;
private final IFacRedLineService facRedLineService;
/**
* 查询项目土地统计
*/
@ -272,6 +276,10 @@ public class ProjectBigScreenController extends BaseController {
throw new ServiceException("更新云端萤石摄像头名称异常", HttpStatus.ERROR);
}
}
if (req.getLatitude() != null && req.getLongitude() != null) {
ys7Device.setLatitude(req.getLatitude());
ys7Device.setLongitude(req.getLongitude());
}
return toAjax(othYs7DeviceService.updateById(ys7Device));
}
@ -296,7 +304,7 @@ public class ProjectBigScreenController extends BaseController {
/**
* 查询GPS设备用户列表
*/
@SaCheckPermission("project:big:screen")
// @SaCheckPermission("project:big:screen")
@GetMapping("/getList")
public R<List<String>> getList(Long projectId) {
return R.ok(projectBigScreenService.getList(projectId));
@ -316,7 +324,7 @@ public class ProjectBigScreenController extends BaseController {
/**
* 查询地图项目分类
*/
@SaCheckPermission("project:big:screen")
// @SaCheckPermission("project:big:screen")
@GetMapping("/getProjectMapList")
public R<Map<String, Map<String, Map<String, String>>>> getProjectMapList() {
return R.ok(projectService.getProjectMapList());
@ -325,12 +333,28 @@ public class ProjectBigScreenController extends BaseController {
/**
* 查询地图项目分类
*/
@SaCheckPermission("project:big:screen")
// @SaCheckPermission("project:big:screen")
@PostMapping("/updatePosition")
public R<Void> updatePosition(@RequestBody ProjectUpdateDto dto) {
return toAjax(projectService.updatePosition(dto));
}
/**
* 获取项目信息
*/
@GetMapping("/getProjectInfo/{projectId}")
public R<ProjectInfo> getProjectInfo(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
BusProjectVo projectVo = projectService.queryById(projectId);
ProjectInfo info = new ProjectInfo();
info.setLat(projectVo.getLat());
info.setLng(projectVo.getLng());
info.setPlan(projectVo.getPlan());
info.setPosition(projectVo.getPosition());
info.setProjectId(projectVo.getId().toString());
return R.ok(info);
}
/**
* 查询项目地域分散图
@ -369,4 +393,12 @@ public class ProjectBigScreenController extends BaseController {
return R.ok(projectBigScreenService.getInfoData(req));
}
/**
* 查询设施-红线列表
*/
@GetMapping("/redLine/list")
public TableDataInfo<FacRedLineVo> list(FacRedLineBo bo, PageQuery pageQuery) {
return facRedLineService.queryPageList(bo, pageQuery);
}
}

View File

@ -5,6 +5,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author lilemy
@ -37,4 +38,13 @@ public class Ys7DeviceUpdateReq implements Serializable {
*/
private String remark;
/**
* 纬度精确到6位小数
*/
private BigDecimal latitude;
/**
* 经度精确到6位小数
*/
private BigDecimal longitude;
}

View File

@ -0,0 +1,42 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-22 15:00
*/
@Data
public class ProjectInfo implements Serializable {
@Serial
private static final long serialVersionUID = -8436930470114636496L;
/**
* 纬度
*/
private String lat;
/**
* 经度
*/
private String lng;
/**
* 计划
*/
private String plan;
/**
* 位置
*/
private String position;
/**
* 项目id
*/
private String projectId;
}

View File

@ -505,7 +505,7 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
gps.put("id", item.getClientId());
gps.put("userId", item.getUserId());
gps.put("label", item.getClientId());
gps.put("name", item.getDeviceName());
gps.put("name", item.getUserId() != null ? item.getUserName() : item.getClientId());
gps.put("type", "gps");
gps.put("lat", item.getLocLatitude());
gps.put("lng", item.getLocLongitude());

View File

@ -0,0 +1,65 @@
package org.dromara.common.enums;
import lombok.Data;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public enum AppUserTypeEnum {
SG("0", "施工人员", 2L),
BZZ("0", "施工人员(班组长)", 3L),
GL("1", "管理", 4L),
FB("2", "分包", 5L),
;
private final String type;
private final String value;
private final Long roleId;
AppUserTypeEnum(String type, String value,Long roleId) {
this.type = type;
this.value = value;
this.roleId = roleId;
}
public static final List<Long> ROLE_ID_LIST = Arrays.asList(SG.roleId, BZZ.roleId, FB.roleId, GL.roleId);
/**
* roleId获取枚举
*
* @param roleId 角色
* @return 枚举
*/
public static AppUserTypeEnum getByRoleId(Long roleId) {
for (AppUserTypeEnum value : AppUserTypeEnum.values()) {
if (value.getRoleId().equals(roleId)) {
return value;
}
}
return null;
}
/**
* type获取枚举
*
* @param type 类型
* @return 枚举
*/
public static AppUserTypeEnum getByType(String type) {
for (AppUserTypeEnum value : AppUserTypeEnum.values()) {
if (value.getType().equals(type)) {
return value;
}
}
return null;
}
}

View File

@ -1,12 +1,21 @@
package org.dromara.common.utils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.mobileAttendanceMachine.DeviceMessageSender;
import org.dromara.mobileAttendanceMachine.KqjEntity;
import org.dromara.project.domain.BusAttendanceMachine;
import org.dromara.project.service.IBusAttendanceMachineService;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@ -17,6 +26,16 @@ import java.util.List;
@Slf4j
public class AsyncUtil {
@Resource
@Lazy
private DeviceMessageSender deviceMessageSender;
@Resource
@Lazy
private ISysOssService ossService;
@Resource
@Lazy
private IBusAttendanceMachineService attendanceMachineService;
//发送短信
@Async
public void sendSms(List<String> mobileList, String config) {
@ -39,4 +58,27 @@ public class AsyncUtil {
}
//下发考勤人员
@Async
public void sendPersonnel(Long teamId, SubConstructionUser constructionUser) {
SysOssVo byId = ossService.getById(Long.valueOf(constructionUser.getFacePic()));
List<BusAttendanceMachine> list = attendanceMachineService.lambdaQuery().apply("FIND_IN_SET({0}, teams)", teamId).list();
for (BusAttendanceMachine machine : list) {
deviceMessageSender.sendPersonnelInformation(machine.getSn(), constructionUser.getSysUserId().toString(), constructionUser.getUserName(), byId.getUrl());
}
}
//删除考勤人员
@Async
public void deletePersonnel(SubConstructionUser constructionUser) {
List<BusAttendanceMachine> list = attendanceMachineService.lambdaQuery().apply("FIND_IN_SET({0}, teams)", constructionUser.getTeamId()).list();
for (BusAttendanceMachine machine : list) {
try {
deviceMessageSender.deleteUser(machine.getSn(), constructionUser.getSysUserId().toString());
} catch (Exception e) {
log.error("删除考勤人员异常", e);
}
}
}
}

View File

@ -0,0 +1,89 @@
package org.dromara.common.utils.attendance;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class FaceUtil {
private static final String FACE_URL = "http://192.168.110.5:1224";
/**
* 创建人脸记录 post
* @param name 姓名
* @param card 身份证
* @param path 人脸图片HTTP地址需可公开访问
*/
public static void createFaceRecord(String name, String card, String path) {
String url = FACE_URL+"/api/faces";
HashMap<String, Object> param = new HashMap<>() {{
put("name", name);
put("card", card);
put("path", path);
}};
HttpUtil.post(url, param);
}
/**
* 人脸检测
* @param path 图片HTTP地址需可公开访问
*/
public static Map<String, String> faceDetect(String path) {
String url = FACE_URL+"/api/faces/detect";
HashMap<String, Object> param = new HashMap<>() {{
put("path", path);
put("similarity_threshold", 0.8);
}};
//转成json
String post = HttpUtil.post(url, param);
Map<String, String> map = new HashMap<>();
// 遍历检测到的人脸数据
try {
// 解析返回的JSON数据
JSONObject response = JSON.parseObject(post);
JSONObject data = response.getJSONObject("data");
JSONArray detectedFaces = data.getJSONArray("detected_faces");
for (int i = 0; i < detectedFaces.size(); i++) {
JSONObject face = detectedFaces.getJSONObject(i);
JSONObject matchInfo = face.getJSONObject("match_info");
// 检查相似度是否大于等于阈值
double similarity = matchInfo.getDoubleValue("similarity");
double threshold = matchInfo.getDoubleValue("threshold");
if (similarity >= threshold) {
// 提取用户信息
JSONObject userInfo = face.getJSONObject("user_info");
String name = userInfo.getString("name");
String idCard = userInfo.getString("id_card");
map.put(idCard, name);
}
}
}catch (Exception e){
log.error("人脸检测失败",e);
}
return map;
}
public static void main(String[] args) {
// Map<String, String> map = faceDetect("http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/12/9688ce2474ad47e7bf59c641848cdf8f.jpg");
// System.out.println(map);
// createFaceRecord("石志强","513022111145632652","http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/12/9688ce2474ad47e7bf59c641848cdf8f.jpg");
}
}

View File

@ -364,10 +364,11 @@ public class SubConstructionUserController extends BaseController {
if (user.getEntryDate() != null) {
vo.setEntryDate(DateUtils.formatDateTime(user.getEntryDate()));
}
//0男 1女 2未知
String sex = vo.getSex();
if (sex != null && sex.equals("1")) {
if (sex != null && sex.equals("0")) {
vo.setSex("");
} else if (sex != null && sex.equals("2")) {
} else if (sex != null && sex.equals("1")) {
vo.setSex("");
} else {
vo.setSex("未知");

View File

@ -101,4 +101,7 @@ public class SubConstructionUserQueryReq implements Serializable {
*/
private String userId;
private String phone;
}

View File

@ -19,12 +19,14 @@ import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.enums.AppUserTypeEnum;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.exception.OssException;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.utils.AsyncUtil;
import org.dromara.common.utils.IdCardEncryptorUtil;
import org.dromara.common.utils.baiduUtil.BaiDuFace;
import org.dromara.common.utils.baiduUtil.BaiDuOCR;
@ -45,6 +47,7 @@ import org.dromara.contractor.mapper.SubConstructionUserMapper;
import org.dromara.contractor.service.ISubConstructionUserFileService;
import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.contractor.service.ISubContractorService;
import org.dromara.mobileAttendanceMachine.DeviceMessageSender;
import org.dromara.project.domain.*;
import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum;
import org.dromara.project.domain.enums.BusAttendanceCommuterEnum;
@ -150,6 +153,11 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
@Resource
private ISysRoleService roleService;
@Resource
private AsyncUtil asyncUtil;
/**
* 查询施工人员
*
@ -201,8 +209,10 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
LambdaQueryWrapper<SubConstructionUser> lqw = Wrappers.lambdaQuery();
// 从对象中取值
String userName = req.getUserName();
String phone = req.getPhone();
// 模糊查询
lqw.like(StringUtils.isNotBlank(phone), SubConstructionUser::getPhone, phone);
lqw.like(StringUtils.isNotBlank(userName), SubConstructionUser::getUserName, userName);
lqw.isNull(SubConstructionUser::getProjectId);
lqw.isNull(SubConstructionUser::getTeamId);
@ -276,7 +286,7 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
userRoleMapper.delete(Wrappers.<SysUserRole>lambdaQuery()
.eq(SysUserRole::getUserId, constructionUser.getSysUserId())
.eq(SysUserRole::getProjectId, dto.getProjectId())
.in(SysUserRole::getRoleId, Arrays.asList(2L, 3L))
.in(SysUserRole::getRoleId, AppUserTypeEnum.ROLE_ID_LIST)
);
//再添加分配角色
Long roleId = "0".equals(dto.getPostId()) ? 2L : 3L;
@ -289,6 +299,8 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
//强退
roleService.cleanOnlineUser(Collections.singletonList(constructionUser.getSysUserId()));
asyncUtil.sendPersonnel(dto.getTeamId(), constructionUser);
return i > 0;
}

View File

@ -6,7 +6,6 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
@ -17,6 +16,7 @@ import org.dromara.common.web.core.BaseController;
import org.dromara.ctr.domain.bo.CtrContractProgressSettlementBo;
import org.dromara.ctr.domain.dto.CtrContractProgressSettlementCreateReq;
import org.dromara.ctr.domain.dto.CtrContractProgressSettlementUpdateReq;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementTotalVo;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementVo;
import org.dromara.ctr.service.ICtrContractProgressSettlementService;
import org.springframework.validation.annotation.Validated;
@ -92,6 +92,15 @@ public class CtrContractProgressSettlementController extends BaseController {
return toAjax(ctrContractProgressSettlementService.updateByBo(req));
}
/**
* 查询金额合计
*/
@SaCheckPermission("ctr:contractProgressSettlement:query")
@GetMapping("/queryMoneyTotal")
public R<CtrContractProgressSettlementTotalVo> queryMoneyTotal(CtrContractProgressSettlementBo bo) {
return R.ok(ctrContractProgressSettlementService.queryMoneyTotal(bo));
}
/**
* 删除承包合同进度结算
*

View File

@ -29,6 +29,11 @@ public class CtrSubcontractProgressSettlement extends BaseEntity {
@TableId(value = "id")
private Long id;
/**
* 所属部门
*/
private Long deptId;
/**
* 单据编码
*/

View File

@ -60,6 +60,11 @@ public class CtrContractProgressSettlementBo extends BaseEntity {
@NotBlank(message = "合同编码不能为空", groups = {AddGroup.class, EditGroup.class})
private String contractCode;
/**
* 类型1付款 2收款
*/
private String type;
/**
* 合同名称
*/

View File

@ -31,12 +31,6 @@ public class CtrContractProgressSettlementCreateReq implements Serializable {
@NotNull(message = "所属部门不能为空")
private Long deptId;
/**
* 单据编码
*/
@NotBlank(message = "单据编码不能为空")
private String documentCode;
/**
* 标题
*/

View File

@ -24,10 +24,9 @@ public class CtrSubcontractProgressSettlementCreateReq implements Serializable {
private static final long serialVersionUID = 770553999547826460L;
/**
* 单据编码
* 所属部门
*/
@NotBlank(message = "单据编码不能为空")
private String documentCode;
private Long deptId;
/**
* 标题

View File

@ -0,0 +1,23 @@
package org.dromara.ctr.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author lilemy
* @date 2025-10-21 11:01
*/
@Data
public class CtrContractProgressSettlementTotalVo implements Serializable {
@Serial
private static final long serialVersionUID = 1537548228169684228L;
/**
* 总金额
*/
private BigDecimal moneyTotal;
}

View File

@ -41,6 +41,12 @@ public class CtrContractProgressSettlementVo implements Serializable {
@ExcelProperty(value = "所属部门")
private Long deptId;
/**
* 部门名称
*/
@Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
private String deptName;
/**
* 单据编码
*/
@ -114,6 +120,12 @@ public class CtrContractProgressSettlementVo implements Serializable {
@ExcelProperty(value = "结算单位(客户)")
private Long settlementUnit;
/**
* 结算单位名称
*/
@Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "settlementUnit")
private String settlementUnitName;
/**
* 本期结算金额
*/

View File

@ -4,6 +4,8 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.ctr.domain.CtrSubcontractProgressSettlement;
import java.io.Serial;
@ -45,6 +47,18 @@ public class CtrSubcontractProgressSettlementVo implements Serializable {
@ExcelProperty(value = "标题")
private String title;
/**
* 所属部门
*/
@ExcelProperty(value = "所属部门")
private Long deptId;
/**
* 部门名称
*/
@Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
private String deptName;
/**
* 单据日期
*/
@ -105,6 +119,12 @@ public class CtrSubcontractProgressSettlementVo implements Serializable {
@ExcelProperty(value = "结算单位")
private Long settlementUnit;
/**
* 结算单位名称
*/
@Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "settlementUnit")
private String settlementUnitName;
/**
* 合同金额
*/

View File

@ -7,6 +7,7 @@ import org.dromara.ctr.domain.CtrContractProgressSettlement;
import org.dromara.ctr.domain.bo.CtrContractProgressSettlementBo;
import org.dromara.ctr.domain.dto.CtrContractProgressSettlementCreateReq;
import org.dromara.ctr.domain.dto.CtrContractProgressSettlementUpdateReq;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementTotalVo;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementVo;
import java.util.Collection;
@ -45,6 +46,14 @@ public interface ICtrContractProgressSettlementService extends IService<CtrContr
*/
List<CtrContractProgressSettlementVo> queryList(CtrContractProgressSettlementBo bo);
/**
* 查询金额统计
*
* @param bo 查询条件
* @return 金额统计
*/
CtrContractProgressSettlementTotalVo queryMoneyTotal(CtrContractProgressSettlementBo bo);
/**
* 新增承包合同进度结算
*

View File

@ -1,6 +1,7 @@
package org.dromara.ctr.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -8,9 +9,11 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.ctr.domain.CtrContractProgressSettlement;
import org.dromara.ctr.domain.CtrContractProgressSettlementItem;
import org.dromara.ctr.domain.bo.CtrContractProgressSettlementBo;
@ -19,18 +22,22 @@ import org.dromara.ctr.domain.dto.CtrContractProgressSettlementItemCreateReq;
import org.dromara.ctr.domain.dto.CtrContractProgressSettlementItemUpdateReq;
import org.dromara.ctr.domain.dto.CtrContractProgressSettlementUpdateReq;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementItemVo;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementTotalVo;
import org.dromara.ctr.domain.vo.CtrContractProgressSettlementVo;
import org.dromara.ctr.mapper.CtrContractProgressSettlementMapper;
import org.dromara.ctr.service.ICtrContractProgressSettlementItemService;
import org.dromara.ctr.service.ICtrContractProgressSettlementService;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.service.ISysDeptService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -46,6 +53,8 @@ public class CtrContractProgressSettlementServiceImpl extends ServiceImpl<CtrCon
private final ICtrContractProgressSettlementItemService contractProgressSettlementItemService;
private final ISysDeptService deptService;
/**
* 查询承包合同进度结算
*
@ -97,11 +106,66 @@ public class CtrContractProgressSettlementServiceImpl extends ServiceImpl<CtrCon
return baseMapper.selectVoList(lqw);
}
/**
* 查询金额统计
*
* @param bo 查询条件
* @return 金额统计
*/
@Override
public CtrContractProgressSettlementTotalVo queryMoneyTotal(CtrContractProgressSettlementBo bo) {
LambdaQueryWrapper<CtrContractProgressSettlement> lqw = Wrappers.lambdaQuery();
Long deptId = bo.getDeptId();
String type = bo.getType();
if (ObjectUtils.isNotEmpty(deptId) && StringUtils.isNotBlank(type) && !LoginHelper.isSuperAdmin()) {
SysDeptVo sysDeptVo = deptService.selectDeptById(deptId);
List<Long> list = StringUtils.splitTo(sysDeptVo.getAncestors(), Convert::toLong);
if (list.size() == 2 && Objects.equals(type, "1")) {
lqw.eq(CtrContractProgressSettlement::getDeptId, deptId);
} else if (list.size() == 2 && Objects.equals(type, "2")) {
lqw.eq(CtrContractProgressSettlement::getSettlementUnit, deptId);
} else if (list.size() > 2 && Objects.equals(type, "1")) {
lqw.eq(CtrContractProgressSettlement::getDeptId, list.get(2));
} else if (list.size() > 2 && Objects.equals(type, "2")) {
lqw.eq(CtrContractProgressSettlement::getSettlementUnit, list.get(2));
}
}
List<CtrContractProgressSettlement> settlementList = this.list(lqw);
CtrContractProgressSettlementTotalVo vo = new CtrContractProgressSettlementTotalVo();
if (CollUtil.isEmpty(settlementList)) {
return vo;
}
if (Objects.equals(type, "1")) {
vo.setMoneyTotal(settlementList.stream().map(CtrContractProgressSettlement::getSettlementMoney)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add));
} else if (Objects.equals(type, "2")) {
vo.setMoneyTotal(settlementList.stream().map(CtrContractProgressSettlement::getDeductionMoney)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add));
}
return vo;
}
private LambdaQueryWrapper<CtrContractProgressSettlement> buildQueryWrapper(CtrContractProgressSettlementBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<CtrContractProgressSettlement> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(CtrContractProgressSettlement::getId);
lqw.eq(bo.getDeptId() != null, CtrContractProgressSettlement::getDeptId, bo.getDeptId());
Long deptId = bo.getDeptId();
String type = bo.getType();
if (ObjectUtils.isNotEmpty(deptId) && StringUtils.isNotBlank(type) && !LoginHelper.isSuperAdmin()) {
SysDeptVo sysDeptVo = deptService.selectDeptById(deptId);
List<Long> list = StringUtils.splitTo(sysDeptVo.getAncestors(), Convert::toLong);
if (list.size() == 2 && Objects.equals(type, "1")) {
lqw.eq(CtrContractProgressSettlement::getDeptId, deptId);
} else if (list.size() == 2 && Objects.equals(type, "2")) {
lqw.eq(CtrContractProgressSettlement::getSettlementUnit, deptId);
} else if (list.size() > 2 && Objects.equals(type, "1")) {
lqw.eq(CtrContractProgressSettlement::getDeptId, list.get(2));
} else if (list.size() > 2 && Objects.equals(type, "2")) {
lqw.eq(CtrContractProgressSettlement::getSettlementUnit, list.get(2));
}
}
lqw.like(StringUtils.isNotBlank(bo.getDocumentCode()), CtrContractProgressSettlement::getDocumentCode, bo.getDocumentCode());
lqw.eq(StringUtils.isNotBlank(bo.getTitle()), CtrContractProgressSettlement::getTitle, bo.getTitle());
lqw.eq(bo.getSettlementDate() != null, CtrContractProgressSettlement::getSettlementDate, bo.getSettlementDate());
@ -134,8 +198,25 @@ public class CtrContractProgressSettlementServiceImpl extends ServiceImpl<CtrCon
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(CtrContractProgressSettlementCreateReq req) {
CtrContractProgressSettlement add = MapstructUtils.convert(req, CtrContractProgressSettlement.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (Objects.equals(req.getDeptId(), req.getSettlementUnit())) {
throw new ServiceException("结算单位不能与收款单位相同");
}
// 生成唯一编号
LocalDate today = LocalDate.now();
Long userId = LoginHelper.getUserId();
boolean flag;
synchronized (userId.toString().intern()) {
LocalDateTime startOfDay = today.atStartOfDay();
LocalDateTime endOfDay = today.plusDays(1).atStartOfDay().minusNanos(1);
// 获取当天的最大编号
Long count = this.lambdaQuery()
.between(CtrContractProgressSettlement::getCreateTime, startOfDay, endOfDay)
.count();
String result = String.format("%03d", count + 1); // 3表示长度0表示补0
add.setDocumentCode(today.format(DateTimeFormatter.BASIC_ISO_DATE) + "-" + result);
validEntityBeforeSave(add);
flag = baseMapper.insert(add) > 0;
}
if (flag) {
Long id = add.getId();
List<CtrContractProgressSettlementItemCreateReq> inInventory = req.getInInventory();
@ -184,6 +265,9 @@ public class CtrContractProgressSettlementServiceImpl extends ServiceImpl<CtrCon
public Boolean updateByBo(CtrContractProgressSettlementUpdateReq req) {
CtrContractProgressSettlement update = MapstructUtils.convert(req, CtrContractProgressSettlement.class);
validEntityBeforeSave(update);
if (Objects.equals(req.getDeptId(), req.getSettlementUnit())) {
throw new ServiceException("结算单位不能与收款单位相同");
}
Long id = req.getId();
// 删除旧数据
List<CtrContractProgressSettlementItem> oldList = contractProgressSettlementItemService.lambdaQuery()

View File

@ -11,6 +11,7 @@ import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.ctr.domain.CtrSubcontractProgressSettlement;
import org.dromara.ctr.domain.CtrSubcontractProgressSettlementItem;
import org.dromara.ctr.domain.bo.CtrSubcontractProgressSettlementBo;
@ -26,6 +27,9 @@ import org.dromara.ctr.service.ICtrSubcontractProgressSettlementService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -128,8 +132,22 @@ public class CtrSubcontractProgressSettlementServiceImpl extends ServiceImpl<Ctr
@Override
public Boolean insertByBo(CtrSubcontractProgressSettlementCreateReq req) {
CtrSubcontractProgressSettlement add = MapstructUtils.convert(req, CtrSubcontractProgressSettlement.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
// 生成唯一编号
LocalDate today = LocalDate.now();
Long userId = LoginHelper.getUserId();
boolean flag;
synchronized (userId.toString().intern()) {
LocalDateTime startOfDay = today.atStartOfDay();
LocalDateTime endOfDay = today.plusDays(1).atStartOfDay().minusNanos(1);
// 获取当天的最大编号
Long count = this.lambdaQuery()
.between(CtrSubcontractProgressSettlement::getCreateTime, startOfDay, endOfDay)
.count();
String result = String.format("%03d", count + 1); // 3表示长度0表示补0
add.setDocumentCode(today.format(DateTimeFormatter.BASIC_ISO_DATE) + "-" + result);
validEntityBeforeSave(add);
flag = baseMapper.insert(add) > 0;
}
if (flag) {
Long id = add.getId();
List<CtrSubcontractProgressSettlementItemCreateReq> inInventory = req.getInInventory();

View File

@ -1,8 +1,15 @@
package org.dromara.design.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
@ -10,17 +17,17 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.design.domain.bo.ImportExcelFileReq;
import org.dromara.design.domain.bo.ObtainAllVersionNumbersReq;
import org.dromara.design.domain.bo.CoryObtainTheListReq;
import org.dromara.design.domain.bo.SheetListReq;
import org.dromara.design.domain.bo.*;
import org.dromara.design.domain.vo.*;
import org.dromara.design.exportUtil.bill.*;
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
/**
* 工程量清单版本
@ -103,6 +110,74 @@ public class BusBillofquantitiesVersionsController extends BaseController {
return R.ok(busBillofquantitiesVersionsService.obtainAllClassification(bo));
}
/**
* 导出工程量清单版本列表
*/
@Log(title = "工程量清单版本", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(String versions,Long projectId, HttpServletResponse response) throws IOException {
Map<String, List<BillOfQuantitiesExport>> sheetDataMap = busBillofquantitiesVersionsService.export(versions,projectId);
if (sheetDataMap.isEmpty()) {
response.getWriter().write("无数据可导出");
return;
}
// 2. 设置响应头(同上)
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
String fileName = URLEncoder.encode( "工程量清单", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + fileName + ".xlsx");
// 3. 构建Excel写入器
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
try {
// 4. 遍历每个Sheet分组创建Sheet并写入数据
for (Map.Entry<String, List<BillOfQuantitiesExport>> entry : sheetDataMap.entrySet()) {
String sheetName = entry.getKey();
List<BillOfQuantitiesExport> dataList = entry.getValue();
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为红色
headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, new WriteCellStyle());
// 定义Sheet同时注册标题处理器和冻结窗格处理器
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
.registerWriteHandler(new FreezePaneWriteHandler()) // 冻结前2行
.registerWriteHandler(new ColumnWidthWriteHandler()) // 调整列宽
.registerWriteHandler(new CustomRowStyleHandler()) // 新增:边框和标题样式
.registerWriteHandler(horizontalCellStyleStrategy)
.head(head(sheetName))
.build();
// 写入数据
excelWriter.write(dataList, writeSheet);
}
} finally {
excelWriter.finish();
}
}
private List<List<String>> head(String sheetName) {
List<List<String>> list = new ArrayList<List<String>>();
List<String> list1 = Arrays.asList(sheetName,"编号");
List<String> list2 = Arrays.asList(sheetName, "名称");
List<String> list3 = Arrays.asList(sheetName, "规格");
List<String> list4 = Arrays.asList(sheetName, "单位");
List<String> list5 = Arrays.asList(sheetName, "数量");
List<String> list6 = Arrays.asList(sheetName, "备注");
list.add(list1);
list.add(list2);
list.add(list3);
list.add(list4);
list.add(list5);
list.add(list6);
return list;
}
// /**
// * 导入物资设备清单

View File

@ -1,17 +1,40 @@
package org.dromara.design.controller;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.design.domain.BusDrawingreviewReceipts;
import org.dromara.design.domain.DesCollectFile;
import org.dromara.design.domain.DesDesignChange;
import org.dromara.design.domain.bo.DesUserBo;
import org.dromara.design.domain.dto.designchange.DesDesignExtendDetailDto;
import org.dromara.design.domain.vo.DesCollectFileWordVo;
import org.dromara.design.domain.vo.DesUserVo;
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
import org.dromara.design.service.IDesDesignChangeService;
import org.dromara.design.service.IDesUserService;
import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.vo.project.BusSubProjectVo;
import org.dromara.project.service.IBusProjectService;
import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysUserService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -44,6 +67,12 @@ public class BusDrawingreviewReceiptsController extends BaseController {
private final IDesUserService desUserService;
private final IDesDesignChangeService desDesignChangeService;
private final ISysDictDataService dictDataService;
private final ISysUserService userService;
/**
* 查询设计-图纸评审验证列表
*/
@ -132,4 +161,85 @@ public class BusDrawingreviewReceiptsController extends BaseController {
return R.ok(desUserService.queryList(bo));
}
private static final String TEMPLATE_PATH = "template/设计验证表.docx";
@PostMapping("/downloadWord")
public void generateDesignLeaderDoc(Long id, HttpServletResponse response) {
OutputStream outputStream = null;
try {
// 1. 调用Service生成目标模板的Word字节流
byte[] docBytes = generateDocBytes(id);
// 2. 配置响应头:确保前端正确下载(避免中文乱码、指定文件类型)
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流类型
// 下载文件名:格式为“[项目名]-设计负责人任命通知单.docx”此处用projectId拼接真实场景可从数据中获取项目名
String downloadFileName = URLEncoder.encode(
"设计验证表.docx",
"UTF-8"
);
response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
response.setContentLength(docBytes.length); // 设置响应体长度(优化下载体验)
// 3. 将Word字节流写入响应
outputStream = response.getOutputStream();
outputStream.write(docBytes);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 异常处理返回500错误状态码
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
// 关闭流,避免资源泄漏
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public byte[] generateDocBytes(Long id) throws Exception {
// -------------------------- 步骤1按projectId查询项目数据模拟真实业务 --------------------------
// 实际场景替换为数据库查询如调用DAO获取项目名称、负责人等
DesDesignChangeVo desDesignChangeVo = desDesignChangeService.queryById(id);
BusDrawingreviewReceipts receipts = busDrawingreviewReceiptsService.lambdaQuery()
.eq(BusDrawingreviewReceipts::getDrawingreviewId, id)
.last("limit 1")
.one();
DesDesignExtendDetailDto extendDetail = desDesignChangeVo.getExtendDetail();
String s = dictDataService.selectDictLabel("des_user_major", receipts.getProfessional());
String designerName = null;
if(StrUtil.isNotBlank(receipts.getDesigner())){
Long userId = Long.parseLong(receipts.getDesigner());
designerName= userService.queryNameById(userId);
}
Map<String, Object> placeholderData = new HashMap<>();
placeholderData.put("projectName", receipts.getProjectName());
placeholderData.put("subName",extendDetail.getSubName());
placeholderData.put("stage", receipts.getStage());
placeholderData.put("professionalName", s);
placeholderData.put("volume", receipts.getVolume());
placeholderData.put("designerName", designerName);
placeholderData.put("verificationOpinion", receipts.getVerificationOpinion());
placeholderData.put("executionOpinion", receipts.getExecutionOpinion());
// -------------------------- 步骤2用poi-tl加载目标模板并替换占位符 --------------------------
// 读取resources下的“设计项目负责人任命通知单.docx”模板
ClassPathResource templateResource = new ClassPathResource(TEMPLATE_PATH);
try (InputStream templateIs = templateResource.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
XWPFTemplate template = XWPFTemplate.compile(templateIs).render(placeholderData);
// -------------------------- 步骤3将生成的文档写入字节流 --------------------------
template.write(outputStream);
template.close(); // 关闭模板资源
return outputStream.toByteArray();
}
}
}

View File

@ -1,14 +1,31 @@
package org.dromara.design.controller;
import java.util.List;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.design.domain.DesCollectFile;
import org.dromara.design.domain.bo.DesCollectFileBo;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.design.domain.dto.designchange.DesDesignExtendDetailDto;
import org.dromara.design.domain.vo.DesCollectFileVo;
import org.dromara.design.domain.vo.DesCollectFileWordVo;
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
import org.dromara.design.service.IDesCollectFileService;
import org.dromara.project.domain.BusProject;
import org.dromara.project.service.IBusProjectService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -38,6 +55,8 @@ public class DesCollectFileController extends BaseController {
private final IDesCollectFileService desCollectFileService;
private final IBusProjectService projectService;
/**
* 查询收资文件列表
*/
@ -105,7 +124,6 @@ public class DesCollectFileController extends BaseController {
return toAjax(desCollectFileService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 上传资料文件
*/
@ -117,4 +135,93 @@ public class DesCollectFileController extends BaseController {
@NotNull(message = "请先选择项目")Long projectId) {
return toAjax(desCollectFileService.addFile(file, catalogueId, projectId));
}
@PostMapping("/exportZip")
public void exportZip(ExportDto dto, HttpServletResponse response) throws Exception {
desCollectFileService.exportAsZip(dto, response);
}
private static final String TEMPLATE_PATH = "template/设计输入资料清单及评审表.docx";
@PostMapping("/downloadWord")
public void generateDesignLeaderDoc(Long projectId, HttpServletResponse response) {
OutputStream outputStream = null;
try {
// 1. 调用Service生成目标模板的Word字节流
byte[] docBytes = generateDocBytes(projectId);
// 2. 配置响应头:确保前端正确下载(避免中文乱码、指定文件类型)
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流类型
// 下载文件名:格式为“[项目名]-设计负责人任命通知单.docx”此处用projectId拼接真实场景可从数据中获取项目名
String downloadFileName = URLEncoder.encode(
"设计输入资料清单及评审表.docx",
"UTF-8"
);
response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
response.setContentLength(docBytes.length); // 设置响应体长度(优化下载体验)
// 3. 将Word字节流写入响应
outputStream = response.getOutputStream();
outputStream.write(docBytes);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 异常处理返回500错误状态码
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
// 关闭流,避免资源泄漏
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public byte[] generateDocBytes(Long projectId) throws Exception {
// -------------------------- 步骤1按projectId查询项目数据模拟真实业务 --------------------------
// 实际场景替换为数据库查询如调用DAO获取项目名称、负责人等
List<DesCollectFile> list = desCollectFileService.lambdaQuery()
.eq(DesCollectFile::getProjectId, projectId).list();
List<DesCollectFileWordVo> files = new ArrayList<>();
int i = 1;
for (DesCollectFile desCollectFile : list) {
DesCollectFileWordVo desCollectFileWordVo = new DesCollectFileWordVo();
desCollectFileWordVo.setNum(i);
i++;
desCollectFileWordVo.setFileName(desCollectFile.getFileName());
files.add(desCollectFileWordVo);
}
BusProject project = projectService.getById(projectId);
Map<String, Object> placeholderData = new HashMap<>();
placeholderData.put("projectName", project.getProjectName());
placeholderData.put("files",files);
// -------------------------- 步骤2用poi-tl加载目标模板并替换占位符 --------------------------
// 读取resources下的“设计项目负责人任命通知单.docx”模板
ClassPathResource templateResource = new ClassPathResource(TEMPLATE_PATH);
try (InputStream templateIs = templateResource.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
Configure config = Configure.builder()
.bind("files", policy).build();
XWPFTemplate template = XWPFTemplate.compile(templateIs, config).render(placeholderData);
// -------------------------- 步骤3将生成的文档写入字节流 --------------------------
template.write(outputStream);
template.close(); // 关闭模板资源
return outputStream.toByteArray();
}
}
}

View File

@ -0,0 +1,342 @@
package org.dromara.design.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.apache.poi.xwpf.usermodel.*;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.design.domain.DesConstructionSchedulePlan;
import org.dromara.design.domain.DesUser;
import org.dromara.design.domain.dto.constructionscheduleplan.*;
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
import org.dromara.design.exportUtil.plan.AttachmentPersonnel;
import org.dromara.design.service.IDesConstructionSchedulePlanService;
import org.dromara.design.service.IDesUserService;
import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysDictTypeService;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* 设计计划
*
* @author lilemy
* @date 2025-08-01
*/
@Validated
@RestController
@RequestMapping("/design/constructionSchedulePlan")
public class DesConstructionSchedulePlanController extends BaseController {
@Resource
private IDesConstructionSchedulePlanService desConstructionSchedulePlanService;
@Resource
private IDesUserService desUserService;
@Resource
private ISysDictTypeService dictTypeService;
/**
* 查询设计计划列表
*/
@SaCheckPermission("design:constructionSchedulePlan:list")
@GetMapping("/list")
public R<List<DesConstructionSchedulePlanVo>> list(DesConstructionSchedulePlanQueryReq req) {
List<DesConstructionSchedulePlanVo> list = desConstructionSchedulePlanService.queryList(req);
return R.ok(list);
}
/**
* 导出设计计划列表
*/
@SaCheckPermission("design:constructionSchedulePlan:export")
@Log(title = "施工进度计划", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(DesConstructionSchedulePlanQueryReq req, HttpServletResponse response) {
List<DesConstructionSchedulePlanVo> list = desConstructionSchedulePlanService.queryList(req);
ExcelUtil.exportExcel(list, "施工进度计划", DesConstructionSchedulePlanVo.class, response);
}
/**
* 根据项目id导出设计计划模版
*/
@SaCheckPermission("design:constructionSchedulePlan:exportTemplate")
@Log(title = "施工进度计划", businessType = BusinessType.EXPORT)
@PostMapping("/exportTemplate/{projectId}")
public void exportExcelByProjectId(@NotNull(message = "项目id不能为空")
@PathVariable Long projectId, HttpServletResponse response) {
desConstructionSchedulePlanService.exportExcelByProjectId(projectId, response);
}
/**
* 读取设计计划模版
*/
@SaCheckPermission("design:constructionSchedulePlan:readTemplate")
@Log(title = "施工进度计划", businessType = BusinessType.IMPORT)
@PostMapping("/readTemplate")
public R<Void> readExcel(@RequestParam("file") MultipartFile file, Long projectId) {
List<DesConstructionSchedulePlanExcelDto> list = desConstructionSchedulePlanService.readExcel(file, projectId);
if (CollUtil.isNotEmpty(list)) {
List<DesConstructionSchedulePlan> planList = desConstructionSchedulePlanService.convertToEntities(list);
return toAjax(desConstructionSchedulePlanService.saveBatch(planList));
}
return toAjax(true);
}
/**
* 获取设计计划详细信息
*
* @param id 主键
*/
@SaCheckPermission("design:constructionSchedulePlan:query")
@GetMapping("/{id}")
public R<DesConstructionSchedulePlanVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(desConstructionSchedulePlanService.queryById(id));
}
/**
* 新增设计计划
*/
@SaCheckPermission("design:constructionSchedulePlan:add")
@Log(title = "施工进度计划", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<DesConstructionSchedulePlanVo> add(@Validated @RequestBody DesConstructionSchedulePlanCreateReq req) {
return R.ok(desConstructionSchedulePlanService.insertByBo(req));
}
/**
* 修改设计计划
*/
@SaCheckPermission("design:constructionSchedulePlan:edit")
@Log(title = "施工进度计划", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody DesConstructionSchedulePlanUpdateReq req) {
return toAjax(desConstructionSchedulePlanService.updateByBo(req));
}
/**
* 修改设计计划为完成状态
*
*/
@SaCheckPermission("design:constructionSchedulePlan:editFinish")
@Log(title = "施工进度计划", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/finish")
public R<Void> editFinish(@Validated @RequestBody DesConstructionSchedulePlanFinishReq req) {
return toAjax(desConstructionSchedulePlanService.updateFinish(req));
}
/**
* 删除设计计划
*
* @param ids 主键串
*/
@SaCheckPermission("design:constructionSchedulePlan:remove")
@Log(title = "施工进度计划", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(desConstructionSchedulePlanService.deleteByIds(List.of(ids)));
}
@PostMapping("/exportSchedule")
public void exportSchedule(HttpServletResponse response, Long projectId) throws IOException {
desConstructionSchedulePlanService.exportSchedule(response,projectId);
}
private static final String TEMPLATE_RESOURCE_PATH = "template/CCCET-JL-CX-25设计计划表.docx";
// -------------------------- 2. 核心接口:修复单元格清空逻辑与数据填充 --------------------------
@PostMapping("/downloadWord")
public void fillCccetTemplate(Long projectId, HttpServletResponse response) {
// 1. 基础参数校验(避免空数据)
// 2. 读取resource中的模板+填充数据
try (
// 关键通过ClassPathResource读取resource/template下的模板
InputStream templateIs = new ClassPathResource(TEMPLATE_RESOURCE_PATH).getInputStream();
XWPFDocument doc = new XWPFDocument(templateIs); // 加载模板
OutputStream out = response.getOutputStream() // 响应流
) {
// --------------------------
// 步骤1填充第1页-主信息表索引0固定6列
// --------------------------
// XWPFTable mainTable = doc.getTables().get(0);
// // 工程名称第1行第0列
// if (mainTable.getRows().size() > 1 && mainTable.getRow(1).getCell(0) != null) {
// mainTable.getRow(1).getCell(0).setText(request.getProjectName() == null ? "" : request.getProjectName());
// }
// // 工程号第1行第1列
// if (mainTable.getRows().size() > 1 && mainTable.getRow(1).getCell(1) != null) {
// mainTable.getRow(1).getCell(1).setText(request.getProjectNo());
// }
// // 编制日期第2行第3列
// if (mainTable.getRows().size() > 2 && mainTable.getRow(2).getCell(3) != null) {
// mainTable.getRow(2).getCell(3).setText(request.getCompileDate() == null ? "" : request.getCompileDate());
// }
// // 编制人第3行第0列
// if (mainTable.getRows().size() > 3 && mainTable.getRow(3).getCell(0) != null) {
// mainTable.getRow(3).getCell(0).setText(request.getCompiler() == null ? "" : request.getCompiler());
// }
// // 批准人第3行第2列
// if (mainTable.getRows().size() > 3 && mainTable.getRow(3).getCell(2) != null) {
// mainTable.getRow(3).getCell(2).setText(request.getApprover() == null ? "" : request.getApprover());
// }
// // 设计阶段第3行第4列
// if (mainTable.getRows().size() > 3 && mainTable.getRow(3).getCell(4) != null) {
// mainTable.getRow(3).getCell(4).setText(request.getDesignStage() == null ? "" : request.getDesignStage());
// }
// // 设计规模第4行第0列
// if (mainTable.getRows().size() > 4 && mainTable.getRow(4).getCell(0) != null) {
// mainTable.getRow(4).getCell(0).setText(request.getDesignScale() == null ? "" : request.getDesignScale());
// }
// --------------------------
// 步骤2填充第3页-附件1人员配置表索引2固定11列
// --------------------------
XWPFTable staffTable = doc.getTables().get(2);
// 删除模板中附件1的空数据行保留第0行表头
while (staffTable.getRows().size() > 2) {
staffTable.removeRow(1);
}
List<AttachmentPersonnel> list = getPersonnelDataByProjectId(projectId);
for (AttachmentPersonnel staff : list) {
XWPFTableRow newRow = staffTable.createRow();
// 补全11列避免POI默认列数不足导致null
while (newRow.getTableCells().size() < 11) {
newRow.createCell();
}
// 按附件1列顺序填充
newRow.getCell(0).setText(staff.getProfessional() == null ? "" : staff.getProfessional());
newRow.getCell(1).setText(staff.getLeaderName() == null ? "" : staff.getLeaderName());
newRow.getCell(2).setText(staff.getLeaderTitle() == null ? "" : staff.getLeaderTitle());
newRow.getCell(3).setText(staff.getDesignerName() == null ? "" : staff.getDesignerName());
newRow.getCell(4).setText(staff.getDesignerTitle() == null ? "" : staff.getDesignerTitle());
newRow.getCell(5).setText(staff.getReviewerName() == null ? "" : staff.getReviewerName());
newRow.getCell(6).setText(staff.getReviewerTitle() == null ? "" : staff.getReviewerTitle());
newRow.getCell(7).setText(staff.getCheckerName() == null ? "" : staff.getCheckerName());
newRow.getCell(8).setText(staff.getCheckerTitle() == null ? "" : staff.getCheckerTitle());
newRow.getCell(9).setText(staff.getApproverName() == null ? "" : staff.getApproverName());
newRow.getCell(10).setText(staff.getApproverTitle() == null ? "" : staff.getApproverTitle());
}
// --------------------------
// 步骤3设置响应头触发前端下载
// --------------------------
response.setContentType("application/octet-stream");
// 文件名填充后_工程号_CCCET-JL-CX-25设计计划表.docx
String fileName = "CCCET-JL-CX-25设计计划表.docx";
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); // 允许前端获取文件名
// --------------------------
// 步骤4写出文件到前端
// --------------------------
doc.write(out);
out.flush();
} catch (Exception e) {
// 异常封装(前端可捕获具体错误)
throw new RuntimeException("CCCET-JL-CX-25模板填充失败" + e.getMessage());
}
}
/**
* 根据projectId获取数据仅针对CCCET-JL-CX-25设计计划表.docx附件1
*/
private List<AttachmentPersonnel> getPersonnelDataByProjectId(Long projectId) {
// 模拟数据库查询实际项目替换为真实Service调用
List<DesUser> userList = desUserService.list(Wrappers.<DesUser>lambdaQuery()
.eq(DesUser::getProjectId, projectId)
);
if (userList.isEmpty()) {
return Collections.emptyList();
}
// 专业字典映射(编码→名称)
List<SysDictDataVo> majorDict = dictTypeService.selectDictDataByType("des_user_major");
Map<String, String> majorMap = majorDict.stream()
.collect(Collectors.toMap(SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel));
// 按角色分组1-专业负责人2-设计人3-校审人4-审定人5-审核人)
DesUser leader = userList.stream().filter(u -> "1".equals(u.getUserType())).findFirst().orElse(null);
Map<String, List<DesUser>> designerMap = userList.stream()
.filter(u -> "2".equals(u.getUserType()))
.collect(Collectors.groupingBy(DesUser::getUserMajor));
Map<String, List<DesUser>> reviewerMap = userList.stream()
.filter(u -> "3".equals(u.getUserType()))
.collect(Collectors.groupingBy(DesUser::getUserMajor));
Map<String, List<DesUser>> checkerMap = userList.stream()
.filter(u -> "5".equals(u.getUserType()))
.collect(Collectors.groupingBy(DesUser::getUserMajor));
Map<String, List<DesUser>> approverMap = userList.stream()
.filter(u -> "4".equals(u.getUserType()))
.collect(Collectors.groupingBy(DesUser::getUserMajor));
// 构建附件1数据一个专业一行避免重复
List<AttachmentPersonnel> dataList = new ArrayList<>();
for (Map.Entry<String, List<DesUser>> entry : designerMap.entrySet()) {
String majorCode = entry.getKey();
String majorName = majorMap.getOrDefault(majorCode, majorCode);
List<DesUser> designers = entry.getValue();
// 获取对应专业的其他角色
DesUser reviewer = reviewerMap.getOrDefault(majorCode, Collections.emptyList()).stream().findFirst().orElse(null);
DesUser checker = checkerMap.getOrDefault(majorCode, Collections.emptyList()).stream().findFirst().orElse(null);
DesUser approver = approverMap.getOrDefault(majorCode, Collections.emptyList()).stream().findFirst().orElse(null);
// 封装数据(多个设计人用顿号分隔)
AttachmentPersonnel data = new AttachmentPersonnel();
data.setProfessional(majorName);
data.setLeaderName(leader != null ? leader.getUserName() : "");
//data.setLeadeTitle(leader != null ? leader.getUserTitle() : "");
data.setDesignerName(designers.stream().map(DesUser::getUserName).collect(Collectors.joining("")));
//data.setDesignerTitle(designers.stream().map(DesUser::getUserTitle).collect(Collectors.joining("、")));
data.setReviewerName(reviewer != null ? reviewer.getUserName() : "");
//data.setReviewerTitle(reviewer != null ? reviewer.getUserTitle() : "");
data.setCheckerName(checker != null ? checker.getUserName() : "");
//data.setCheckerTitle(checker != null ? checker.getUserTitle() : "");
data.setApproverName(approver != null ? approver.getUserName() : "");
//data.setApproverTitle(approver != null ? approver.getUserTitle() : "");
dataList.add(data);
}
return dataList;
}
}

View File

@ -2,6 +2,7 @@ package org.dromara.design.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.deepoove.poi.XWPFTemplate;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
@ -16,24 +17,36 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.design.domain.DesUser;
import org.dromara.design.domain.DesVolumeCatalog;
import org.dromara.design.domain.DesVolumeFile;
import org.dromara.design.domain.dto.designchange.DesDesignChangeCreateReq;
import org.dromara.design.domain.dto.designchange.DesDesignChangeQueryReq;
import org.dromara.design.domain.dto.designchange.DesDesignChangeUpdateReq;
import org.dromara.design.domain.dto.designchange.DesDesignExtendDetailDto;
import org.dromara.design.domain.dto.volumecatalog.DesVolumeCatalogQueryReq;
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
import org.dromara.design.domain.vo.volumecatalog.DesVolumeCatalogVo;
import org.dromara.design.service.IDesDesignChangeService;
import org.dromara.design.service.IDesVolumeCatalogService;
import org.dromara.design.service.IDesVolumeFileService;
import org.dromara.project.domain.BusProject;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysOssService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@ -56,6 +69,9 @@ public class DesDesignChangeController extends BaseController {
@Resource
private IDesVolumeFileService desVolumeFileService;
@Resource
private ISysDictDataService dictDataService;
/**
* 查询设计变更管理列表
*/
@ -146,4 +162,92 @@ public class DesDesignChangeController extends BaseController {
return R.ok(list);
}
private static final String TEMPLATE_PATH = "template/设计更改通知单.docx";
@PostMapping("/downloadWord")
public void generateDesignLeaderDoc(Long id, HttpServletResponse response) {
OutputStream outputStream = null;
try {
// 1. 调用Service生成目标模板的Word字节流
byte[] docBytes = generateDocBytes(id);
// 2. 配置响应头:确保前端正确下载(避免中文乱码、指定文件类型)
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流类型
// 下载文件名:格式为“[项目名]-设计负责人任命通知单.docx”此处用projectId拼接真实场景可从数据中获取项目名
String downloadFileName = URLEncoder.encode(
"设计更改通知单.docx",
"UTF-8"
);
response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
response.setContentLength(docBytes.length); // 设置响应体长度(优化下载体验)
// 3. 将Word字节流写入响应
outputStream = response.getOutputStream();
outputStream.write(docBytes);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 异常处理返回500错误状态码
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
// 关闭流,避免资源泄漏
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public byte[] generateDocBytes(Long id) throws Exception {
// -------------------------- 步骤1按projectId查询项目数据模拟真实业务 --------------------------
// 实际场景替换为数据库查询如调用DAO获取项目名称、负责人等
DesDesignChangeVo vo = desDesignChangeService.queryById(id);
DesDesignExtendDetailDto extendDetail = vo.getExtendDetail()==null?new DesDesignExtendDetailDto():vo.getExtendDetail();
Map<String, Object> placeholderData = new HashMap<>();
placeholderData.put("projectName", vo.getProjectName());
placeholderData.put("designPhase",extendDetail.getDesignPhase());
placeholderData.put("subName",extendDetail.getSubName());
String s = dictDataService.selectDictLabel("des_user_major", vo.getSpecialty());
placeholderData.put("specialty",s);
placeholderData.put("volumeNo",vo.getVolumeNo());
String changeReason = vo.getChangeReason();
List<String> reasons = Arrays.asList("设计漏项", "设计改进", "设计差错", "接口差错",
"业主要求", "施工承包商要求", "外部资料与最终情况不符", "材料代用或其他");
String reason = reasons.stream()
.map(item -> changeReason.contains(String.valueOf(reasons.indexOf(item) + 1)) ? "" + item : "" + item)
.collect(Collectors.joining());
placeholderData.put("changeReason", reason);
placeholderData.put("designDisposal1", "1".equals(extendDetail.getDesignDisposal())?"" : "");
placeholderData.put("designDisposal2", "2".equals(extendDetail.getDesignDisposal())?"" : "");
placeholderData.put("designDisposal3", "3".equals(extendDetail.getDesignDisposal())?"" : "");
placeholderData.put("changeContent",vo.getChangeContent());
placeholderData.put("changeCategory1", "1".equals(extendDetail.getChangeCategory())?"" : "");
placeholderData.put("changeCategory2", "2".equals(extendDetail.getChangeCategory())?"" : "");
placeholderData.put("involvingProfessions", extendDetail.getInvolvingProfessions());
// -------------------------- 步骤2用poi-tl加载目标模板并替换占位符 --------------------------
// 读取resources下的“设计项目负责人任命通知单.docx”模板
ClassPathResource templateResource = new ClassPathResource(TEMPLATE_PATH);
try (InputStream templateIs = templateResource.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// 1. 加载模板 2. 注入替换数据 3. 渲染生成新文档
XWPFTemplate template = XWPFTemplate.compile(templateIs)
.render(placeholderData); // 自动匹配{{变量名}}占位符
// -------------------------- 步骤3将生成的文档写入字节流 --------------------------
template.write(outputStream);
template.close(); // 关闭模板资源
return outputStream.toByteArray();
}
}
}

View File

@ -129,6 +129,7 @@ public class DesExtractController extends BaseController {
@SaCheckPermission("design:extract:userMajor")
@GetMapping("/userMajor")
public R<List<DesUserVo>> selectUserMajor(DesUserBo bo) {
bo.setUserType("1");
return R.ok( deUserService.queryList(bo));
}

View File

@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.design.domain.dto.ExportDto;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -128,4 +129,9 @@ public class DesPrelimSchemeController extends BaseController {
return toAjax(desPrelimSchemeService.updateFile(file, projectId,id));
}
@PostMapping("/exportZipWithStatus")
public void exportZipWithStatus(ExportDto dto, HttpServletResponse response) throws Exception {
desPrelimSchemeService.exportAsZipWithStatusPrefix(dto, response);
}
}

View File

@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.design.domain.dto.ExportDto;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -126,4 +127,10 @@ public class DesSchemeController extends BaseController {
public R<Void> updateFile(MultipartFile file, Long projectId, @NotNull(message = "主键不能为空")@PathVariable Long id) {
return toAjax(desSchemeService.updateFile(file, projectId,id));
}
@PostMapping("/exportZipWithStatus")
public void exportZipWithStatus(ExportDto dto, HttpServletResponse response) throws Exception {
desSchemeService.exportAsZipWithStatusPrefix(dto, response);
}
}

View File

@ -1,16 +1,18 @@
package org.dromara.design.controller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.rmi.ServerException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.*;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.deepoove.poi.XWPFTemplate;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
@ -22,8 +24,15 @@ import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//import org.dromara.design.domain.DesUserExcelData;
import org.dromara.design.domain.DesUser;
import org.dromara.design.domain.DesUserExcelData;
import org.dromara.design.domain.dto.desUser.DesUserBatchDto;
import org.dromara.project.domain.BusProject;
import org.dromara.project.service.IBusProjectService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -54,6 +63,7 @@ import org.springframework.web.multipart.MultipartFile;
public class DesUserController extends BaseController {
private final IDesUserService desUserService;
private final IBusProjectService projectService;
/**
* 查询设计人员列表
@ -142,5 +152,75 @@ public class DesUserController extends BaseController {
return toAjax(desUserService.batchAddOrUpdate(dto));
}
private static final String TEMPLATE_PATH = "template/设计项目负责人任命通知单.docx";
@PostMapping("/downloadWord")
public void generateDesignLeaderDoc( Long projectId,
HttpServletResponse response
) {
OutputStream outputStream = null;
try {
// 1. 调用Service生成目标模板的Word字节流
byte[] docBytes = generateDocBytes(projectId);
// 2. 配置响应头:确保前端正确下载(避免中文乱码、指定文件类型)
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流类型
// 下载文件名:格式为“[项目名]-设计负责人任命通知单.docx”此处用projectId拼接真实场景可从数据中获取项目名
String downloadFileName = URLEncoder.encode(
"设计负责人任命通知单.docx",
"UTF-8"
);
response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
response.setContentLength(docBytes.length); // 设置响应体长度(优化下载体验)
// 3. 将Word字节流写入响应
outputStream = response.getOutputStream();
outputStream.write(docBytes);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 异常处理返回500错误状态码
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
// 关闭流,避免资源泄漏
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public byte[] generateDocBytes(Long projectId) throws Exception {
// -------------------------- 步骤1按projectId查询项目数据模拟真实业务 --------------------------
// 实际场景替换为数据库查询如调用DAO获取项目名称、负责人等
BusProject byId = projectService.getById(projectId);
DesUser desUser = desUserService.lambdaQuery().eq(DesUser::getProjectId, projectId)
.eq(DesUser::getUserType, "1")
.last("limit 1").one();
Map<String, Object> placeholderData = new HashMap<>();
placeholderData.put("projectName", byId==null?"" :byId.getProjectName());
placeholderData.put("leaderName",desUser==null?"": desUser.getUserName());
// -------------------------- 步骤2用poi-tl加载目标模板并替换占位符 --------------------------
// 读取resources下的“设计项目负责人任命通知单.docx”模板
ClassPathResource templateResource = new ClassPathResource(TEMPLATE_PATH);
try (InputStream templateIs = templateResource.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// 1. 加载模板 2. 注入替换数据 3. 渲染生成新文档
XWPFTemplate template = XWPFTemplate.compile(templateIs)
.render(placeholderData); // 自动匹配{{变量名}}占位符
// -------------------------- 步骤3将生成的文档写入字节流 --------------------------
template.write(outputStream);
template.close(); // 关闭模板资源
return outputStream.toByteArray();
}
}
}

View File

@ -0,0 +1,87 @@
package org.dromara.design.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.time.LocalDate;
/**
* 施工进度计划对象 pgs_construction_schedule_plan
*
* @author lilemy
* @date 2025-08-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("des_construction_schedule_plan")
public class DesConstructionSchedulePlan extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 父ID
*/
private Long parentId;
/**
* 节点名称
*/
private String nodeName;
/**
* 对应项目结构
*/
private Long projectStructure;
/**
* 对应项目结构名称
*/
private String projectStructureName;
/**
* 预计开始时间
*/
private LocalDate planStartDate;
/**
* 预计结束时间
*/
private LocalDate planEndDate;
/**
* 实际开始时间
*/
private LocalDate practicalStartDate;
/**
* 实际结束时间
*/
private LocalDate practicalEndDate;
/**
* 状态
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,11 @@
package org.dromara.design.domain.dto;
import lombok.Data;
import java.util.List;
@Data
public class ExportDto {
private Long projectId;
private List<Long> ids;
}

View File

@ -0,0 +1,77 @@
package org.dromara.design.domain.dto.constructionscheduleplan;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* @author lilemy
* @date 2025-08-01 14:05
*/
@Data
public class DesConstructionSchedulePlanCreateReq implements Serializable {
@Serial
private static final long serialVersionUID = 4060838737379600701L;
/**
* 项目ID
*/
@NotNull(message = "项目ID不能为空")
private Long projectId;
/**
* 父ID
*/
private Long parentId;
/**
* 节点名称
*/
@NotBlank(message = "节点名称不能为空")
private String nodeName;
/**
* 对应项目结构
*/
private Long projectStructure;
/**
* 对应项目结构名称
*/
private String projectStructureName;
/**
* 预计开始时间
*/
private LocalDate planStartDate;
/**
* 预计结束时间
*/
private LocalDate planEndDate;
/**
* 实际开始时间
*/
private LocalDate practicalStartDate;
/**
* 实际结束时间
*/
private LocalDate practicalEndDate;
/**
* 状态
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,71 @@
package org.dromara.design.domain.dto.constructionscheduleplan;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.time.LocalDate;
/**
* @author lilemy
* @date 2025-09-06 17:12
*/
@Data
@AllArgsConstructor
public class DesConstructionSchedulePlanExcelDto {
/**
* 编号1, 1.1, 1.1.1
*/
private String number;
/**
* 项目ID
*/
private Long projectId;
/**
* 节点名称
*/
private String nodeName;
/**
* 对应项目结构
*/
private Long projectStructure;
/**
* 对应项目结构名称
*/
private String projectStructureName;
/**
* 预计开始时间
*/
private LocalDate planStartDate;
/**
* 预计结束时间
*/
private LocalDate planEndDate;
/**
* 实际开始时间
*/
private LocalDate practicalStartDate;
/**
* 实际结束时间
*/
private LocalDate practicalEndDate;
/**
* 状态
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,31 @@
package org.dromara.design.domain.dto.constructionscheduleplan;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* @author lilemy
* @date 2025-09-17 10:15
*/
@Data
public class DesConstructionSchedulePlanFinishReq implements Serializable {
@Serial
private static final long serialVersionUID = 8139653508791280689L;
/**
* 主键
*/
@NotNull(message = "主键不能为空")
private Long id;
/**
* 完成时间
*/
@NotNull(message = "完成时间不能为空")
private LocalDate finishDate;
}

View File

@ -0,0 +1,37 @@
package org.dromara.design.domain.dto.constructionscheduleplan;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-08-01 14:05
*/
@Data
public class DesConstructionSchedulePlanQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = 9021370369055688811L;
/**
* 项目ID
*/
private Long projectId;
/**
* 节点名称
*/
private String nodeName;
/**
* 对应项目结构名称
*/
private String projectStructureName;
/**
* 状态
*/
private String status;
}

View File

@ -0,0 +1,74 @@
package org.dromara.design.domain.dto.constructionscheduleplan;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* @author lilemy
* @date 2025-08-01 14:06
*/
@Data
public class DesConstructionSchedulePlanUpdateReq implements Serializable {
@Serial
private static final long serialVersionUID = 6955873817030428268L;
/**
* 主键ID
*/
private Long id;
/**
* 节点名称
*/
private String nodeName;
/**
* 父ID
*/
private Long parentId;
/**
* 对应项目结构
*/
private Long projectStructure;
/**
* 对应项目结构名称
*/
private String projectStructureName;
/**
* 预计开始时间
*/
private LocalDate planStartDate;
/**
* 预计结束时间
*/
private LocalDate planEndDate;
/**
* 实际开始时间
*/
private LocalDate practicalStartDate;
/**
* 实际结束时间
*/
private LocalDate practicalEndDate;
/**
* 状态
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,15 @@
package org.dromara.design.domain.vo;
import lombok.Data;
@Data
public class DesCollectFileWordVo {
private Integer num;
private String fileName;
private String opinion;
}

View File

@ -0,0 +1,104 @@
package org.dromara.design.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.progress.domain.PgsConstructionSchedulePlan;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* 施工进度计划视图对象 pgs_construction_schedule_plan
*
* @author lilemy
* @date 2025-08-01
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = PgsConstructionSchedulePlan.class)
public class DesConstructionSchedulePlanVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
* 父ID
*/
@ExcelProperty(value = "父ID")
private Long parentId;
/**
* 节点名称
*/
@ExcelProperty(value = "节点名称")
private String nodeName;
/**
* 对应项目结构
*/
@ExcelProperty(value = "对应项目结构")
private Long projectStructure;
/**
* 对应项目结构名称
*/
@ExcelProperty(value = "对应项目结构名称")
private String projectStructureName;
/**
* 预计开始时间
*/
@ExcelProperty(value = "预计开始时间")
private LocalDate planStartDate;
/**
* 预计结束时间
*/
@ExcelProperty(value = "预计结束时间")
private LocalDate planEndDate;
/**
* 实际开始时间
*/
@ExcelProperty(value = "实际开始时间")
private LocalDate practicalStartDate;
/**
* 实际结束时间
*/
@ExcelProperty(value = "实际结束时间")
private LocalDate practicalEndDate;
/**
* 状态
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "project_construction_status")
private String status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@ -1,5 +1,7 @@
package org.dromara.design.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import org.dromara.design.domain.DesSubcontract;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
@ -31,13 +33,13 @@ public class DesSubcontractVo implements Serializable {
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
@ExcelIgnore
private Long id;
/**
* 项目id
*/
@ExcelProperty(value = "项目id")
@ExcelIgnore
private Long projectId;
/**
@ -49,7 +51,13 @@ public class DesSubcontractVo implements Serializable {
/**
* 分包要求
*/
@ExcelProperty(value = "说明")
private String requirement;
/**
* 创建时间
*/
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(value = "创建时间")
private Date createTime;
}

View File

@ -0,0 +1,33 @@
package org.dromara.design.exportUtil.bill;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.HeadStyle;
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
import lombok.Data;
import java.math.BigDecimal;
@Data
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 9)
public class BillOfQuantitiesExport {
@ExcelProperty(value = "编号", index = 0)
private String num;
@ExcelProperty(value = "名称及规格", index = 1)
private String name;
@ExcelProperty(value = "规格", index = 2)
private String specification;
@ExcelProperty(value = "单位", index = 3)
@NumberFormat("#") // 若单位为文本,可去掉此注解
private String unit;
@ExcelProperty(value = "数量", index = 4)
@NumberFormat("#,##0.00") // 数量格式化(保留两位小数)
private BigDecimal quantity;
@ExcelProperty(value = "备注", index = 5)
private String remark;
}

View File

@ -0,0 +1,23 @@
package org.dromara.design.exportUtil.bill;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Sheet;
/**
* 调整指定列的宽度(第二列和第五列)
*/
public class ColumnWidthWriteHandler implements SheetWriteHandler {
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
// 设置列宽单位字符数1字符≈256个单位这里按实际需求调整
// 第二列索引1例如设置宽度为30个字符
sheet.setColumnWidth(1, 30 * 256);
// 第五列索引4例如设置宽度为15个字符
sheet.setColumnWidth(4, 15 * 256);
}
}

View File

@ -0,0 +1,51 @@
package org.dromara.design.exportUtil.bill;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.*;
/**
* 行样式处理器:
* 1. 第二行索引1及以后所有行添加边框
* 2. 第二行索引1的第二列索引1标题栏加粗加大
*/
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.*;
public class CustomRowStyleHandler implements RowWriteHandler {
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
// 关键修改:通过 writeSheetHolder 获取 Workbook兼容所有版本
Workbook workbook = writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook();
int rowIndex = row.getRowNum();
// 1. 第二行索引1及以后的行添加边框
if (rowIndex >= 1) {
CellStyle borderStyle = ExcelStyleUtils.createBorderStyle(workbook);
for (int i = 0; i <= 5; i++) {
Cell cell = row.getCell(i);
if (cell == null) {
cell = row.createCell(i);
}
cell.setCellStyle(borderStyle);
}
// 2. 第二行索引1的第二列索引1标题栏加粗加大
if (rowIndex == 1) {
for (int i = 0; i <= 5; i++) {
Cell secondColumnCell = row.getCell(i);
if (secondColumnCell != null) {
CellStyle headerStyle = ExcelStyleUtils.createSecondColumnHeaderStyle(workbook);
secondColumnCell.setCellStyle(headerStyle);
}
}
}
}
}
}

View File

@ -0,0 +1,57 @@
package org.dromara.design.exportUtil.bill;
import org.apache.poi.ss.usermodel.*;
/**
* 样式工具类(定义边框、字体等样式)
*/
public class ExcelStyleUtils {
// 边框样式(细实线)
public static final BorderStyle BORDER_STYLE = BorderStyle.THIN;
// 边框颜色(黑色)
public static final short BORDER_COLOR = IndexedColors.BLACK.getIndex();
/**
* 创建带边框的单元格样式
*/
public static CellStyle createBorderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
// 上下左右边框
style.setBorderTop(BORDER_STYLE);
style.setBorderBottom(BORDER_STYLE);
style.setBorderLeft(BORDER_STYLE);
style.setBorderRight(BORDER_STYLE);
// 边框颜色
style.setTopBorderColor(BORDER_COLOR);
style.setBottomBorderColor(BORDER_COLOR);
style.setLeftBorderColor(BORDER_COLOR);
style.setRightBorderColor(BORDER_COLOR);
// 单元格内容居中(可选)
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
/**
* 创建第二列标题栏样式(加粗、字号加大)
*/
public static CellStyle createSecondColumnHeaderStyle(Workbook workbook) {
CellStyle style = createBorderStyle(workbook); // 继承边框样式
Font font = workbook.createFont();
font.setBold(true); // 加粗
font.setFontHeightInPoints((short) 12); // 字号加大默认11这里设12
style.setFont(font);
return style;
}
/**
* 创建第一行(合并标题行)的白色背景样式
*/
public static CellStyle createFirstRowWhiteStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
// 设置白色背景
style.setFillForegroundColor(IndexedColors.WHITE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 纯色填充
return style;
}
}

View File

@ -0,0 +1,20 @@
package org.dromara.design.exportUtil.bill;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Sheet;
/**
* 冻结前3行的处理器滚动时前3行固定
*/
public class FreezePaneWriteHandler implements SheetWriteHandler {
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
// 参数说明冻结0列冻结2行从第0列、第2行开始滚动
// 前2行索引0和1将固定在顶部
sheet.createFreezePane(0, 2, 0, 2);
}
}

View File

@ -0,0 +1,41 @@
package org.dromara.design.exportUtil.bill;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
public class SheetHeaderWriteHandler implements SheetWriteHandler {
private final String sheetName;
public SheetHeaderWriteHandler( String sheetName) {
this.sheetName = sheetName;
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
Workbook workbook = writeWorkbookHolder.getWorkbook();
// 第一行索引0合并标题 + 白色背景
Row titleRow = sheet.createRow(0);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue(sheetName);
// 应用白色背景样式
titleCell.setCellStyle(ExcelStyleUtils.createFirstRowWhiteStyle(workbook));
// 合并第一行的0-5列
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 5));
// 第2行索引1字段标题栏手动指定覆盖实体类默认表头
Row headerRow = sheet.createRow(1);
String[] headers = {"编号", "名称", "规格", "单位", "数量", "备注"};
for (int i = 0; i < headers.length; i++) {
headerRow.createCell(i).setCellValue(headers[i]);
}
}
}

View File

@ -0,0 +1,25 @@
package org.dromara.design.exportUtil.plan;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AttachmentPersonnel {
String professional; // 专业及人员分工
String leaderName; // 专业负责人姓名
String leaderTitle; // 专业负责人职称
String designerName; // 设计人姓名
String designerTitle; // 设计人职称
String reviewerName; // 校审人姓名
String reviewerTitle; // 校审人职称
String checkerName; // 审核人姓名
String checkerTitle; // 审核人职称
String approverName; // 审定人姓名
String approverTitle; // 审定人职称
}

View File

@ -0,0 +1,48 @@
package org.dromara.design.exportUtil.plan;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.converters.localdate.LocalDateStringConverter;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import java.time.LocalDate;
@Data
public class ConstructionScheduleExport {
@ExcelProperty(value = "编号", index = 0)
private String levelCode; // 层级编号如1、1.1、1.1.1
@ExcelProperty(value = "节点名称", index = 1)
private String nodeName;
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(value = "预计开始时间", index = 2)
private LocalDate planStartDate;
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(value = "预计结束时间", index = 3)
private LocalDate planEndDate;
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(value = "实际开始时间", index = 4)
private LocalDate practicalStartDate;
@DateTimeFormat("yyyy-MM-dd")
@ExcelProperty(value = "实际结束时间", index = 5)
private LocalDate practicalEndDate;
@ExcelProperty(value = "状态", index = 6,converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "project_construction_status")
private String status;
// 用于构建层级关系的临时字段(不导出)
@ExcelIgnore
private Long id;
@ExcelIgnore
private Long parentId;
}

View File

@ -0,0 +1,25 @@
package org.dromara.design.exportUtil.plan;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
public class ScheduleHeaderWriteHandler implements SheetWriteHandler {
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
// 第一行直接作为标题栏
Row titleRow = sheet.createRow(0);
String[] titles = {"编号", "节点名称", "预计开始时间", "预计结束时间", "实际开始时间", "实际结束时间", "状态"};
for (int i = 0; i < titles.length; i++) {
Cell cell = titleRow.createCell(i);
cell.setCellValue(titles[i]);
}
}
}

View File

@ -0,0 +1,17 @@
package org.dromara.design.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.design.domain.DesConstructionSchedulePlan;
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
import org.dromara.progress.domain.PgsConstructionSchedulePlan;
import org.dromara.progress.domain.vo.constructionscheduleplan.PgsConstructionSchedulePlanVo;
/**
* 施工进度计划Mapper接口
*
* @author lilemy
* @date 2025-08-01
*/
public interface DesConstructionSchedulePlanMapper extends BaseMapperPlus<DesConstructionSchedulePlan, DesConstructionSchedulePlanVo> {
}

View File

@ -3,14 +3,15 @@ package org.dromara.design.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.design.domain.BusBillofquantities;
import org.dromara.design.domain.BusBillofquantitiesVersions;
import org.dromara.design.domain.bo.*;
import org.dromara.design.domain.vo.*;
import org.dromara.design.exportUtil.bill.BillOfQuantitiesExport;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 工程量清单版本Service接口
@ -100,4 +101,7 @@ public interface IBusBillofquantitiesVersionsService extends IService<BusBillofq
List<BusBillofquantitiesMaterialTotalVo> queryMaterialTotalListByProject(Long projectId);
List<BusBillofquantitiesVo> obtainAllClassification(ObtainAllVersionNumbersReq bo);
Map<String, List<BillOfQuantitiesExport>> export(String versions,Long projectId);
}

View File

@ -1,12 +1,14 @@
package org.dromara.design.service;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.design.domain.DesCollectFile;
import org.dromara.design.domain.bo.DesCollectFileBo;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.design.domain.vo.DesCollectFileVo;
import org.springframework.web.multipart.MultipartFile;
@ -79,5 +81,6 @@ public interface IDesCollectFileService extends IService<DesCollectFile>{
Boolean addFile(MultipartFile file, Long catalogueId, Long projectId);
void exportAsZip(ExportDto dto, HttpServletResponse response) throws Exception;
}

View File

@ -0,0 +1,121 @@
package org.dromara.design.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.design.domain.DesConstructionSchedulePlan;
import org.dromara.design.domain.dto.constructionscheduleplan.*;
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
/**
* 施工进度计划Service接口
*
* @author lilemy
* @date 2025-08-01
*/
public interface IDesConstructionSchedulePlanService extends IService<DesConstructionSchedulePlan> {
/**
* 查询施工进度计划
*
* @param id 主键
* @return 施工进度计划
*/
DesConstructionSchedulePlanVo queryById(Long id);
/**
* 查询符合条件的施工进度计划列表
*
* @param req 查询条件
* @return 施工进度计划列表
*/
List<DesConstructionSchedulePlanVo> queryList(DesConstructionSchedulePlanQueryReq req);
/**
* 新增施工进度计划
*
* @param req 施工进度计划
* @return 新增施工进度计划封装
*/
DesConstructionSchedulePlanVo insertByBo(DesConstructionSchedulePlanCreateReq req);
/**
* 修改施工进度计划
*
* @param req 施工进度计划
* @return 是否修改成功
*/
Boolean updateByBo(DesConstructionSchedulePlanUpdateReq req);
/**
* 修改施工进度计划为完成状态
*
* @param req 施工进度计划
* @return 是否修改成功
*/
Boolean updateFinish(DesConstructionSchedulePlanFinishReq req);
/**
* 批量删除施工进度计划信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
Boolean deleteByIds(Collection<Long> ids);
/**
* 获取施工进度计划视图对象
*
* @param constructionSchedulePlan 施工进度计划对象
* @return 施工进度计划视图对象
*/
DesConstructionSchedulePlanVo getVo(DesConstructionSchedulePlan constructionSchedulePlan);
/**
* 获取施工进度计划查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
LambdaQueryWrapper<DesConstructionSchedulePlan> buildQueryWrapper(DesConstructionSchedulePlanQueryReq req);
/**
* 获取施工进度计划分页对象视图
*
* @param constructionSchedulePlanList 施工进度计划分页对象
* @return 施工进度计划分页对象视图
*/
List<DesConstructionSchedulePlanVo> getVoList(List<DesConstructionSchedulePlan> constructionSchedulePlanList);
/**
* 导出Excel
*
* @param projectId 项目id
*/
void exportExcelByProjectId(Long projectId, HttpServletResponse response);
/**
* 读取Excel文件
*
* @param file Excel文件
* @param projectId 项目ID
* @return 读取的数据列表
*/
List<DesConstructionSchedulePlanExcelDto> readExcel(MultipartFile file, Long projectId);
/**
* 将Excel数据转换为实体列表
*
* @param excelList Excel数据列表
* @return 实体列表
*/
List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList);
void exportSchedule(HttpServletResponse response, Long projectId) throws IOException;
}

View File

@ -1,6 +1,7 @@
package org.dromara.design.service;
import jakarta.validation.constraints.NotNull;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.design.domain.vo.DesPrelimSchemeVo;
import org.dromara.design.domain.bo.DesPrelimSchemeBo;
import org.dromara.design.domain.DesPrelimScheme;
@ -8,7 +9,6 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
@ -81,4 +81,7 @@ public interface IDesPrelimSchemeService extends IService<DesPrelimScheme>{
* 修改文件
*/
Boolean updateFile(MultipartFile file, Long projectId, Long id);
void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception;
}

View File

@ -1,7 +1,9 @@
package org.dromara.design.service;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.design.domain.vo.DesSchemeVo;
import org.dromara.design.domain.bo.DesSchemeBo;
import org.dromara.design.domain.DesScheme;
@ -82,4 +84,7 @@ public interface IDesSchemeService extends IService<DesScheme>{
* 修改文件
*/
Boolean updateFile(MultipartFile file, Long projectId, Long id);
void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception;
}

View File

@ -2,7 +2,7 @@ package org.dromara.design.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.util.BeanUtils;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -28,12 +28,11 @@ import org.dromara.common.excel.coryUtils.ExcelReader;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.utils.BatchNumberGenerator;
import org.dromara.common.utils.excel.ExcelDynamicReader;
import org.dromara.design.domain.BusBillofquantities;
import org.dromara.design.domain.BusBillofquantitiesVersions;
import org.dromara.design.domain.bo.*;
import org.dromara.design.domain.dto.MaterialsAndEquipmentExcelDto;
import org.dromara.design.domain.vo.*;
import org.dromara.design.exportUtil.bill.BillOfQuantitiesExport;
import org.dromara.design.mapper.BusBillofquantitiesVersionsMapper;
import org.dromara.design.service.IBusBillofquantitiesService;
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
@ -363,6 +362,35 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
return BeanUtil.copyToList(busBillofquantities,BusBillofquantitiesVo.class);
}
@Override
public Map<String, List<BillOfQuantitiesExport>> export(String versions,Long projectId) {
// 1. 从数据库查询指定版本的所有数据
List<BusBillofquantities> dbList = busBillofquantitiesService.list(Wrappers.<BusBillofquantities>lambdaQuery()
.eq(BusBillofquantities::getVersions, versions)
.eq(BusBillofquantities::getProjectId, projectId)
.orderByAsc(BusBillofquantities::getId)
);
if (CollectionUtil.isEmpty(dbList)) {
return Collections.emptyMap();
}
// 2. 转换为导出实体并按sheet分组
Map<String, List<BillOfQuantitiesExport>> sheetMap = new LinkedHashMap<>();
for (BusBillofquantities dbItem : dbList) {
BillOfQuantitiesExport exportItem = new BillOfQuantitiesExport();
BeanUtil.copyProperties(dbItem, exportItem);
// 处理编号、名称等字段(若需格式化可在此补充)
exportItem.setNum(dbItem.getNum());
exportItem.setName(dbItem.getName());
// 按sheet分组
String sheetName = dbItem.getSheet();
sheetMap.computeIfAbsent(sheetName, k -> new ArrayList<>()).add(exportItem);
}
return sheetMap;
}
/**
* 递归构建树形结构
*

View File

@ -2,8 +2,11 @@ package org.dromara.design.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
@ -18,10 +21,13 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.design.domain.DesCollect;
import org.dromara.design.domain.DesCollectCatalogue;
import org.dromara.design.domain.DesCollectFile;
import org.dromara.design.domain.bo.DesCollectFileBo;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.design.domain.vo.DesCollectFileVo;
import org.dromara.design.mapper.DesCollectFileMapper;
import org.dromara.design.service.IDesCollectCatalogueService;
import org.dromara.design.service.IDesCollectFileService;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
@ -31,10 +37,22 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
@ -53,6 +71,8 @@ public class DesCollectFileServiceImpl extends ServiceImpl<DesCollectFileMapper,
private final ISysOssService ossService;
private final IDesCollectCatalogueService collectCatalogueService;
/**
* 查询收资文件
*
@ -217,4 +237,144 @@ public class DesCollectFileServiceImpl extends ServiceImpl<DesCollectFileMapper,
log.info("监听删除流程事件,上传资料审核任务执行了{}", processDeleteEvent.toString());
}
@Override
public void exportAsZip(ExportDto dto, HttpServletResponse response) throws Exception {
// 1. 查询所有收资文件
List<DesCollectFile> files = baseMapper.selectList(Wrappers.<DesCollectFile>lambdaQuery()
.eq(DesCollectFile::getProjectId, dto.getProjectId())
.in(CollectionUtil.isNotEmpty(dto.getIds()),DesCollectFile::getId, dto.getIds())
);
if (files.isEmpty()) {
throw new RuntimeException("没有可导出的收资文件");
}
// 2. 提取所有catalogueId查询对应的目录名称
List<Long> catalogueIds = files.stream()
.map(DesCollectFile::getCatalogueId)
.distinct()
.collect(Collectors.toList());
Map<Long, String> catalogueNameMap = getCatalogueNames(catalogueIds); // 获取名称映射
// 3. 按catalogueId分组
Map<Long, List<DesCollectFile>> catalogueGroup = files.stream()
.collect(Collectors.groupingBy(DesCollectFile::getCatalogueId));
// 4. 创建临时根目录
File tempRootDir = File.createTempFile("collect_file_", "_temp");
if (!tempRootDir.delete() || !tempRootDir.mkdirs()) {
throw new RuntimeException("创建临时根目录失败");
}
try {
// 5. 按分组创建文件夹(用目录名称)并下载文件
for (Map.Entry<Long, List<DesCollectFile>> entry : catalogueGroup.entrySet()) {
Long catalogueId = entry.getKey();
List<DesCollectFile> fileList = entry.getValue();
// 获取目录名称若查询不到用catalogueId作为默认名称
String catalogueName = catalogueNameMap.getOrDefault(catalogueId, String.valueOf(catalogueId));
// 处理名称中的特殊字符(避免创建文件夹失败)
String safeCatalogueName = catalogueName.replaceAll("[\\\\/:*?\"<>|]", "_");
// 创建以目录名称命名的文件夹
File catalogueDir = new File(tempRootDir, safeCatalogueName);
if (!catalogueDir.exists() && !catalogueDir.mkdirs()) {
throw new RuntimeException("创建文件夹[" + safeCatalogueName + "]失败");
}
// 下载文件(命名格式:审核状态-原文件名)
for (DesCollectFile file : fileList) {
String newFileName = BusinessStatusEnum.getByStatus(file.getStatus()).getDesc() + "-" + file.getFileName();
File destFile = new File(catalogueDir, newFileName);
downloadFile(file.getFileUrl(), destFile); // 复用之前的下载方法
}
}
// 6. 压缩并导出(逻辑不变)
response.setContentType("application/zip");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
String zipFileName = URLEncoder.encode("收资文件汇总", StandardCharsets.UTF_8.name()) + ".zip";
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + zipFileName);
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
zipDirectory(tempRootDir, tempRootDir.getName(), zos);
}
} finally {
FileUtils.deleteQuietly(tempRootDir);
}
}
/**
* 查询catalogueId对应的目录名称从数据库获取
* @param catalogueIds 多个catalogueId
* @return key: catalogueId, value: 目录名称
*/
public Map<Long, String> getCatalogueNames(List<Long> catalogueIds) {
if (catalogueIds.isEmpty()) {
return Collections.emptyMap();
}
// 实际实现:从目录表查询,例如
List<DesCollectCatalogue> desCollectCatalogues = collectCatalogueService.listByIds(catalogueIds);
return desCollectCatalogues.stream()
.collect(Collectors.toMap(DesCollectCatalogue::getId, DesCollectCatalogue::getCatalogueName));
}
/**
* 下载文件Java 21兼容使用HttpClient替代URL
* @param fileUrl 文件URL远程地址或本地路径
* @param destFile 目标文件
*/
private void downloadFile(String fileUrl, File destFile) throws Exception {
// 远程URL文件使用HttpClient下载
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofMinutes(5))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(fileUrl))
.timeout(Duration.ofMinutes(10))
.GET()
.build();
HttpResponse<Path> response = client.send(
request,
HttpResponse.BodyHandlers.ofFile(destFile.toPath())
);
if (response.statusCode() != 200) {
throw new RuntimeException("文件下载失败:" + fileUrl + ",状态码:" + response.statusCode());
}
}
/**
* 递归压缩目录到ZIP流
* @param sourceDir 源目录
* @param baseName ZIP内的基础路径避免包含系统临时目录路径
* @param zos ZIP输出流
*/
private void zipDirectory(File sourceDir, String baseName, ZipOutputStream zos) throws IOException {
File[] files = sourceDir.listFiles();
if (files == null) return;
for (File file : files) {
String entryName = baseName + File.separator + file.getName();
if (file.isDirectory()) {
// 递归压缩子目录
zipDirectory(file, entryName, zos);
} else {
// 压缩文件
zos.putNextEntry(new ZipEntry(entryName));
try (InputStream in = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
}
}
}
}

View File

@ -0,0 +1,728 @@
package org.dromara.design.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.design.domain.DesConstructionSchedulePlan;
import org.dromara.design.domain.bo.DesUserBo;
import org.dromara.design.domain.dto.constructionscheduleplan.*;
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
import org.dromara.design.exportUtil.plan.ConstructionScheduleExport;
import org.dromara.design.exportUtil.plan.ScheduleHeaderWriteHandler;
import org.dromara.design.mapper.DesConstructionSchedulePlanMapper;
import org.dromara.design.service.IDesConstructionSchedulePlanService;
import org.dromara.project.service.IBusProjectService;
import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.domain.vo.SysUserExportVo;
import org.dromara.system.service.ISysDictDataService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
/**
* 施工进度计划Service业务层处理
*
* @author lilemy
* @date 2025-08-01
*/
@Slf4j
@Service
public class DesConstructionSchedulePlanServiceImpl extends ServiceImpl<DesConstructionSchedulePlanMapper, DesConstructionSchedulePlan>
implements IDesConstructionSchedulePlanService {
@Resource
private IBusProjectService projectService;
@Resource
private ISysDictDataService dictDataService;
/**
* 查询施工进度计划
*
* @param id 主键
* @return 施工进度计划
*/
@Override
public DesConstructionSchedulePlanVo queryById(Long id) {
DesConstructionSchedulePlan constructionSchedulePlan = this.getById(id);
if (constructionSchedulePlan == null) {
throw new ServiceException("施工进度计划信息不存在", HttpStatus.NOT_FOUND);
}
return this.getVo(constructionSchedulePlan);
}
/**
* 查询符合条件的施工进度计划列表
*
* @param req 查询条件
* @return 施工进度计划列表
*/
@Override
public List<DesConstructionSchedulePlanVo> queryList(DesConstructionSchedulePlanQueryReq req) {
List<DesConstructionSchedulePlan> result = this.list(this.buildQueryWrapper(req));
return this.getVoList(result);
}
/**
* 新增施工进度计划
*
* @param req 施工进度计划
* @return 新增施工进度计划封装
*/
@Override
public DesConstructionSchedulePlanVo insertByBo(DesConstructionSchedulePlanCreateReq req) {
DesConstructionSchedulePlan constructionSchedulePlan = new DesConstructionSchedulePlan();
BeanUtils.copyProperties(req, constructionSchedulePlan);
boolean save = this.save(constructionSchedulePlan);
if (!save) {
throw new ServiceException("新增施工进度计划信息异常", HttpStatus.ERROR);
}
DesConstructionSchedulePlan newConstructionSchedulePlan = this.getById(constructionSchedulePlan.getId());
return this.getVo(newConstructionSchedulePlan);
}
/**
* 修改施工进度计划
*
* @param req 施工进度计划
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(DesConstructionSchedulePlanUpdateReq req) {
DesConstructionSchedulePlan constructionSchedulePlan = new DesConstructionSchedulePlan();
BeanUtils.copyProperties(req, constructionSchedulePlan);
return this.updateById(constructionSchedulePlan);
}
/**
* 修改施工进度计划为完成状态
*
* @param req 施工进度计划
* @return 是否修改成功
*/
@Override
public Boolean updateFinish(DesConstructionSchedulePlanFinishReq req) {
DesConstructionSchedulePlan plan = this.getById(req.getId());
if (plan == null) {
throw new ServiceException("施工进度计划信息不存在", HttpStatus.NOT_FOUND);
}
if (plan.getStatus().equals("4")) {
throw new ServiceException("施工进度计划已完成", HttpStatus.NOT_FOUND);
}
LocalDate practicalStartDate = plan.getPracticalStartDate();
LocalDate finishDate = req.getFinishDate();
if (practicalStartDate == null) {
throw new ServiceException("请先填写实际开始时间", HttpStatus.NOT_FOUND);
}
if (finishDate.isBefore(practicalStartDate)) {
throw new ServiceException("实际结束时间不能早于实际开始时间", HttpStatus.NOT_FOUND);
}
plan.setStatus("4");
plan.setPracticalEndDate(finishDate);
return this.updateById(plan);
}
/**
* 批量删除施工进度计划信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteByIds(Collection<Long> ids) {
return this.removeBatchByIds(ids);
}
/**
* 获取施工进度计划视图对象
*
* @param constructionSchedulePlan 施工进度计划对象
* @return 施工进度计划视图对象
*/
@Override
public DesConstructionSchedulePlanVo getVo(DesConstructionSchedulePlan constructionSchedulePlan) {
DesConstructionSchedulePlanVo vo = new DesConstructionSchedulePlanVo();
if (constructionSchedulePlan == null) {
return vo;
}
BeanUtils.copyProperties(constructionSchedulePlan, vo);
return vo;
}
/**
* 获取施工进度计划查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
@Override
public LambdaQueryWrapper<DesConstructionSchedulePlan> buildQueryWrapper(DesConstructionSchedulePlanQueryReq req) {
LambdaQueryWrapper<DesConstructionSchedulePlan> lqw = new LambdaQueryWrapper<>();
if (req == null) {
return lqw;
}
Long projectId = req.getProjectId();
String nodeName = req.getNodeName();
String status = req.getStatus();
lqw.eq(ObjectUtils.isNotEmpty(projectId), DesConstructionSchedulePlan::getProjectId, projectId);
lqw.like(StringUtils.isNotBlank(nodeName), DesConstructionSchedulePlan::getNodeName, nodeName);
lqw.eq(StringUtils.isNotBlank(status), DesConstructionSchedulePlan::getStatus, status);
return lqw;
}
/**
* 获取施工进度计划分页对象视图
*
* @param constructionSchedulePlanList 施工进度计划分页对象
* @return 施工进度计划分页对象视图
*/
@Override
public List<DesConstructionSchedulePlanVo> getVoList(List<DesConstructionSchedulePlan> constructionSchedulePlanList) {
return constructionSchedulePlanList.stream().map(this::getVo).toList();
}
// region 导出 excel
/**
* 导出Excel
*
* @param projectId 项目id
*/
@Override
public void exportExcelByProjectId(Long projectId, HttpServletResponse response) {
DesUserBo desUserBo = new DesUserBo();
desUserBo.setProjectId(projectId);
Map<Long, String> projectStructureMap = projectService.getStructureAsList(projectId);
if (projectStructureMap.isEmpty()) {
throw new ServiceException("获取项目列表失败,项目为空!!!");
}
List<SysDictDataVo> dictDataVos = dictDataService.selectByDictType("project_construction_status");
if (dictDataVos == null || dictDataVos.isEmpty()) {
throw new ServiceException("项目施工状态为空!!");
}
Map<String, String> statusMap = new HashMap<>();
for (SysDictDataVo vo : dictDataVos) {
statusMap.put(vo.getDictValue(), vo.getDictLabel());
}
Workbook workbook = new XSSFWorkbook();
// 创建主 Sheet 和隐藏 Sheet
Sheet mainSheet = workbook.createSheet("设计里程碑计划模版");
Sheet dataSheet = workbook.createSheet("DropdownData");
workbook.setSheetHidden(workbook.getSheetIndex(dataSheet), true);
// 3. 创建单元格样式
CellStyle editableStyle = createEditableCellStyle(workbook); // 可编辑单元格样式
CellStyle protectedStyle = createProtectedCellStyle(workbook); // 受保护单元格样式ID列用
//填充隐藏数据Sheet
int rowIndex = 0;
// 填充项目关联结构A列和B列
for (Map.Entry<Long, String> entry : projectStructureMap.entrySet()) {
Row row = dataSheet.createRow(rowIndex++);
row.createCell(0).setCellValue(entry.getValue());
row.createCell(1).setCellValue(entry.getKey().toString());
}
// 重置行索引填充人员和人员IDC列和D列
rowIndex = 0;
for (Map.Entry<String, String> entry : statusMap.entrySet()) {
Row row = dataSheet.getRow(rowIndex);
if (row == null) {
row = dataSheet.createRow(rowIndex);
}
row.createCell(4).setCellValue(entry.getValue());
row.createCell(5).setCellValue(entry.getKey());
rowIndex++;
}
// 主 Sheet 设置表头
Row sheetRow = mainSheet.createRow(0);
String[] headers = {"编号格式11.11.1.1。用于进行父子结构关联)", "节点名称",
"预计开始时间输入格式2025-09-06", "预计结束时间", "实际开始时间", "实际结束时间", "状态", "状态编码", "备注"};
for (int i = 0; i < headers.length; i++) {
Cell cell = sheetRow.createCell(i);
cell.setCellValue(headers[i]);
if (i == 0) {
CellStyle css = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat();
css.setDataFormat(format.getFormat("@"));
cell.setCellStyle(css);
}
}
// 6. 设置专业下拉列表(第二列)
// setMajorDropdown(mainSheet, projectStructureMap.size());
setStatusDropdown(mainSheet, statusMap.size());
// String formulaTemplate = "IFERROR(INDEX(DropdownData!$B$1:$B$" + projectStructureMap.size() + ", MATCH(C{rowNum}, DropdownData!$A$1:$A$" + projectStructureMap.size() + ", 0)),\"\")";
String formulaTemplate1 = "IFERROR(INDEX(DropdownData!$F$1:$F$" + statusMap.size() + ", MATCH(G{rowNum}, DropdownData!$E$1:$E$" + statusMap.size() + ", 0)),\"\")";
for (int i = 1; i <= 1000; i++) { // 从第2行到101行
Row row = mainSheet.createRow(i);
int currentRowNum = i + 1; // Excel行号从1开始
// String formula = formulaTemplate.replace("{rowNum}", String.valueOf(currentRowNum));
//
// Cell cell = row.createCell(2);
// cell.setCellStyle(editableStyle); //专业不锁定
//
// Cell idCell = row.createCell(3);
// idCell.setCellFormula(formula);
// idCell.setCellStyle(protectedStyle); // 应用隐藏公式样式
String formula1 = formulaTemplate1.replace("{rowNum}", String.valueOf(currentRowNum));
Cell cell1 = row.createCell(6);
cell1.setCellStyle(editableStyle); //专业不锁定
Cell idCell1 = row.createCell(7);
idCell1.setCellFormula(formula1);
idCell1.setCellStyle(protectedStyle); // 应用隐藏公式样式
}
for (int i = 1; i <= 100; i++) {
Row row = mainSheet.getRow(i);
if (row == null) {
row = mainSheet.createRow(i);
}
Cell cell = row.createCell(0);
CellStyle textStyle = workbook.createCellStyle();
textStyle.setLocked(false);
DataFormat format = workbook.createDataFormat();
textStyle.setDataFormat(format.getFormat("@")); // "@" 表示文本格式
cell.setCellStyle(textStyle);
Cell cell1 = row.createCell(1);
cell1.setCellStyle(editableStyle);
Cell cell4 = row.createCell(2);
cell4.setCellStyle(editableStyle);
Cell cell5 = row.createCell(3);
cell5.setCellStyle(editableStyle);
Cell cell6 = row.createCell(4);
cell6.setCellStyle(editableStyle);
Cell cell7 = row.createCell(5);
cell7.setCellStyle(editableStyle);
Cell cell10 = row.createCell(8);
cell10.setCellStyle(editableStyle);
}
// 保护工作表,仅允许编辑未锁定的单元格
mainSheet.protectSheet("123456"); // 空密码
// 核心锁定表头第1行和前1列包含ID列
mainSheet.createFreezePane(0, 1, 0, 1);
// 调整列宽(更新列索引)
mainSheet.setColumnWidth(0, 20 * 320); // 编号
mainSheet.setColumnWidth(1, 20 * 280); // 节点名称
mainSheet.setColumnWidth(2, 20 * 320); // 预计开始时间
mainSheet.setColumnWidth(3, 20 * 200); // 预计结束时间
mainSheet.setColumnWidth(4, 20 * 200); // 实际开始时间
mainSheet.setColumnWidth(5, 20 * 200); // 实际结束时间
mainSheet.setColumnWidth(6, 20 * 100); // 状态
mainSheet.setColumnWidth(7, 20 * 200); // 状态编码
mainSheet.setColumnWidth(8, 20 * 200); // 备注
// 2. 设置响应头
// 设置响应头指定Excel格式和下载文件名
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("设计里程碑计划模版.xlsx", StandardCharsets.UTF_8));
// 直接写入响应输出流
try {
workbook.write(response.getOutputStream());
workbook.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 创建可编辑单元格样式
*/
private CellStyle createEditableCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setLocked(false); // 关键:允许编辑
return style;
}
/**
* 创建受保护单元格样式用于ID列
*/
private CellStyle createProtectedCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
DataFormat dataFormat = workbook.createDataFormat();
short formatIndex = dataFormat.getFormat("0"); // 匹配“自定义→0”格式
style.setDataFormat(formatIndex);
style.setHidden(true); // 隐藏公式
style.setLocked(true);
return style;
}
/**
* 设置专业下拉列表第二列索引1
*/
private void setMajorDropdown(Sheet mainSheet, int majorCount) {
DataValidationHelper helper = mainSheet.getDataValidationHelper();
// 专业数据范围数据Sheet的A列从第1行到专业数量行
String majorRange = "DropdownData!$A$1:$A$" + majorCount;
DataValidationConstraint constraint = helper.createFormulaListConstraint(majorRange);
// 作用范围第2行到100行第二列
CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, 2, 2);
DataValidation validation = helper.createValidation(constraint, addressList);
validation.setShowErrorBox(true);
mainSheet.addValidationData(validation);
}
/**
* 设置专业下拉列表第二列索引1
*/
private void setStatusDropdown(Sheet mainSheet, int majorCount) {
DataValidationHelper helper = mainSheet.getDataValidationHelper();
// 专业数据范围数据Sheet的A列从第1行到专业数量行
String majorRange = "DropdownData!$E$1:$E$" + majorCount;
DataValidationConstraint constraint = helper.createFormulaListConstraint(majorRange);
// 作用范围第2行到100行第二列
CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, 6, 6);
DataValidation validation = helper.createValidation(constraint, addressList);
validation.setShowErrorBox(true);
mainSheet.addValidationData(validation);
}
// endregion
/**
* 读取Excel文件
*
* @param file Excel文件
* @param projectId 项目ID
* @return 读取的数据列表
*/
@Override
public List<DesConstructionSchedulePlanExcelDto> readExcel(MultipartFile file, Long projectId) {
List<DesConstructionSchedulePlanExcelDto> dataList = new ArrayList<>();
try (InputStream inputStream = file.getInputStream();
XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
XSSFSheet sheet = workbook.getSheetAt(0);
// 从第二行(index=1)开始读取数据,跳过表头
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (hasValidData(row)) {
String number = getCellValue(row.getCell(0));
String nodeName = getCellValue(row.getCell(1));
String projectStructureName = null;
Long projectStructure = null;
LocalDate planStartDate = getLocalDateValue(row.getCell(2));
LocalDate planEndDate = getLocalDateValue(row.getCell(3));
LocalDate practicalStartDate = getLocalDateValue(row.getCell(4));
LocalDate practicalEndDate = getLocalDateValue(row.getCell(5));
String status = getCellValue(row.getCell(7));
String remark = getCellValue(row.getCell(8));
DesConstructionSchedulePlanExcelDto excelData = new DesConstructionSchedulePlanExcelDto(
number, projectId, nodeName, projectStructure, projectStructureName,
planStartDate, planEndDate, practicalStartDate, practicalEndDate, status, remark
);
dataList.add(excelData);
}
}
} catch (Exception e) {
log.error("读取表格数据失败", e);
throw new ServiceException("读取表格数据失败");
}
return dataList;
}
/**
* 将Excel数据转换为实体列表
*
* @param excelList Excel数据列表
* @return 实体列表
*/
@Override
public List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList) {
List<DesConstructionSchedulePlan> result = new ArrayList<>();
Map<String, Long> numberIdMap = new HashMap<>();
for (DesConstructionSchedulePlanExcelDto dto : excelList) {
DesConstructionSchedulePlan entity = new DesConstructionSchedulePlan();
LocalDate planStartDate = dto.getPlanStartDate();
LocalDate planEndDate = dto.getPlanEndDate();
if (planStartDate == null || planEndDate == null || planStartDate.isAfter(planEndDate)) {
throw new ServiceException("计划开始时间和计划结束时间不能为空,且计划开始时间不能大于计划结束时间", HttpStatus.BAD_REQUEST);
}
// 生成主键
Long id = IdWorker.getId();
entity.setId(id);
entity.setProjectId(dto.getProjectId());
entity.setNodeName(dto.getNodeName());
entity.setProjectStructure(dto.getProjectStructure());
entity.setProjectStructureName(dto.getProjectStructureName());
entity.setPlanStartDate(planStartDate);
entity.setPlanEndDate(planEndDate);
entity.setPracticalStartDate(dto.getPracticalStartDate());
entity.setPracticalEndDate(dto.getPracticalEndDate());
entity.setStatus(dto.getStatus());
entity.setRemark(dto.getRemark());
// 确定父ID
String number = dto.getNumber();
if (number != null && number.contains(".")) {
String parentNumber = number.substring(0, number.lastIndexOf("."));
Long parentId = numberIdMap.get(parentNumber);
if (parentId == null) {
throw new ServiceException("未找到父编号:" + parentNumber, HttpStatus.BAD_REQUEST);
}
entity.setParentId(parentId);
} else {
entity.setParentId(0L); // 顶级节点
}
// 保存当前编号对应的id
numberIdMap.put(number, id);
result.add(entity);
}
return result;
}
private static boolean hasValidData(Row row) {
// 遍历行中的所有单元格
for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) {
Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String cellValue = getCellValue(cell).trim();
// 只要有一个单元格有非空值,就认为是有效行
if (!cellValue.isEmpty()) {
return true;
}
}
return false;
}
private static String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
// 使用CellType枚举判断单元格类型POI 4.0+版本推荐方式)
CellType cellType = cell.getCellType();
// 对于公式单元格,获取其计算结果的类型
if (cellType == CellType.FORMULA) {
cellType = cell.getCachedFormulaResultType();
}
switch (cellType) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
return date.toString();
} else {
// 处理数字类型,避免科学计数法
// 处理数字,移除不必要的.0后缀
String numericValue = String.valueOf(cell.getNumericCellValue());
if (numericValue.endsWith(".0")) {
return numericValue.substring(0, numericValue.length() - 2);
}
return numericValue;
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
default:
return "";
}
}
private static LocalDate getLocalDateValue(Cell cell) {
if (cell == null) {
return null;
}
CellType cellType = cell.getCellType();
if (cellType == CellType.FORMULA) {
cellType = cell.getCachedFormulaResultType();
}
try {
if (cellType == CellType.NUMERIC) {
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} else {
// 如果是数字但不是日期,就尝试转为 LocalDate (例如 20250730)
double numericValue = cell.getNumericCellValue();
String text = String.valueOf((long) numericValue);
return LocalDate.parse(text);
}
} else if (cellType == CellType.STRING) {
String text = cell.getStringCellValue().trim();
if (text.isEmpty()) {
return null;
}
// 尝试解析不同格式
try {
return LocalDate.parse(text); // 默认 ISO 格式 yyyy-MM-dd
} catch (Exception e) {
// 如果 Excel 是 yyyy/MM/dd 或 yyyy.MM.dd可以额外处理
try {
return LocalDate.parse(text, java.time.format.DateTimeFormatter.ofPattern("yyyy/MM/dd"));
} catch (Exception ignored) {
}
try {
return LocalDate.parse(text, java.time.format.DateTimeFormatter.ofPattern("yyyy.MM.dd"));
} catch (Exception ignored) {
}
return null; // 不识别的格式就返回 null
}
}
} catch (Exception e) {
return null;
}
return null;
}
@Override
public void exportSchedule(HttpServletResponse response, Long projectId) throws IOException {
// 1. 构建导出数据(含层级编号)
List<ConstructionScheduleExport> dataList = buildExportData(projectId);
// 2. 设置响应头
ExcelUtil.exportExcel(dataList, "用户数据", ConstructionScheduleExport.class, response);
}
public List<ConstructionScheduleExport> buildExportData(Long projectId) {
// 1. 查询项目下所有里程碑节点
List<DesConstructionSchedulePlan> allNodes = baseMapper.selectList(Wrappers.<DesConstructionSchedulePlan>lambdaQuery()
.eq(DesConstructionSchedulePlan::getProjectId, projectId));
if (CollectionUtil.isEmpty(allNodes)) {
return new ArrayList<>();
}
// 2. 转换为导出模型并缓存ID映射
Map<Long, ConstructionScheduleExport> nodeMap = new HashMap<>();
List<ConstructionScheduleExport> exportList = new ArrayList<>();
for (DesConstructionSchedulePlan node : allNodes) {
ConstructionScheduleExport export = new ConstructionScheduleExport();
BeanUtils.copyProperties(node, export);
nodeMap.put(node.getId(), export);
exportList.add(export);
}
// 3. 构建树形结构并生成层级编号
List<ConstructionScheduleExport> rootNodes = new ArrayList<>();
for (ConstructionScheduleExport export : exportList) {
if (export.getParentId() == 0) { // 父ID为0的是根节点
rootNodes.add(export);
}
}
// 4. 递归生成层级编号根节点从1开始
int rootIndex = 1;
for (ConstructionScheduleExport root : rootNodes) {
root.setLevelCode(String.valueOf(rootIndex));
buildChildrenCode(root, nodeMap);
rootIndex++;
}
// 5. 按层级顺序排序确保1.1在1之后1.1.1在1.1之后)
return sortByLevelCode(exportList);
}
// 递归为子节点生成编号如父节点1 -> 子节点1.1、1.2
private void buildChildrenCode(ConstructionScheduleExport parent, Map<Long, ConstructionScheduleExport> nodeMap) {
List<ConstructionScheduleExport> children = nodeMap.values().stream()
.filter(node -> parent.getId().equals(node.getParentId()))
.collect(Collectors.toList());
int childIndex = 1;
for (ConstructionScheduleExport child : children) {
child.setLevelCode(parent.getLevelCode() + "." + childIndex);
buildChildrenCode(child, nodeMap); // 递归处理孙子节点
childIndex++;
}
}
// 按层级编号排序(字符串自然排序)
private List<ConstructionScheduleExport> sortByLevelCode(List<ConstructionScheduleExport> list) {
return list.stream()
.sorted(Comparator.comparing(ConstructionScheduleExport::getLevelCode))
.collect(Collectors.toList());
}
}

View File

@ -1,8 +1,11 @@
package org.dromara.design.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
@ -15,13 +18,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.design.domain.DesScheme;
import org.dromara.message.domain.MsgConfig;
import org.dromara.message.domain.bo.MsgNoticeBo;
import org.dromara.message.domain.dto.SendMsgDto;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.message.service.IMsgConfigService;
import org.dromara.message.service.IMsgNoticeService;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.context.event.EventListener;
@ -33,8 +32,18 @@ import org.dromara.design.mapper.DesPrelimSchemeMapper;
import org.dromara.design.service.IDesPrelimSchemeService;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
@ -189,6 +198,105 @@ public class DesPrelimSchemeServiceImpl extends ServiceImpl<DesPrelimSchemeMappe
return baseMapper.updateById(desPrelimScheme)>0;
}
/**
* 文件名添加审核状态前缀后压缩为ZIP导出
* @param response 响应对象
*/
@Override
public void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception {
// 1. 查询数据
List<DesPrelimScheme> schemes = baseMapper.selectList(Wrappers.<DesPrelimScheme>lambdaQuery()
.eq(DesPrelimScheme::getProjectId, dto.getProjectId())
.in(CollectionUtil.isNotEmpty(dto.getIds()), DesPrelimScheme::getId, dto.getIds())
);
if (schemes.isEmpty()) {
throw new RuntimeException("没有可导出的初步设计方案文件");
}
// 2. 创建临时目录(存放重命名后的文件)
File tempDir = File.createTempFile("prelim_scheme_", "_temp");
if (!tempDir.delete()) { // 删除临时文件,创建同名目录
throw new RuntimeException("创建临时目录失败");
}
if (!tempDir.mkdirs()) {
throw new RuntimeException("创建临时目录失败");
}
try {
// 3. 下载文件并以“审核状态-原文件名”命名
for (DesPrelimScheme scheme : schemes) {
String status = BusinessStatusEnum.getByStatus(scheme.getStatus()).getDesc(); // 审核状态如draft、approved
String originalFileName = scheme.getFileName(); // 原文件名
String newFileName = status + "-" + originalFileName; // 新文件名:状态-原文件名
// 临时文件路径
File tempFile = new File(tempDir, newFileName);
// 下载文件到临时目录根据实际存储类型实现此处以URL为例
downloadFile(scheme.getFileUrl(), tempFile);
}
// 4. 压缩临时目录中的所有文件为ZIP并写入响应
response.setContentType("application/zip");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
String zipFileName = URLEncoder.encode("初步设计方案", StandardCharsets.UTF_8.name()) + ".zip";
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + zipFileName);
// 压缩文件到响应流
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
File[] tempFiles = tempDir.listFiles();
if (tempFiles != null) {
for (File file : tempFiles) {
// ZIP条目名称为新文件名不含临时目录路径
zos.putNextEntry(new ZipEntry(file.getName()));
try (InputStream in = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
}
}
}
} finally {
// 5. 清理临时目录
FileUtils.deleteQuietly(tempDir);
}
}
/**
* 下载文件到本地临时目录根据实际存储类型调整如OSS、本地文件系统
*/
private void downloadFile(String fileUrl, File destFile) throws Exception {
// 示例从URL下载若为OSS使用OSS SDK的getObject方法
// 创建HTTP客户端设置超时时间
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofMinutes(5)) // 连接超时
.build();
// 创建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(fileUrl)) // 转换为URI比URL更推荐
.timeout(Duration.ofMinutes(10)) // 响应超时
.GET()
.build();
// 发送请求并将响应体写入目标文件
HttpResponse<Path> response = client.send(
request,
HttpResponse.BodyHandlers.ofFile(destFile.toPath()) // 直接写入文件路径
);
// 检查响应状态200表示成功
if (response.statusCode() != 200) {
throw new RuntimeException("文件下载失败URL: " + fileUrl + ",状态码: " + response.statusCode());
}
}
/**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 正常使用只需#processEvent.flowCode=='leave1'

View File

@ -1,8 +1,11 @@
package org.dromara.design.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
@ -19,6 +22,7 @@ import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.design.domain.DesCollectFile;
import org.dromara.design.domain.DesPrelimScheme;
import org.dromara.design.domain.dto.ExportDto;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
@ -31,10 +35,23 @@ import org.dromara.design.mapper.DesSchemeMapper;
import org.dromara.design.service.IDesSchemeService;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
@ -183,6 +200,101 @@ public class DesSchemeServiceImpl extends ServiceImpl<DesSchemeMapper, DesScheme
return baseMapper.updateById(desScheme)>0;
}
@Override
public void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception {
// 1. 查询数据
List<DesScheme> schemes = baseMapper.selectList(Wrappers.<DesScheme>lambdaQuery()
.eq(DesScheme::getProjectId, dto.getProjectId())
.in(CollectionUtil.isNotEmpty(dto.getIds()), DesScheme::getId, dto.getIds())
);
if (schemes.isEmpty()) {
throw new RuntimeException("没有可导出的初步设计方案文件");
}
// 2. 创建临时目录(存放重命名后的文件)
File tempDir = File.createTempFile("prelim_scheme_", "_temp");
if (!tempDir.delete()) { // 删除临时文件,创建同名目录
throw new RuntimeException("创建临时目录失败");
}
if (!tempDir.mkdirs()) {
throw new RuntimeException("创建临时目录失败");
}
try {
// 3. 下载文件并以“审核状态-原文件名”命名
for (DesScheme scheme : schemes) {
String status = BusinessStatusEnum.getByStatus(scheme.getStatus()).getDesc(); // 审核状态如draft、approved
String originalFileName = scheme.getFileName(); // 原文件名
String newFileName = status + "-" + originalFileName; // 新文件名:状态-原文件名
// 临时文件路径
File tempFile = new File(tempDir, newFileName);
// 下载文件到临时目录根据实际存储类型实现此处以URL为例
downloadFile(scheme.getFileUrl(), tempFile);
}
// 4. 压缩临时目录中的所有文件为ZIP并写入响应
response.setContentType("application/zip");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
String zipFileName = URLEncoder.encode("初步设计方案", StandardCharsets.UTF_8.name()) + ".zip";
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + zipFileName);
// 压缩文件到响应流
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
File[] tempFiles = tempDir.listFiles();
if (tempFiles != null) {
for (File file : tempFiles) {
// ZIP条目名称为新文件名不含临时目录路径
zos.putNextEntry(new ZipEntry(file.getName()));
try (InputStream in = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
}
}
}
} finally {
// 5. 清理临时目录
FileUtils.deleteQuietly(tempDir);
}
}
/**
* 下载文件到本地临时目录根据实际存储类型调整如OSS、本地文件系统
*/
private void downloadFile(String fileUrl, File destFile) throws Exception {
// 示例从URL下载若为OSS使用OSS SDK的getObject方法
// 创建HTTP客户端设置超时时间
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofMinutes(5)) // 连接超时
.build();
// 创建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(fileUrl)) // 转换为URI比URL更推荐
.timeout(Duration.ofMinutes(10)) // 响应超时
.GET()
.build();
// 发送请求并将响应体写入目标文件
HttpResponse<Path> response = client.send(
request,
HttpResponse.BodyHandlers.ofFile(destFile.toPath()) // 直接写入文件路径
);
// 检查响应状态200表示成功
if (response.statusCode() != 200) {
throw new RuntimeException("文件下载失败URL: " + fileUrl + ",状态码: " + response.statusCode());
}
}
/**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 正常使用只需#processEvent.flowCode=='leave1'

View File

@ -1,39 +0,0 @@
package org.dromara.facility.domain.vo.matrix;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.progress.domain.vo.progresscategory.PgsProgressCategoryStructureVo;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-08-23 01:17
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FacMatrixStructureVo implements Serializable {
@Serial
private static final long serialVersionUID = 7526403047030009646L;
/**
* 主键
*/
private Long id;
/**
* 方阵名称
*/
private String name;
/**
* 分项工程
*/
private List<PgsProgressCategoryStructureVo> children;
}

View File

@ -847,7 +847,7 @@ public class FacPhotovoltaicPanelPartsServiceImpl implements IFacPhotovoltaicPan
// 根据类型,更新对应数据
if (type.equals(RecognizerTypeEnum.HOLE.getValue())) {
photovoltaicPanelPointService.updateFinishNumberByCoordinate(projectIds, matchPoints);
} else if (type.equals(RecognizerTypeEnum.PILE.getValue())) {
} else if (type.equals(RecognizerTypeEnum.COLUMN.getValue())) {
photovoltaicPanelColumnService.updateFinishNumberByCoordinate(projectIds, matchPoints);
} else if (type.equals(RecognizerTypeEnum.BRACKET.getValue())) {
photovoltaicPanelSupportService.updateFinishNumberByCoordinate(projectIds, matchPoints);
@ -871,7 +871,7 @@ public class FacPhotovoltaicPanelPartsServiceImpl implements IFacPhotovoltaicPan
return list.stream().map(point ->
new IdCoordinatePoint(point.getId(), point.getPositions())).toList();
}
} else if (type.equals(RecognizerTypeEnum.PILE.getValue())) {
} else if (type.equals(RecognizerTypeEnum.COLUMN.getValue())) {
List<FacPhotovoltaicPanelColumn> list = photovoltaicPanelColumnService.lambdaQuery()
.in(FacPhotovoltaicPanelColumn::getProjectId, projectIds)
.ne(FacPhotovoltaicPanelColumn::getStatus, FacFinishStatusEnum.FINISH.getValue())

View File

@ -8,6 +8,7 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.gps.domain.vo.GpsStatusVo;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -56,6 +57,16 @@ public class GpsEquipmentSonController extends BaseController {
return R.ok(gpsEquipmentSonService.queryList(bo));
}
/**
* 查询GPS设备定位日期信息列表
*/
// @SaCheckPermission("gps:equipmentSon:getRlList")
@GetMapping("/getRlList")
public R<List<GpsStatusVo>> getRlList(GpsEquipmentSonBo bo) {
return R.ok(gpsEquipmentSonService.getRlList(bo));
}
/**
* 查询GPS设备定位信息列表(大屏获取人员最后一次位置)
*/

View File

@ -0,0 +1,12 @@
package org.dromara.gps.domain.vo;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
@Data
public class GpsStatusVo implements Serializable {
private LocalDate riqi;
private Long count;
}

View File

@ -3,8 +3,10 @@ package org.dromara.gps.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.dromara.gps.domain.GpsEquipmentSon;
import org.dromara.gps.domain.bo.GpsEquipmentSonBo;
import org.dromara.gps.domain.vo.GpsEquipmentSonVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.gps.domain.vo.GpsStatusVo;
import java.time.LocalDateTime;
import java.util.List;
@ -28,9 +30,10 @@ public interface GpsEquipmentSonMapper extends BaseMapperPlus<GpsEquipmentSon, G
"AND create_time BETWEEN #{startTime} AND #{endTime}\n" +
")\n" +
"SELECT\n" +
" *\n" +
" r.*,su.nick_name as userName\n" +
"FROM\n" +
" RankedData\n" +
" RankedData r\n" +
"LEFT JOIN sys_user su ON r.user_id=su.user_id\n" +
"WHERE\n" +
" rn = 1;")
List<GpsEquipmentSonVo> getClientList(@Param("projectId") Long projectId, @Param("startTime") LocalDateTime startOfDay, @Param("endTime") LocalDateTime now);
@ -77,4 +80,16 @@ public interface GpsEquipmentSonMapper extends BaseMapperPlus<GpsEquipmentSon, G
"WHERE\n" +
" rn = 1;")
List<GpsEquipmentSonVo> getUserListByProjectId(@Param("projectId") Long projectId, @Param("startTime") LocalDateTime startOfDay, @Param("endTime") LocalDateTime now);
@Select("SELECT\n" +
" DATE(create_time) AS riqi,\n" +
" COUNT(id) AS count \n" +
"FROM\n" +
" gps_equipment_son\n" +
"WHERE\n" +
" project_id = #{bo.projectId}\n" +
" AND client_id = #{bo.clientId}\n" +
" AND create_time BETWEEN #{bo.startTime} AND #{bo.endTime}\n" +
" GROUP BY DATE(create_time)")
List<GpsStatusVo> getRlList(@Param("bo") GpsEquipmentSonBo bo);
}

View File

@ -7,6 +7,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.gps.domain.vo.GpsStatusVo;
import java.time.LocalDateTime;
import java.util.Collection;
@ -78,4 +79,6 @@ public interface IGpsEquipmentSonService extends IService<GpsEquipmentSon>{
List<GpsEquipmentSonVo> getLargerScreenList(GpsEquipmentSonBo bo);
List<GpsEquipmentSonVo> getUserListByProjectId(Long projectId, LocalDateTime startOfDay, LocalDateTime now);
List<GpsStatusVo> getRlList(GpsEquipmentSonBo bo);
}

View File

@ -1,6 +1,7 @@
package org.dromara.gps.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -11,6 +12,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.gps.domain.vo.GpsStatusVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDictDataService;
@ -100,6 +102,7 @@ public class GpsEquipmentSonServiceImpl extends ServiceImpl<GpsEquipmentSonMappe
lqw.eq(bo.getProjectId() != null, GpsEquipmentSon::getProjectId, bo.getProjectId());
lqw.eq(bo.getUserId() != null, GpsEquipmentSon::getUserId, bo.getUserId());
lqw.eq(StringUtils.isNotBlank(bo.getClientId()), GpsEquipmentSon::getClientId, bo.getClientId());
lqw.between(bo.getStartTime() != null && bo.getEndTime() != null, GpsEquipmentSon::getCreateTime, bo.getStartTime(), bo.getEndTime());
return lqw;
}
@ -194,4 +197,18 @@ public class GpsEquipmentSonServiceImpl extends ServiceImpl<GpsEquipmentSonMappe
public List<GpsEquipmentSonVo> getUserListByProjectId(Long projectId, LocalDateTime startOfDay, LocalDateTime now) {
return baseMapper.getUserListByProjectId(projectId,startOfDay,now);
}
@Override
public List<GpsStatusVo> getRlList(GpsEquipmentSonBo bo) {
if (bo.getProjectId() == null) {
throw new ServiceException("项目id不能为空");
}
if (bo.getClientId() == null) {
throw new ServiceException("设备id不能为空");
}
if (bo.getStartTime() == null || bo.getEndTime() == null) {
throw new ServiceException("开始时间和结算时间不能为空!!!");
}
return baseMapper.getRlList(bo);
}
}

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