Compare commits
147 Commits
prod
...
0a18a0892a
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a18a0892a | |||
| b2e1cce667 | |||
| 64c1c84ed3 | |||
| f2301264ea | |||
| c83af6df6d | |||
| 22d9dfe138 | |||
| bcee164f48 | |||
| f6d8cb3ba7 | |||
| 659146a2cf | |||
| c17c084095 | |||
| 5962f694e2 | |||
| 373528b432 | |||
| e9f5e0fa03 | |||
| b88a92b7e1 | |||
| fb72063369 | |||
| 9809213a12 | |||
| 87e1cb7473 | |||
| c456ae215f | |||
| 091d7bfc0e | |||
| c9f80fe498 | |||
| 7a3deba52a | |||
| eb015fcecc | |||
| d9fab2a4a2 | |||
| 7f125b4548 | |||
| 88dcecc88c | |||
| 5458329252 | |||
| c4e275f5a8 | |||
| f8f89fd96e | |||
| b8ffa41a30 | |||
| f99cd08d57 | |||
| 45cae080a0 | |||
| 84a8f49e95 | |||
| e725991ece | |||
| c2a06a729c | |||
| 4a5e50a7a2 | |||
| 9b30a7bcec | |||
| ec383e44db | |||
| d3909f131f | |||
| a444d4c953 | |||
| 3c01ec861b | |||
| 365cf00644 | |||
| 4faff3b0bc | |||
| 866f2336c3 | |||
| e4132ea540 | |||
| 53208c36cf | |||
| 8f2a3e6e50 | |||
| b67a7d5370 | |||
| 07509c8e15 | |||
| e6c58a64ac | |||
| abb6b8c13a | |||
| de492728bc | |||
| 82fa732db6 | |||
| 275d640263 | |||
| 7e47b8a74f | |||
| d4301da0ec | |||
| fe5b5473dd | |||
| 0126d44761 | |||
| dbefd88e41 | |||
| 9a3b7ebe54 | |||
| 81be4a862c | |||
| 43f2db9f7e | |||
| abefa90408 | |||
| 08f48b7817 | |||
| 6b8ace60d4 | |||
| b997dd5f00 | |||
| 6174743858 | |||
| 82d55d7188 | |||
| aec5eacd0b | |||
| a320b85965 | |||
| b61a7c153d | |||
| fb9b01cf34 | |||
| 32f134873a | |||
| f4220be9d6 | |||
| 8252fd7216 | |||
| 57855f4307 | |||
| 6784eafe6e | |||
| 0b42c1d6a6 | |||
| 4b37a7327f | |||
| 0287f1e4ce | |||
| 5d8af1cab8 | |||
| 123896f08b | |||
| a8a198b51f | |||
| f00b98714a | |||
| 4ff87f3996 | |||
| 5f3ae0f9f1 | |||
| 786c864a27 | |||
| c61e802b85 | |||
| 9a568799f4 | |||
| 8aa38063bf | |||
| c4a11ec245 | |||
| 169b76589c | |||
| 199f51ea21 | |||
| 23572dfc07 | |||
| 570b0ce316 | |||
| f953a96c36 | |||
| 25c4eee464 | |||
| b209ef1fab | |||
| 77e9f4d9a2 | |||
| e73c808bc3 | |||
| edf0d1a5db | |||
| cc23a308c1 | |||
| bc891327c9 | |||
| 39bedfeb92 | |||
| 56fc4ff83e | |||
| 524ed30728 | |||
| 584304e744 | |||
| 56418600c5 | |||
| 40e57b18cb | |||
| ced6cb219c | |||
| 218ec5ea95 | |||
| 1c601bd68e | |||
| 7d6eba719b | |||
| 8cfc34dbcb | |||
| 23a749973d | |||
| 4b96702dc9 | |||
| a1af8711ef | |||
| 9e8cff931b | |||
| 5bccd71bdc | |||
| 544f35a601 | |||
| f920d4976e | |||
| cdcd665d43 | |||
| 08de61e455 | |||
| 025c3115b7 | |||
| 9e366554b7 | |||
| d934abf0fe | |||
| e16e9133e2 | |||
| d7854a35d7 | |||
| 97e0dd467f | |||
| e58a99e696 | |||
| 0d5a9eb505 | |||
| 45ac1817e1 | |||
| f2d4ff4237 | |||
| a4cc5c14f3 | |||
| 5be571cc30 | |||
| ff656dd046 | |||
| 20574d0037 | |||
| 42cf396e93 | |||
| 2a30b05dd7 | |||
| 4ecee185bf | |||
| 48ab59c67d | |||
| 806e8f3391 | |||
| 61244321a7 | |||
| ff1e613316 | |||
| 03161751fc | |||
| 9ebf4a3e5f | |||
| 3ba13e3ef8 | |||
| 8d3853fe6b |
@ -1,3 +1,7 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
server:
|
||||||
|
# 服务器的HTTP端口,默认为8080
|
||||||
|
port: 8899
|
||||||
--- # 监控中心配置
|
--- # 监控中心配置
|
||||||
spring.boot.admin.client:
|
spring.boot.admin.client:
|
||||||
# 增加客户端开关
|
# 增加客户端开关
|
||||||
@ -32,6 +36,12 @@ snail-job:
|
|||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
|
ai:
|
||||||
|
dashscope:
|
||||||
|
api-key: sk-8d8df92fcbac4bd2922edba30b0bb8fa
|
||||||
|
chat:
|
||||||
|
options:
|
||||||
|
model: qwen-plus
|
||||||
datasource:
|
datasource:
|
||||||
type: com.zaxxer.hikari.HikariDataSource
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||||
@ -52,38 +62,38 @@ spring:
|
|||||||
url: jdbc:mysql://192.168.110.2:13386/xinnengyuandev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
url: jdbc:mysql://192.168.110.2:13386/xinnengyuandev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
username: xinnengyuandev
|
username: xinnengyuandev
|
||||||
password: StRWCZdZirysNSs2
|
password: StRWCZdZirysNSs2
|
||||||
# url: jdbc:mysql://192.168.110.2:13386/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
# url: jdbc:mysql://192.168.110.2:13386/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
# username: xinnengyuan
|
# username: xinnengyuan
|
||||||
# password: mEZPC5Sdf3r2HENi
|
# 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
|
|
||||||
slave2:
|
|
||||||
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
|
|
||||||
# slave:
|
# slave:
|
||||||
# lazy: true
|
# lazy: true
|
||||||
# type: ${spring.datasource.type}
|
# type: ${spring.datasource.type}
|
||||||
# driverClassName: com.mysql.cj.jdbc.Driver
|
# 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
|
# 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: zmkgprod
|
# username: zmkgc
|
||||||
# password: MaY8nehwWkJriWPm
|
# 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
|
||||||
|
# slave2:
|
||||||
|
# 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
|
||||||
|
# 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:
|
# oracle:
|
||||||
# type: ${spring.datasource.type}
|
# type: ${spring.datasource.type}
|
||||||
# driverClassName: oracle.jdbc.OracleDriver
|
# driverClassName: oracle.jdbc.OracleDriver
|
||||||
@ -354,3 +364,7 @@ qrCode:
|
|||||||
# 无人机大图
|
# 无人机大图
|
||||||
drone:
|
drone:
|
||||||
url: http://192.168.110.2:9512
|
url: http://192.168.110.2:9512
|
||||||
|
# 聊天服务
|
||||||
|
chat:
|
||||||
|
server:
|
||||||
|
port: 19099
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
server:
|
||||||
|
# 服务器的HTTP端口,默认为8080
|
||||||
|
port: 9918
|
||||||
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
||||||
spring.servlet.multipart.location: /ruoyi/server/temp
|
spring.servlet.multipart.location: /ruoyi/server/temp
|
||||||
|
|
||||||
@ -35,6 +39,12 @@ snail-job:
|
|||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
|
ai:
|
||||||
|
dashscope:
|
||||||
|
api-key: xxx
|
||||||
|
chat:
|
||||||
|
options:
|
||||||
|
model: qwen-plus
|
||||||
datasource:
|
datasource:
|
||||||
type: com.zaxxer.hikari.HikariDataSource
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||||
@ -105,7 +115,7 @@ spring.data:
|
|||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 9287
|
port: 9287
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 6
|
database: 17
|
||||||
# redis 密码必须配置
|
# redis 密码必须配置
|
||||||
password: syar23rdsaagdrsa
|
password: syar23rdsaagdrsa
|
||||||
# 连接超时时间
|
# 连接超时时间
|
||||||
@ -164,7 +174,7 @@ sms:
|
|||||||
# 配置源类型用于标定配置来源(interface,yaml)
|
# 配置源类型用于标定配置来源(interface,yaml)
|
||||||
config-type: yaml
|
config-type: yaml
|
||||||
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
||||||
restricted: true
|
restricted: false
|
||||||
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
|
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
|
||||||
minute-max: 1
|
minute-max: 1
|
||||||
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
|
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
|
||||||
@ -183,12 +193,45 @@ sms:
|
|||||||
signature: 您的短信签名
|
signature: 您的短信签名
|
||||||
sdk-app-id: 您的sdkAppId
|
sdk-app-id: 您的sdkAppId
|
||||||
config2:
|
config2:
|
||||||
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
|
# 登录
|
||||||
supplier: tencent
|
supplier: tencent
|
||||||
access-key-id: 您的accessKey
|
access-key-id: AKIDb3JK5dx4wa0DCxWqvxlKejvysZ3ITVJv
|
||||||
access-key-secret: 您的accessKeySecret
|
access-key-secret: c5LPFsJI8k7GDxTkoeFj4A1ukQr66rPi
|
||||||
signature: 您的短信签名
|
signature: 重庆远界大数据研究院
|
||||||
sdk-app-id: 您的sdkAppId
|
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:
|
justauth:
|
||||||
@ -275,8 +318,11 @@ weather:
|
|||||||
dxf2GeoJson:
|
dxf2GeoJson:
|
||||||
file-name: main
|
file-name: main
|
||||||
ys7:
|
ys7:
|
||||||
app-key: 3acf9f1a43dc4209841e0893003db0a2
|
app-key: xxx
|
||||||
app-secret: 4bbf3e9394f55d3af6e3af27b2d3db36
|
app-secret: xxx
|
||||||
|
job:
|
||||||
|
capture-enabled: false # 控制是否启用萤石抓拍任务
|
||||||
|
device-sync-enabled: false # 控制是否同步萤石设备
|
||||||
# 斯巴达算法
|
# 斯巴达算法
|
||||||
sparta:
|
sparta:
|
||||||
url: http://119.3.204.120:8040
|
url: http://119.3.204.120:8040
|
||||||
@ -286,10 +332,14 @@ sparta:
|
|||||||
id-card:
|
id-card:
|
||||||
encrypt-key: 7ae260d150a14027d2238a1cf80a48ef
|
encrypt-key: 7ae260d150a14027d2238a1cf80a48ef
|
||||||
recognizer:
|
recognizer:
|
||||||
url: http://192.168.110.5:50070
|
url: http://192.168.110.5:50071
|
||||||
|
|
||||||
qrCode:
|
qrCode:
|
||||||
url: http://xny.yj-3d.com:7171
|
url: http://xny.yj-3d.com:7788
|
||||||
# 无人机大图
|
# 无人机大图
|
||||||
drone:
|
drone:
|
||||||
url: http://192.168.110.2:9512
|
url: http://192.168.110.2:9512
|
||||||
|
# 聊天服务
|
||||||
|
chat:
|
||||||
|
server:
|
||||||
|
port: 18088
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
server:
|
||||||
|
# 服务器的HTTP端口,默认为8080
|
||||||
|
port: 8899
|
||||||
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
||||||
spring.servlet.multipart.location: /ruoyi/server/temp
|
spring.servlet.multipart.location: /ruoyi/server/temp
|
||||||
|
|
||||||
@ -35,6 +39,12 @@ snail-job:
|
|||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
|
ai:
|
||||||
|
dashscope:
|
||||||
|
api-key: xxx
|
||||||
|
chat:
|
||||||
|
options:
|
||||||
|
model: qwen-plus
|
||||||
datasource:
|
datasource:
|
||||||
type: com.zaxxer.hikari.HikariDataSource
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||||
@ -326,7 +336,7 @@ ys7:
|
|||||||
app-key: 3acf9f1a43dc4209841e0893003db0a2
|
app-key: 3acf9f1a43dc4209841e0893003db0a2
|
||||||
app-secret: 09e29c70ae1161fbc3ce2030fc09ba2e
|
app-secret: 09e29c70ae1161fbc3ce2030fc09ba2e
|
||||||
job:
|
job:
|
||||||
capture-enabled: false # 控制是否启用萤石抓拍任务
|
capture-enabled: true # 控制是否启用萤石抓拍任务
|
||||||
device-sync-enabled: true # 控制是否同步萤石设备
|
device-sync-enabled: true # 控制是否同步萤石设备
|
||||||
# 斯巴达算法
|
# 斯巴达算法
|
||||||
sparta:
|
sparta:
|
||||||
@ -344,3 +354,7 @@ qrCode:
|
|||||||
# 无人机大图
|
# 无人机大图
|
||||||
drone:
|
drone:
|
||||||
url: http://192.168.110.2:9512
|
url: http://192.168.110.2:9512
|
||||||
|
# 聊天服务
|
||||||
|
chat:
|
||||||
|
server:
|
||||||
|
port: 19099
|
||||||
|
|||||||
@ -22,7 +22,7 @@ captcha:
|
|||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
server:
|
server:
|
||||||
# 服务器的HTTP端口,默认为8080
|
# 服务器的HTTP端口,默认为8080
|
||||||
port: 8899
|
# port: 8899
|
||||||
servlet:
|
servlet:
|
||||||
# 应用的访问路径
|
# 应用的访问路径
|
||||||
context-path: /
|
context-path: /
|
||||||
@ -106,6 +106,10 @@ sa-token:
|
|||||||
is-share: false
|
is-share: false
|
||||||
# jwt秘钥
|
# jwt秘钥
|
||||||
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
|
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
|
||||||
|
# token有效期,单位s 默认30天, -1代表永不过期
|
||||||
|
timeout: 2592000
|
||||||
|
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||||
|
active-timeout: 86400
|
||||||
|
|
||||||
# security配置
|
# security配置
|
||||||
security:
|
security:
|
||||||
@ -130,6 +134,7 @@ security:
|
|||||||
# todo 仅测试
|
# todo 仅测试
|
||||||
- /facility/matrix/**
|
- /facility/matrix/**
|
||||||
- /hat/device/data
|
- /hat/device/data
|
||||||
|
- /websocket/ue
|
||||||
|
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
@ -277,14 +282,16 @@ springdoc:
|
|||||||
packages-to-scan: org.dromara.gps
|
packages-to-scan: org.dromara.gps
|
||||||
- group: 24.招标模块
|
- group: 24.招标模块
|
||||||
packages-to-scan: org.dromara.tender
|
packages-to-scan: org.dromara.tender
|
||||||
# - group: 25.app版本模块
|
- group: 29.app版本模块
|
||||||
# packages-to-scan: org.dromara.app
|
packages-to-scan: org.dromara.app
|
||||||
- group: 25.数据迁移模块
|
- group: 25.数据迁移模块
|
||||||
packages-to-scan: org.dromara.transferData
|
packages-to-scan: org.dromara.transferData
|
||||||
- group: 26.netty消息模块
|
- group: 26.netty消息模块
|
||||||
packages-to-scan: org.dromara.websocket
|
packages-to-scan: org.dromara.websocket
|
||||||
- group: 27.新中大模块
|
- group: 27.新中大模块
|
||||||
packages-to-scan: org.dromara.xzd
|
packages-to-scan: org.dromara.xzd
|
||||||
|
- group: 28.车辆模块
|
||||||
|
packages-to-scan: org.dromara.vehicle
|
||||||
# knife4j的增强配置,不需要增强可以不配
|
# knife4j的增强配置,不需要增强可以不配
|
||||||
knife4j:
|
knife4j:
|
||||||
enable: true
|
enable: true
|
||||||
@ -300,6 +307,15 @@ xss:
|
|||||||
- /system/notice
|
- /system/notice
|
||||||
- /warm-flow/save-xml
|
- /warm-flow/save-xml
|
||||||
- /project/project
|
- /project/project
|
||||||
|
- /xzd/contractDetails/**
|
||||||
|
- /xzd/contractChange/**
|
||||||
|
- /xzd/comprehensive/csContractChange/**
|
||||||
|
- /xzd/comprehensive/csContractInformation/**
|
||||||
|
- /xzd/hetongbiangeng/**
|
||||||
|
- /xzd/fenbaohetongjungong/**
|
||||||
|
- /xzd/fenbaohetongbiangg/**
|
||||||
|
- /xzd/fenbaohetongxinxi/**
|
||||||
|
- /xzd/contractManagement/**
|
||||||
|
|
||||||
# 全局线程池相关配置
|
# 全局线程池相关配置
|
||||||
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
|
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
|
||||||
|
|||||||
Binary file not shown.
BIN
xinnengyuan/ruoyi-admin/src/main/resources/template/设计更改通知单.docx
Normal file
BIN
xinnengyuan/ruoyi-admin/src/main/resources/template/设计更改通知单.docx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
xinnengyuan/ruoyi-admin/src/main/resources/template/设计验证表.docx
Normal file
BIN
xinnengyuan/ruoyi-admin/src/main/resources/template/设计验证表.docx
Normal file
Binary file not shown.
@ -29,6 +29,8 @@ import org.dromara.manager.ys7manager.Ys7Constant;
|
|||||||
import org.dromara.manager.ys7manager.Ys7Manager;
|
import org.dromara.manager.ys7manager.Ys7Manager;
|
||||||
import org.dromara.manager.ys7manager.vo.Ys7ResponseVo;
|
import org.dromara.manager.ys7manager.vo.Ys7ResponseVo;
|
||||||
import org.dromara.other.domain.OthYs7Device;
|
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.other.service.IOthYs7DeviceService;
|
||||||
import org.dromara.out.domain.OutConstructionValue;
|
import org.dromara.out.domain.OutConstructionValue;
|
||||||
import org.dromara.out.domain.OutConstructionValueRange;
|
import org.dromara.out.domain.OutConstructionValueRange;
|
||||||
@ -55,6 +57,8 @@ import java.time.LocalDate;
|
|||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,6 +116,9 @@ public class DemoTest {
|
|||||||
@Resource
|
@Resource
|
||||||
private IOthYs7DeviceService ys7DeviceService;
|
private IOthYs7DeviceService ys7DeviceService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IOthYs7DeviceImgService ys7DeviceImgService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private Ys7Manager ys7Manager;
|
private Ys7Manager ys7Manager;
|
||||||
|
|
||||||
@ -128,7 +135,7 @@ public class DemoTest {
|
|||||||
log.info("执行定时任务:同步 {}至{} 计划详情到施工产值", lastMonday, lastSunday);
|
log.info("执行定时任务:同步 {}至{} 计划详情到施工产值", lastMonday, lastSunday);
|
||||||
Boolean synced = progressPlanDetailService.syncPlanDetail2ConstructionValue(lastMonday, lastSunday, null);*/
|
Boolean synced = progressPlanDetailService.syncPlanDetail2ConstructionValue(lastMonday, lastSunday, null);*/
|
||||||
LocalDate start = LocalDate.of(2024, 1, 1); // 起始时间(2024-01-01)
|
LocalDate start = LocalDate.of(2024, 1, 1); // 起始时间(2024-01-01)
|
||||||
LocalDate end = LocalDate.of(2025, 10, 20); // 截止时间(2025-09-15)
|
LocalDate end = LocalDate.of(2025, 10, 27); // 截止时间(2025-09-15)
|
||||||
|
|
||||||
// 如果起始不是周一,调整到当周的周一
|
// 如果起始不是周一,调整到当周的周一
|
||||||
if (start.getDayOfWeek() != DayOfWeek.MONDAY) {
|
if (start.getDayOfWeek() != DayOfWeek.MONDAY) {
|
||||||
@ -143,12 +150,18 @@ public class DemoTest {
|
|||||||
System.out.println(monday + " ~ " + sunday);
|
System.out.println(monday + " ~ " + sunday);
|
||||||
log.info("执行定时任务:同步 {}至{} 计划详情到施工产值", monday, sunday);
|
log.info("执行定时任务:同步 {}至{} 计划详情到施工产值", monday, sunday);
|
||||||
// Boolean synced = progressPlanDetailService.syncPlanDetail2ConstructionValue(start, now, null);
|
// Boolean synced = progressPlanDetailService.syncPlanDetail2ConstructionValue(start, now, null);
|
||||||
|
List<BusProject> list = projectService.lambdaQuery()
|
||||||
|
.eq(BusProject::getPId, 1968506669544656898L)
|
||||||
|
.list();
|
||||||
|
List<Long> list1 = new ArrayList<>(list.stream().map(BusProject::getId).toList());
|
||||||
|
list1.add(1968506669544656898L);
|
||||||
// 获取范围时间内的计划详情
|
// 获取范围时间内的计划详情
|
||||||
List<PgsProgressPlanDetail> planDetailList = progressPlanDetailService.lambdaQuery()
|
List<PgsProgressPlanDetail> planDetailList = progressPlanDetailService.lambdaQuery()
|
||||||
|
.in(PgsProgressPlanDetail::getProjectId, list1)
|
||||||
.ge(PgsProgressPlanDetail::getDate, monday)
|
.ge(PgsProgressPlanDetail::getDate, monday)
|
||||||
.le(PgsProgressPlanDetail::getDate, sunday)
|
.le(PgsProgressPlanDetail::getDate, sunday)
|
||||||
.ne(PgsProgressPlanDetail::getFinishedNumber, BigDecimal.ZERO)
|
.ne(PgsProgressPlanDetail::getFinishedNumber, BigDecimal.ZERO)
|
||||||
// .eq(PgsProgressPlanDetail::getStatus, "1")
|
.eq(PgsProgressPlanDetail::getStatus, "1")
|
||||||
.list();
|
.list();
|
||||||
if (CollUtil.isEmpty(planDetailList)) {
|
if (CollUtil.isEmpty(planDetailList)) {
|
||||||
// 下一周
|
// 下一周
|
||||||
@ -547,4 +560,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));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,132 @@
|
|||||||
|
package org.dromara.test;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.materials.domain.MatMaterialIssue;
|
||||||
|
import org.dromara.materials.domain.MatMaterialReceive;
|
||||||
|
import org.dromara.materials.domain.MatMaterials;
|
||||||
|
import org.dromara.materials.domain.MatMaterialsInventory;
|
||||||
|
import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum;
|
||||||
|
import org.dromara.materials.service.IMatMaterialIssueService;
|
||||||
|
import org.dromara.materials.service.IMatMaterialReceiveService;
|
||||||
|
import org.dromara.materials.service.IMatMaterialsInventoryService;
|
||||||
|
import org.dromara.materials.service.IMatMaterialsService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-10-30 15:58
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@SpringBootTest
|
||||||
|
public class MaterialsTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 长顺项目id
|
||||||
|
*/
|
||||||
|
private static final Long PROJECT_ID = 1897161054676336641L;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IMatMaterialsService materialsService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IMatMaterialsInventoryService materialsInventoryService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IMatMaterialReceiveService materialReceiveService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IMatMaterialIssueService materialIssueService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
// 获取所有材料
|
||||||
|
List<MatMaterials> materials = materialsService.lambdaQuery()
|
||||||
|
.eq(MatMaterials::getProjectId, PROJECT_ID)
|
||||||
|
.list();
|
||||||
|
Set<Long> materialIds = materials.stream().map(MatMaterials::getId).collect(Collectors.toSet());
|
||||||
|
// 获取所有材料的出库数据
|
||||||
|
List<MatMaterialsInventory> inventoryList = materialsInventoryService.lambdaQuery()
|
||||||
|
.in(MatMaterialsInventory::getMaterialsId, materialIds)
|
||||||
|
.eq(MatMaterialsInventory::getOutPut, MatMaterialsInventoryOutPutEnum.OUT.getValue())
|
||||||
|
.list();
|
||||||
|
// 按表单编号分组
|
||||||
|
Map<String, List<MatMaterials>> map = materials.stream()
|
||||||
|
.collect(Collectors.groupingBy(MatMaterials::getFormCode));
|
||||||
|
for (Map.Entry<String, List<MatMaterials>> entry : map.entrySet()) {
|
||||||
|
String formCode = entry.getKey();
|
||||||
|
List<MatMaterials> materialsList = entry.getValue();
|
||||||
|
// 获取入库数据
|
||||||
|
MatMaterialReceive receive = materialReceiveService.lambdaQuery()
|
||||||
|
.eq(MatMaterialReceive::getFormCode, formCode)
|
||||||
|
.one();
|
||||||
|
// 创建领料出库数据
|
||||||
|
MatMaterialIssue issue = new MatMaterialIssue();
|
||||||
|
issue.setProjectId(PROJECT_ID);
|
||||||
|
issue.setMaterialSource("2");
|
||||||
|
issue.setFormCode(formCode);
|
||||||
|
issue.setProjectName(receive.getProjectName());
|
||||||
|
issue.setMaterialName(receive.getMaterialName());
|
||||||
|
issue.setOrderingUnit(receive.getOrderingUnit());
|
||||||
|
issue.setSupplierUnit(receive.getSupplierUnit());
|
||||||
|
// issue.setIssueUnit(inventory.getRecipient());
|
||||||
|
// issue.setIssueUnitId(inventory.getRecipientId());
|
||||||
|
// issue.setShipper(inventory.getShipper());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// issue.setStorageUnit();
|
||||||
|
issue.setCertCount(0);
|
||||||
|
issue.setReportCount(0);
|
||||||
|
issue.setTechDocCount(0);
|
||||||
|
issue.setLicenseCount(0);
|
||||||
|
log.info("领料出库数据:{}", issue);
|
||||||
|
log.info("=============================");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MatMaterials material : materials) {
|
||||||
|
String formCode = material.getFormCode();
|
||||||
|
// 查看入库数据
|
||||||
|
MatMaterialReceive receive = materialReceiveService.lambdaQuery()
|
||||||
|
.eq(MatMaterialReceive::getFormCode, formCode)
|
||||||
|
.one();
|
||||||
|
// 查看出库数据
|
||||||
|
/* List<MatMaterialsInventory> inventoryList = materialsInventoryService.lambdaQuery()
|
||||||
|
.eq(MatMaterialsInventory::getMaterialsId, material.getId())
|
||||||
|
.eq(MatMaterialsInventory::getOutPut, MatMaterialsInventoryOutPutEnum.OUT.getValue())
|
||||||
|
.list();
|
||||||
|
if (CollUtil.isEmpty(inventoryList)) {
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
// 创建领料出库数据
|
||||||
|
List<MatMaterialIssue> issueList = inventoryList.stream().map(inventory -> {
|
||||||
|
MatMaterialIssue issue = new MatMaterialIssue();
|
||||||
|
issue.setProjectId(PROJECT_ID);
|
||||||
|
issue.setMaterialSource("2");
|
||||||
|
issue.setFormCode(receive.getFormCode());
|
||||||
|
issue.setProjectName(receive.getProjectName());
|
||||||
|
issue.setMaterialName(receive.getMaterialName());
|
||||||
|
issue.setOrderingUnit(receive.getOrderingUnit());
|
||||||
|
issue.setSupplierUnit(receive.getSupplierUnit());
|
||||||
|
issue.setIssueUnit(inventory.getRecipient());
|
||||||
|
issue.setIssueUnitId(inventory.getRecipientId());
|
||||||
|
issue.setShipper(inventory.getShipper());
|
||||||
|
// issue.setStorageUnit();
|
||||||
|
issue.setCertCount(0);
|
||||||
|
issue.setReportCount(0);
|
||||||
|
issue.setTechDocCount(0);
|
||||||
|
issue.setLicenseCount(0);
|
||||||
|
log.info("领料出库数据:{}", issue);
|
||||||
|
log.info("=============================");
|
||||||
|
return issue;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ public class RecognizerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void 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);
|
log.info("recognize: {}", recognize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,17 @@
|
|||||||
package org.dromara.test;
|
package org.dromara.test;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.dromara.common.core.utils.MapstructUtils;
|
||||||
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
|
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
|
||||||
import org.dromara.safety.service.IHseViolationRecordService;
|
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.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -20,6 +28,9 @@ public class ViolationRecordTest {
|
|||||||
@Resource
|
@Resource
|
||||||
private IHseViolationRecordService violationRecordService;
|
private IHseViolationRecordService violationRecordService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SysMenuMapper sysMenuMapper;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test() {
|
void test() {
|
||||||
List<HseViolationRecordCreateDto> list = new ArrayList<>();
|
List<HseViolationRecordCreateDto> list = new ArrayList<>();
|
||||||
@ -55,4 +66,26 @@ public class ViolationRecordTest {
|
|||||||
list.add(dto5);
|
list.add(dto5);
|
||||||
violationRecordService.insertByMonitor(list);
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdCsContractInformationService {
|
||||||
|
|
||||||
|
String selectNameByIds(String ids);
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdCustomerinformationService {
|
||||||
|
|
||||||
|
String selectNmaeByIds(String id);
|
||||||
|
|
||||||
|
String selectNmaeById(Long id);
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdJsCgJungonService {
|
||||||
|
|
||||||
|
String selectNameByIds(String ids);
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdProjectService {
|
||||||
|
|
||||||
|
String selectNmaeByIds(String ids);
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdPurchaseContractInformationService {
|
||||||
|
String selectNameByIds(String ids);
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdSupplierInfoService {
|
||||||
|
|
||||||
|
String selectNmaeByIds(String ids);
|
||||||
|
|
||||||
|
String selectNmaeById(Long id);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
public interface XzdSupplierOpenBankService {
|
||||||
|
|
||||||
|
String selectNameByIds(String ids);
|
||||||
|
}
|
||||||
@ -37,4 +37,36 @@ public interface TransConstant {
|
|||||||
*/
|
*/
|
||||||
String PROJECT_ID_TO_NAME = "project_id_to_name";
|
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,3 +5,10 @@ org.dromara.common.translation.core.impl.OssUrlTranslationImpl
|
|||||||
org.dromara.common.translation.core.impl.UserNameTranslationImpl
|
org.dromara.common.translation.core.impl.UserNameTranslationImpl
|
||||||
org.dromara.common.translation.core.impl.NicknameTranslationImpl
|
org.dromara.common.translation.core.impl.NicknameTranslationImpl
|
||||||
org.dromara.common.translation.core.impl.ProjectNameTranslationImpl
|
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
|
||||||
|
|||||||
@ -15,16 +15,31 @@
|
|||||||
system系统模块
|
system系统模块
|
||||||
</description>
|
</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>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud.ai</groupId>
|
||||||
|
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- Java WebSocket 标准API -->
|
<!-- Java WebSocket 标准API -->
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>javax.websocket</groupId>-->
|
<!-- <groupId>javax.websocket</groupId>-->
|
||||||
<!-- <artifactId>javax.websocket-api</artifactId>-->
|
<!-- <artifactId>javax.websocket-api</artifactId>-->
|
||||||
<!-- <version>1.1</version>-->
|
<!-- <version>1.1</version>-->
|
||||||
<!-- <scope>provided</scope>-->
|
<!-- <scope>provided</scope>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
@ -36,18 +51,18 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>com.drewnoakes</groupId>-->
|
<!-- <groupId>com.drewnoakes</groupId>-->
|
||||||
<!-- <artifactId>metadata-extractor</artifactId>-->
|
<!-- <artifactId>metadata-extractor</artifactId>-->
|
||||||
<!-- <version>2.18.0</version>-->
|
<!-- <version>2.18.0</version>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>technology.tabula</groupId>-->
|
<!-- <groupId>technology.tabula</groupId>-->
|
||||||
<!-- <artifactId>tabula</artifactId>-->
|
<!-- <artifactId>tabula</artifactId>-->
|
||||||
<!-- <version>1.0.4</version>-->
|
<!-- <version>1.0.4</version>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
<!-- JSON解析(FastJSON) -->
|
<!-- JSON解析(FastJSON) -->
|
||||||
@ -119,19 +134,18 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 支持中文字体 -->
|
<!-- 支持中文字体 -->
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>com.itextpdf</groupId>-->
|
<!-- <groupId>com.itextpdf</groupId>-->
|
||||||
<!-- <artifactId>itext-asian</artifactId>-->
|
<!-- <artifactId>itext-asian</artifactId>-->
|
||||||
<!-- <version>5.2.0</version>-->
|
<!-- <version>5.2.0</version>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
<!-- <!– iText –>-->
|
<!-- <!– iText –>-->
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>com.itextpdf</groupId>-->
|
<!-- <groupId>com.itextpdf</groupId>-->
|
||||||
<!-- <artifactId>itextpdf</artifactId>-->
|
<!-- <artifactId>itextpdf</artifactId>-->
|
||||||
<!-- <version>5.5.13.3</version>-->
|
<!-- <version>5.5.13.3</version>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
<!-- ZXing -->
|
<!-- ZXing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.zxing</groupId>
|
<groupId>com.google.zxing</groupId>
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,8 +3,12 @@ package org.dromara.app.controller;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.app.domain.SysPackage;
|
import org.dromara.app.domain.SysPackage;
|
||||||
|
import org.dromara.app.domain.bo.SysPackageBo;
|
||||||
|
import org.dromara.app.domain.vo.SysPackageVo;
|
||||||
import org.dromara.app.service.SysPackageServiceImpl;
|
import org.dromara.app.service.SysPackageServiceImpl;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.dromara.system.domain.vo.SysOssVo;
|
import org.dromara.system.domain.vo.SysOssVo;
|
||||||
import org.dromara.system.service.impl.SysOssServiceImpl;
|
import org.dromara.system.service.impl.SysOssServiceImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -26,6 +30,14 @@ public class SysPackageController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SysPackageServiceImpl sysPackageService;
|
private SysPackageServiceImpl sysPackageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo<SysPackageVo> list(SysPackageBo bo, PageQuery pageQuery) {
|
||||||
|
return sysPackageService.queryPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最新版本
|
* 获取最新版本
|
||||||
*/
|
*/
|
||||||
@ -42,7 +54,7 @@ public class SysPackageController {
|
|||||||
* 上传最新安装包及版本
|
* 上传最新安装包及版本
|
||||||
*/
|
*/
|
||||||
@GetMapping("/uploadNewVersion")
|
@GetMapping("/uploadNewVersion")
|
||||||
public R<SysPackage> uploadNewVersion(String version, String type, MultipartFile file) {
|
public R<SysPackage> uploadNewVersion(String version, String type, MultipartFile file, String context) {
|
||||||
String originalFileName = "apk/package/app-release.apk";
|
String originalFileName = "apk/package/app-release.apk";
|
||||||
|
|
||||||
// 先查询最新记录
|
// 先查询最新记录
|
||||||
@ -57,11 +69,11 @@ public class SysPackageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 分离事务:再处理数据库操作
|
// 分离事务:再处理数据库操作
|
||||||
return handleDatabaseOperations(version, type, upload, list);
|
return handleDatabaseOperations(version, type, upload, list, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public R<SysPackage> handleDatabaseOperations(String version, String type, SysOssVo upload, List<SysPackage> existingPackages) {
|
public R<SysPackage> handleDatabaseOperations(String version, String type, SysOssVo upload, List<SysPackage> existingPackages, String context) {
|
||||||
try {
|
try {
|
||||||
// 先删除旧文件记录
|
// 先删除旧文件记录
|
||||||
if (existingPackages != null && !existingPackages.isEmpty()) {
|
if (existingPackages != null && !existingPackages.isEmpty()) {
|
||||||
@ -78,6 +90,7 @@ public class SysPackageController {
|
|||||||
sysPackage.setFileId(upload.getOssId());
|
sysPackage.setFileId(upload.getOssId());
|
||||||
sysPackage.setFileUrl(upload.getUrl());
|
sysPackage.setFileUrl(upload.getUrl());
|
||||||
sysPackage.setType(type);
|
sysPackage.setType(type);
|
||||||
|
sysPackage.setContext(context);
|
||||||
|
|
||||||
boolean save = sysPackageService.save(sysPackage);
|
boolean save = sysPackageService.save(sysPackage);
|
||||||
if (!save) {
|
if (!save) {
|
||||||
|
|||||||
@ -30,4 +30,9 @@ public class SysPackage extends BaseEntity {
|
|||||||
|
|
||||||
private String fileUrl;
|
private String fileUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新内容
|
||||||
|
*/
|
||||||
|
private String context;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
package org.dromara.app.domain.bo;
|
||||||
|
|
||||||
|
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||||
|
import org.dromara.xzd.domain.XzdContractDetails;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@AutoMapper(target = XzdContractDetails.class, reverseConvertGenerate = false)
|
||||||
|
public class SysPackageBo extends BaseEntity {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0安卓1苹果2鸿蒙
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
private Long fileId;
|
||||||
|
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新内容
|
||||||
|
*/
|
||||||
|
private String context;
|
||||||
|
}
|
||||||
@ -27,4 +27,9 @@ public class SysPackageVo {
|
|||||||
|
|
||||||
@ExcelProperty("安装包地址")
|
@ExcelProperty("安装包地址")
|
||||||
private String fileUrl;
|
private String fileUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新内容
|
||||||
|
*/
|
||||||
|
private String context;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,24 @@
|
|||||||
package org.dromara.app.service;
|
package org.dromara.app.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.dromara.app.domain.SysPackage;
|
import org.dromara.app.domain.SysPackage;
|
||||||
|
import org.dromara.app.domain.bo.SysPackageBo;
|
||||||
|
import org.dromara.app.domain.vo.SysPackageVo;
|
||||||
import org.dromara.app.mapper.SysPackageMapper;
|
import org.dromara.app.mapper.SysPackageMapper;
|
||||||
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class SysPackageServiceImpl extends ServiceImpl<SysPackageMapper, SysPackage> {
|
public class SysPackageServiceImpl extends ServiceImpl<SysPackageMapper, SysPackage> {
|
||||||
|
|
||||||
|
public TableDataInfo<SysPackageVo> queryPageList(SysPackageBo bo, PageQuery pageQuery) {
|
||||||
|
// LambdaQueryWrapper<SysPackage> lqw = buildQueryWrapper(bo);
|
||||||
|
Page<SysPackageVo> result = baseMapper.selectVoPage(pageQuery.build(), new LambdaQueryWrapper<>());
|
||||||
|
// setValue(result.getRecords());
|
||||||
|
return TableDataInfo.build(result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
package org.dromara.bigscreen.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无人机消息redis订阅专用线程池
|
||||||
|
*/
|
||||||
|
@Configuration // 标记为配置类,让Spring扫描加载
|
||||||
|
@EnableAsync // 启用Spring异步功能(若项目已在启动类加过,此处可省略,但加上更稳妥)
|
||||||
|
public class AsyncConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义名为 "messageAsyncExecutor" 的线程池 Bean(与 @Async("messageAsyncExecutor") 对应)
|
||||||
|
*/
|
||||||
|
@Bean(name = "messageAsyncExecutor") // Bean名称必须是 "messageAsyncExecutor",大小写敏感
|
||||||
|
public Executor messageAsyncExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
|
||||||
|
// 1. 核心线程数:根据CPU核心数或业务量调整(示例:5个)
|
||||||
|
executor.setCorePoolSize(5);
|
||||||
|
// 2. 最大线程数:避免线程过多导致资源耗尽(示例:10个)
|
||||||
|
executor.setMaxPoolSize(10);
|
||||||
|
// 3. 任务队列容量:缓冲待处理的异步任务(示例:50个)
|
||||||
|
executor.setQueueCapacity(50);
|
||||||
|
// 4. 线程名前缀:便于日志排查(格式:message-async-1, message-async-2...)
|
||||||
|
executor.setThreadNamePrefix("message-async-");
|
||||||
|
// 5. 线程空闲时间:超过60秒空闲则销毁(释放资源)
|
||||||
|
executor.setKeepAliveSeconds(60);
|
||||||
|
// 6. 任务拒绝策略:队列满时,由提交任务的线程临时执行(避免消息丢失)
|
||||||
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
|
||||||
|
// 7. 初始化线程池(必须调用,否则线程池不会生效)
|
||||||
|
executor.initialize();
|
||||||
|
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,8 +12,6 @@ import org.springframework.data.redis.listener.PatternTopic;
|
|||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class RedisConfig {
|
public class RedisConfig {
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,100 @@
|
|||||||
|
package org.dromara.bigscreen.controller;
|
||||||
|
|
||||||
|
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.conditions.query.QueryWrapper;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.core.domain.dto.UserDTO;
|
||||||
|
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||||
|
import org.dromara.common.core.utils.StreamUtils;
|
||||||
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
import org.dromara.common.web.core.BaseController;
|
||||||
|
import org.dromara.system.service.impl.SysUserServiceImpl;
|
||||||
|
import org.dromara.warm.flow.core.FlowEngine;
|
||||||
|
import org.dromara.warm.flow.core.entity.User;
|
||||||
|
import org.dromara.warm.flow.core.enums.NodeType;
|
||||||
|
import org.dromara.warm.flow.orm.entity.FlowDefinition;
|
||||||
|
import org.dromara.warm.flow.orm.mapper.FlowDefinitionMapper;
|
||||||
|
import org.dromara.workflow.domain.bo.FlowTaskBo;
|
||||||
|
import org.dromara.workflow.domain.vo.FlowTaskVo;
|
||||||
|
import org.dromara.workflow.mapper.FlwTaskMapper;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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.RestController;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 个人首页接口
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/personalHome")
|
||||||
|
public class PersonalHomeController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FlwTaskMapper flwTaskMapper;
|
||||||
|
@Autowired
|
||||||
|
private FlowDefinitionMapper flowDefinitionMapper;
|
||||||
|
@Autowired
|
||||||
|
private SysUserServiceImpl userService;
|
||||||
|
|
||||||
|
@GetMapping("/getTaskList")
|
||||||
|
public R<List<FlowTaskVo>> getTaskList(FlowTaskBo flowTaskBo) {
|
||||||
|
QueryWrapper<FlowTaskBo> queryWrapper = new QueryWrapper<>();
|
||||||
|
List<Long> definitionIds = new ArrayList<>();
|
||||||
|
if (!"0".equals(flowTaskBo.getProjectId())){
|
||||||
|
List<FlowDefinition> flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper<FlowDefinition>()
|
||||||
|
.select(FlowDefinition::getId)
|
||||||
|
.like(FlowDefinition::getFlowCode, flowTaskBo.getProjectId()));
|
||||||
|
if (flowDefinitions != null && !flowDefinitions.isEmpty()) {
|
||||||
|
flowDefinitions.forEach(flowDefinition -> {
|
||||||
|
definitionIds.add(flowDefinition.getId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (definitionIds.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey());
|
||||||
|
queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr());
|
||||||
|
queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus());
|
||||||
|
List<FlowTaskVo> page = this.getFlowTaskDefinitionIdsVoPage(definitionIds, queryWrapper);
|
||||||
|
return R.ok(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FlowTaskVo> getFlowTaskDefinitionIdsVoPage(List<Long> definitionIds, QueryWrapper<FlowTaskBo> queryWrapper) {
|
||||||
|
List<FlowTaskVo> records = flwTaskMapper.getListRunTaskDefinitionInfo(definitionIds, queryWrapper);
|
||||||
|
if (CollUtil.isNotEmpty(records)) {
|
||||||
|
List<Long> taskIds = StreamUtils.toList(records, FlowTaskVo::getId);
|
||||||
|
Map<Long, List<UserDTO>> listMap = currentTaskAllUser(taskIds);
|
||||||
|
records.forEach(t -> {
|
||||||
|
List<UserDTO> userList = listMap.getOrDefault(t.getId(), Collections.emptyList());
|
||||||
|
if (CollUtil.isNotEmpty(userList)) {
|
||||||
|
t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId())));
|
||||||
|
t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Long, List<UserDTO>> currentTaskAllUser(List<Long> taskIdList) {
|
||||||
|
Map<Long, List<UserDTO>> map = new HashMap<>();
|
||||||
|
// 获取与当前任务关联的用户列表
|
||||||
|
List<User> associatedUsers = FlowEngine.userService().getByAssociateds(taskIdList);
|
||||||
|
Map<Long, List<User>> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated);
|
||||||
|
for (Map.Entry<Long, List<User>> entry : listMap.entrySet()) {
|
||||||
|
List<User> value = entry.getValue();
|
||||||
|
if (CollUtil.isNotEmpty(value)) {
|
||||||
|
List<UserDTO> userDtoList = userService.selectListByIds(StreamUtils.toList(value, e -> Convert.toLong(e.getProcessedBy())));
|
||||||
|
map.put(entry.getKey(), userDtoList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -6,13 +6,11 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.dromara.bigscreen.domain.dto.ProjectImageProgressDetailReq;
|
||||||
import org.dromara.bigscreen.domain.dto.ProjectUpdateDto;
|
import org.dromara.bigscreen.domain.dto.ProjectUpdateDto;
|
||||||
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
|
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
|
||||||
import org.dromara.bigscreen.domain.dto.Ys7DeviceUpdateReq;
|
import org.dromara.bigscreen.domain.dto.Ys7DeviceUpdateReq;
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
|
import org.dromara.bigscreen.domain.vo.*;
|
||||||
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.service.ProjectBigScreenService;
|
import org.dromara.bigscreen.service.ProjectBigScreenService;
|
||||||
import org.dromara.common.core.constant.HttpStatus;
|
import org.dromara.common.core.constant.HttpStatus;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
@ -20,7 +18,12 @@ import org.dromara.common.core.exception.ServiceException;
|
|||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
import org.dromara.common.log.annotation.Log;
|
import org.dromara.common.log.annotation.Log;
|
||||||
import org.dromara.common.log.enums.BusinessType;
|
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.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.gps.domain.bo.GpsEquipmentBo;
|
||||||
import org.dromara.land.domain.BusLandBlock;
|
import org.dromara.land.domain.BusLandBlock;
|
||||||
import org.dromara.land.domain.BusLandTransferLedger;
|
import org.dromara.land.domain.BusLandTransferLedger;
|
||||||
@ -33,8 +36,8 @@ import org.dromara.manager.ys7manager.Ys7Manager;
|
|||||||
import org.dromara.other.domain.OthYs7Device;
|
import org.dromara.other.domain.OthYs7Device;
|
||||||
import org.dromara.other.service.IOthYs7DeviceService;
|
import org.dromara.other.service.IOthYs7DeviceService;
|
||||||
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
|
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.domain.vo.projectnews.BusProjectNewsVo;
|
||||||
import org.dromara.project.service.IBusProjectNewsService;
|
|
||||||
import org.dromara.project.service.IBusProjectService;
|
import org.dromara.project.service.IBusProjectService;
|
||||||
import org.dromara.quality.domain.dto.qualityinspection.QltQualityInspectionGisReq;
|
import org.dromara.quality.domain.dto.qualityinspection.QltQualityInspectionGisReq;
|
||||||
import org.dromara.quality.domain.vo.qualityinspection.QltQualityInspectionListGisVo;
|
import org.dromara.quality.domain.vo.qualityinspection.QltQualityInspectionListGisVo;
|
||||||
@ -76,8 +79,6 @@ public class ProjectBigScreenController extends BaseController {
|
|||||||
|
|
||||||
private final IBusProjectService projectService;
|
private final IBusProjectService projectService;
|
||||||
|
|
||||||
private final IBusProjectNewsService projectNewsService;
|
|
||||||
|
|
||||||
private final IQltQualityInspectionService qualityInspectionService;
|
private final IQltQualityInspectionService qualityInspectionService;
|
||||||
|
|
||||||
private final IHseSafetyInspectionService safetyInspectionService;
|
private final IHseSafetyInspectionService safetyInspectionService;
|
||||||
@ -86,6 +87,8 @@ public class ProjectBigScreenController extends BaseController {
|
|||||||
|
|
||||||
private final BusLandTransferLedgerSonServiceImpl busLandTransferLedgerSonService;
|
private final BusLandTransferLedgerSonServiceImpl busLandTransferLedgerSonService;
|
||||||
|
|
||||||
|
private final IFacRedLineService facRedLineService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询项目土地统计
|
* 查询项目土地统计
|
||||||
*/
|
*/
|
||||||
@ -218,6 +221,15 @@ public class ProjectBigScreenController extends BaseController {
|
|||||||
return R.ok(projectBigScreenService.getProjectImageProgress(projectId));
|
return R.ok(projectBigScreenService.getProjectImageProgress(projectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目形象进度详情
|
||||||
|
*/
|
||||||
|
@GetMapping("/imageProgress/detail")
|
||||||
|
public R<List<ProjectImageProgressDetailVo>> getProjectImageProgressDetail(@Validated ProjectImageProgressDetailReq req) {
|
||||||
|
List<ProjectImageProgressDetailVo> list = projectBigScreenService.getProjectImageProgressDetail(req);
|
||||||
|
return R.ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询项目概括
|
* 查询项目概括
|
||||||
*/
|
*/
|
||||||
@ -334,6 +346,22 @@ public class ProjectBigScreenController extends BaseController {
|
|||||||
return toAjax(projectService.updatePosition(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询项目地域分散图
|
* 查询项目地域分散图
|
||||||
@ -372,4 +400,12 @@ public class ProjectBigScreenController extends BaseController {
|
|||||||
return R.ok(projectBigScreenService.getInfoData(req));
|
return R.ok(projectBigScreenService.getInfoData(req));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询设施-红线列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/redLine/list")
|
||||||
|
public TableDataInfo<FacRedLineVo> list(FacRedLineBo bo, PageQuery pageQuery) {
|
||||||
|
return facRedLineService.queryPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
package org.dromara.bigscreen.domain.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-03 18:53
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ProjectImageProgressDetailReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = -2943732963243838829L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "项目主键不能为空")
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度名称
|
||||||
|
*/
|
||||||
|
@NotNull(message = "进度名称不能为空")
|
||||||
|
private String progressName;
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package org.dromara.bigscreen.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-03 16:34
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ProjectImageProgressDetailVo implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = -8317739851423164942L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度名称
|
||||||
|
*/
|
||||||
|
private String progressName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计划进度
|
||||||
|
*/
|
||||||
|
private BigDecimal planProgress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际进度
|
||||||
|
*/
|
||||||
|
private BigDecimal actualProgress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总进度
|
||||||
|
*/
|
||||||
|
private BigDecimal totalProgress;
|
||||||
|
|
||||||
|
}
|
||||||
@ -26,6 +26,16 @@ public class ProjectImageProgressVo implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private BigDecimal progressTotal;
|
private BigDecimal progressTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产值
|
||||||
|
*/
|
||||||
|
private BigDecimal valueTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产值百分比
|
||||||
|
*/
|
||||||
|
private BigDecimal valuePercentage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 场区百分比
|
* 场区百分比
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -5,7 +5,10 @@ import cn.hutool.json.JSONObject;
|
|||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.bigscreen.service.IAsyncMessageHandlerService;
|
||||||
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
||||||
|
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
|
||||||
import org.dromara.common.websocket.utils.WebSocketUtils;
|
import org.dromara.common.websocket.utils.WebSocketUtils;
|
||||||
import org.dromara.drone.domain.DroProjectDrone;
|
import org.dromara.drone.domain.DroProjectDrone;
|
||||||
import org.dromara.drone.service.IDroProjectDroneService;
|
import org.dromara.drone.service.IDroProjectDroneService;
|
||||||
@ -17,25 +20,18 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redis消息监听器,用于处理订阅频道收到的消息
|
* Redis消息监听器,用于处理订阅频道收到的消息
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class RedisMessageListener implements MessageListener {
|
public class RedisMessageListener implements MessageListener {
|
||||||
|
|
||||||
@Lazy
|
// 注入异步消息处理服务
|
||||||
private final StringRedisTemplate stringRedisTemplate;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy
|
private IAsyncMessageHandlerService asyncMessageHandlerService;
|
||||||
private IDroProjectDroneService droProjectDroneService;
|
|
||||||
|
|
||||||
// 构造函数注入StringRedisTemplate
|
|
||||||
public RedisMessageListener(StringRedisTemplate stringRedisTemplate) {
|
|
||||||
this.stringRedisTemplate = stringRedisTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理接收到的消息
|
* 处理接收到的消息
|
||||||
* @param message 消息对象
|
* @param message 消息对象
|
||||||
@ -43,50 +39,19 @@ public class RedisMessageListener implements MessageListener {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Message message, byte[] pattern) {
|
public void onMessage(Message message, byte[] pattern) {
|
||||||
// 处理消息
|
try {
|
||||||
// System.out.println("返回:"+stringRedisTemplate.getStringSerializer().deserialize(message.getBody()));
|
// 1. 快速日志记录(证明监听到消息)
|
||||||
String gateway = JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getStr("gateway");
|
log.info("【Redis消息监听】收到消息,长度:{}字节,提交异步处理", message.getBody().length);
|
||||||
String key = "";
|
|
||||||
if (JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getJSONObject("data").get("job_number") != null) {
|
|
||||||
key = "wrj:osd1:"+gateway;
|
|
||||||
}else if (JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getJSONObject("data").get("wireless_link") != null) {
|
|
||||||
key = "wrj:osd2:"+gateway;
|
|
||||||
}else if (JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getJSONObject("data").get("network_state") != null) {
|
|
||||||
key = "wrj:osd3:"+gateway;
|
|
||||||
DroProjectDrone droProjectDrone = droProjectDroneService.getBaseMapper().selectOne(new LambdaQueryWrapper<DroProjectDrone>().eq(DroProjectDrone::getDroneSn, gateway));
|
|
||||||
setWs(message, gateway, droProjectDrone);
|
|
||||||
}else{
|
|
||||||
key = "wrj:osd4:"+gateway;
|
|
||||||
DroProjectDrone droProjectDrone = droProjectDroneService.getBaseMapper().selectOne(new LambdaQueryWrapper<DroProjectDrone>().eq(DroProjectDrone::getDroneSn, gateway));
|
|
||||||
setWs(message, gateway, droProjectDrone);
|
|
||||||
|
|
||||||
|
// 2. 提交给异步服务处理(核心:线程分离,监听线程立即返回)
|
||||||
|
asyncMessageHandlerService.handleRedisMessageAsync(message);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获提交过程中的异常(如异步服务注入失败)
|
||||||
|
log.error("【Redis消息监听】提交异步处理失败", e);
|
||||||
}
|
}
|
||||||
stringRedisTemplate
|
|
||||||
.opsForValue()
|
|
||||||
.set(key
|
|
||||||
, Objects.requireNonNull(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setWs(Message message, String gateway, DroProjectDrone droProjectDrone) {
|
|
||||||
String pushContent = buildPushMessage(gateway,stringRedisTemplate.getStringSerializer().deserialize(message.getBody()), droProjectDrone.getProjectId());
|
|
||||||
// 发送给指定用户(equipment.getUserId())
|
|
||||||
WebSocketMessageDto messageDto = new WebSocketMessageDto();
|
|
||||||
messageDto.setMessage(pushContent);
|
|
||||||
messageDto.setSessionKeys(Collections.singletonList(droProjectDrone.getProjectId()));
|
|
||||||
WebSocketUtils.publishMessage(messageDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildPushMessage(String key, String message, Long projectId) {
|
|
||||||
JSONObject messageObj = new JSONObject();
|
|
||||||
messageObj.put("type", "wrj_DATA_UPDATE");
|
|
||||||
messageObj.put("projectId",projectId.toString());
|
|
||||||
messageObj.put("clientId",key);
|
|
||||||
// 位置信息
|
|
||||||
JSONObject locationObj = new JSONObject();
|
|
||||||
locationObj.put("latitude", JSONUtil.parseObj(message).getJSONObject("data").get("latitude").toString()); // 纬度
|
|
||||||
locationObj.put("longitude", JSONUtil.parseObj(message).getJSONObject("data").get("longitude").toString()); // 经度
|
|
||||||
messageObj.put("location", locationObj);
|
|
||||||
return messageObj.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
package org.dromara.bigscreen.manager;
|
package org.dromara.bigscreen.manager;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.websocket.websocket.service.InitOnStartWebSocketServer;
|
||||||
import org.dromara.common.redis.utils.RedisUtils;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.dromara.drone.domain.DroProjectDrone;
|
||||||
import org.dromara.drone.service.IDroProjectDroneService;
|
import org.dromara.drone.service.IDroProjectDroneService;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.data.redis.listener.PatternTopic;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.listener.ChannelTopic;
|
||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -39,17 +41,24 @@ public class RedisSubscribeManager {
|
|||||||
private IDroProjectDroneService droProjectDroneService;
|
private IDroProjectDroneService droProjectDroneService;
|
||||||
|
|
||||||
// 3. 维护已订阅的主题关系:key=完整主题(如wrj:8UUXN4P00A06NK),value=对应的PatternTopic
|
// 3. 维护已订阅的主题关系:key=完整主题(如wrj:8UUXN4P00A06NK),value=对应的PatternTopic
|
||||||
private final Map<String, PatternTopic> subscribedTopics = new ConcurrentHashMap<>();
|
private final Map<String, ChannelTopic> subscribedTopics = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 定义锁对象
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
// 注入 RedisConnectionFactory
|
||||||
|
@Resource
|
||||||
|
private RedisConnectionFactory redisConnectionFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目启动后立即执行一次订阅(避免等待定时任务首次执行)
|
* 项目启动后立即执行一次订阅(避免等待定时任务首次执行)
|
||||||
*/
|
*/
|
||||||
@PostConstruct
|
// @PostConstruct
|
||||||
public void initSubscribe() {
|
public void initSubscribe() {
|
||||||
log.info("项目启动,初始化Redis订阅...");
|
log.info("项目启动,初始化Redis订阅...");
|
||||||
// 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix)
|
// 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix)
|
||||||
List<String> latestKeys = droProjectDroneService.getTopicsByKeyPrefix();
|
List<DroProjectDrone> latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper<DroProjectDrone>().groupBy(DroProjectDrone::getDroneSn));
|
||||||
if (latestKeys == null || latestKeys.isEmpty()) {
|
if (latestKeys == null || latestKeys.isEmpty()) {
|
||||||
log.warn("未获取到任何主题,将取消所有现有订阅");
|
log.warn("未获取到任何主题,将取消所有现有订阅");
|
||||||
cancelAllSubscribes();
|
cancelAllSubscribes();
|
||||||
@ -58,11 +67,13 @@ public class RedisSubscribeManager {
|
|||||||
|
|
||||||
// 步骤2:构建最新的完整主题(格式:wrj:key)
|
// 步骤2:构建最新的完整主题(格式:wrj:key)
|
||||||
Set<String> latestFullTopics = new HashSet<>();
|
Set<String> latestFullTopics = new HashSet<>();
|
||||||
for (String key : latestKeys) {
|
for (DroProjectDrone key : latestKeys) {
|
||||||
latestFullTopics.add("wrj:osd1:" + key);
|
latestFullTopics.add("wrj:osd1:" + key.getDroneSn());
|
||||||
latestFullTopics.add("wrj:osd2:" + key);
|
latestFullTopics.add("wrj:osd2:" + key.getDroneSn());
|
||||||
latestFullTopics.add("wrj:osd3:" + key);
|
latestFullTopics.add("wrj:osd3:" + key.getDroneSn());
|
||||||
latestFullTopics.add("wrj:osd4:" + key);
|
if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) {
|
||||||
|
latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +88,7 @@ public class RedisSubscribeManager {
|
|||||||
* 4. 定时任务:定期更新订阅(每5分钟执行一次,可调整cron表达式)
|
* 4. 定时任务:定期更新订阅(每5分钟执行一次,可调整cron表达式)
|
||||||
* cron格式:秒 分 时 日 月 周 年(示例:0 0/5 * * * ? 表示每5分钟)
|
* cron格式:秒 分 时 日 月 周 年(示例:0 0/5 * * * ? 表示每5分钟)
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0 0/6 * * * ?")
|
// @Scheduled(cron = "0 0/6 * * * ?")
|
||||||
public void dynamicUpdateSubscribe() {
|
public void dynamicUpdateSubscribe() {
|
||||||
try {
|
try {
|
||||||
Object object = RedisUtils.getCacheObject("xmjdap:ws");
|
Object object = RedisUtils.getCacheObject("xmjdap:ws");
|
||||||
@ -94,7 +105,7 @@ public class RedisSubscribeManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix)
|
// 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix)
|
||||||
List<String> latestKeys = droProjectDroneService.getTopicsByKeyPrefix();
|
List<DroProjectDrone> latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper<DroProjectDrone>().groupBy(DroProjectDrone::getDroneSn));
|
||||||
if (latestKeys == null || latestKeys.isEmpty()) {
|
if (latestKeys == null || latestKeys.isEmpty()) {
|
||||||
log.warn("定时任务未获取到任何主题,将取消所有现有订阅");
|
log.warn("定时任务未获取到任何主题,将取消所有现有订阅");
|
||||||
cancelAllSubscribes();
|
cancelAllSubscribes();
|
||||||
@ -103,11 +114,13 @@ public class RedisSubscribeManager {
|
|||||||
|
|
||||||
// 步骤2:构建最新的完整主题(格式:wrj:key)
|
// 步骤2:构建最新的完整主题(格式:wrj:key)
|
||||||
Set<String> latestFullTopics = new HashSet<>();
|
Set<String> latestFullTopics = new HashSet<>();
|
||||||
for (String key : latestKeys) {
|
for (DroProjectDrone key : latestKeys) {
|
||||||
latestFullTopics.add("wrj:osd1:" + key);
|
latestFullTopics.add("wrj:osd1:" + key.getDroneSn());
|
||||||
latestFullTopics.add("wrj:osd2:" + key);
|
latestFullTopics.add("wrj:osd2:" + key.getDroneSn());
|
||||||
latestFullTopics.add("wrj:osd3:" + key);
|
latestFullTopics.add("wrj:osd3:" + key.getDroneSn());
|
||||||
latestFullTopics.add("wrj:osd4:" + key);
|
if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) {
|
||||||
|
latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 步骤3:对比现有订阅,删除过期主题
|
// 步骤3:对比现有订阅,删除过期主题
|
||||||
@ -123,10 +136,71 @@ public class RedisSubscribeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Scheduled(cron = "0/10 * * * * ?")
|
||||||
|
// @JobExecutor(name = "ueWsConnect")
|
||||||
|
public void ueWsConnect() {
|
||||||
|
synchronized (lock) { // 确保同一时间只有一个线程修改
|
||||||
|
try {
|
||||||
|
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
|
||||||
|
Object object = RedisUtils.getCacheObject("xmjdap:ws");
|
||||||
|
log.info("开始执行Redis订阅更新定时任务...");
|
||||||
|
if (onlineCount == 0 && object == null) {
|
||||||
|
cancelAllSubscribes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (object != null) {
|
||||||
|
long oldTime = Long.parseLong(String.valueOf(object));
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - oldTime > 300000) {
|
||||||
|
RedisUtils.deleteObject("xmjdap:ws");
|
||||||
|
cancelAllSubscribes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("Redis时间缓存更新定时任务");
|
||||||
|
}
|
||||||
|
// 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix)
|
||||||
|
List<DroProjectDrone> latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper<DroProjectDrone>().groupBy(DroProjectDrone::getDroneSn));
|
||||||
|
if (latestKeys == null || latestKeys.isEmpty()) {
|
||||||
|
log.warn("定时任务未获取到任何主题,将取消所有现有订阅");
|
||||||
|
cancelAllSubscribes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤2:构建最新的完整主题(格式:wrj:key)
|
||||||
|
Set<String> latestFullTopics = new HashSet<>();
|
||||||
|
for (DroProjectDrone key : latestKeys) {
|
||||||
|
latestFullTopics.add("wrj:osd1:" + key.getDroneSn());
|
||||||
|
latestFullTopics.add("wrj:osd2:" + key.getDroneSn());
|
||||||
|
latestFullTopics.add("wrj:osd3:" + key.getDroneSn());
|
||||||
|
if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) {
|
||||||
|
latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤3:对比现有订阅,删除过期主题
|
||||||
|
cancelExpiredSubscribes(latestFullTopics);
|
||||||
|
|
||||||
|
// 步骤4:对比现有订阅,新增未订阅的主题
|
||||||
|
addNewSubscribes(latestFullTopics);
|
||||||
|
|
||||||
|
log.info("Redis订阅更新完成,当前已订阅主题数:{}", subscribedTopics.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Redis订阅更新定时任务执行失败", e);
|
||||||
|
// 异常时不修改现有订阅,避免误删
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消过期的订阅(现有订阅不在最新列表中的主题)
|
* 取消过期的订阅(现有订阅不在最新列表中的主题)
|
||||||
*/
|
*/
|
||||||
private void cancelExpiredSubscribes(Set<String> latestFullTopics) {
|
public void cancelExpiredSubscribes(Set<String> latestFullTopics) {
|
||||||
|
synchronized (lock) { // 确保同一时间只有一个线程修改
|
||||||
|
if (!redisMessageListenerContainer.isRunning()) {
|
||||||
|
log.error("RedisMessageListenerContainer 未运行,无法取消订阅");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 遍历现有订阅,找出需要删除的主题
|
// 遍历现有订阅,找出需要删除的主题
|
||||||
Set<String> expiredTopics = subscribedTopics.keySet().stream()
|
Set<String> expiredTopics = subscribedTopics.keySet().stream()
|
||||||
.filter(topic -> !latestFullTopics.contains(topic))
|
.filter(topic -> !latestFullTopics.contains(topic))
|
||||||
@ -139,7 +213,7 @@ public class RedisSubscribeManager {
|
|||||||
|
|
||||||
// 取消每个过期主题的订阅
|
// 取消每个过期主题的订阅
|
||||||
for (String expiredTopic : expiredTopics) {
|
for (String expiredTopic : expiredTopics) {
|
||||||
PatternTopic topic = subscribedTopics.get(expiredTopic);
|
ChannelTopic topic = subscribedTopics.get(expiredTopic);
|
||||||
if (topic != null) {
|
if (topic != null) {
|
||||||
// 从容器中移除监听器(取消订阅)
|
// 从容器中移除监听器(取消订阅)
|
||||||
redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, topic);
|
redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, topic);
|
||||||
@ -148,11 +222,25 @@ public class RedisSubscribeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增未订阅的主题(最新列表中有但现有订阅没有的主题)
|
* 新增未订阅的主题(最新列表中有但现有订阅没有的主题)
|
||||||
*/
|
*/
|
||||||
private void addNewSubscribes(Set<String> latestFullTopics) {
|
public void addNewSubscribes(Set<String> latestFullTopics) {
|
||||||
|
synchronized (lock) { // 确保同一时间只有一个线程修改
|
||||||
|
if (!redisMessageListenerContainer.isRunning()) {
|
||||||
|
log.error("RedisMessageListenerContainer 未运行,无法添加订阅");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// // 测试 Redis 连接
|
||||||
|
// try (RedisConnection conn = redisConnectionFactory.getConnection()) {
|
||||||
|
// conn.ping(); // 发送 ping 命令,验证连接
|
||||||
|
// log.info("Redis 连接正常,可执行命令");
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("Redis 连接失败,无法订阅主题", e);
|
||||||
|
// return; // 连接失败,终止订阅
|
||||||
|
// }
|
||||||
// 遍历最新主题,找出需要新增的主题
|
// 遍历最新主题,找出需要新增的主题
|
||||||
Set<String> newTopics = latestFullTopics.stream()
|
Set<String> newTopics = latestFullTopics.stream()
|
||||||
.filter(topic -> !subscribedTopics.containsKey(topic))
|
.filter(topic -> !subscribedTopics.containsKey(topic))
|
||||||
@ -162,16 +250,21 @@ public class RedisSubscribeManager {
|
|||||||
log.info("无新增订阅主题,无需添加");
|
log.info("无新增订阅主题,无需添加");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Object delegate = redisMessageListenerAdapter.getDelegate();
|
||||||
|
// log.info("适配器绑定的监听器实例:{}",
|
||||||
|
// delegate.getClass().getName());
|
||||||
|
|
||||||
// 为每个新主题添加订阅
|
// 为每个新主题添加订阅
|
||||||
for (String newTopic : newTopics) {
|
for (String newTopic : newTopics) {
|
||||||
PatternTopic topic = new PatternTopic(newTopic);
|
ChannelTopic topic = new ChannelTopic(newTopic);
|
||||||
// 向容器中添加监听器(新增订阅)
|
// 向容器中添加监听器(新增订阅)
|
||||||
|
// log.info("添加订阅前,容器状态:{}", redisMessageListenerContainer.isRunning() ? "运行中" : "已停止");
|
||||||
redisMessageListenerContainer.addMessageListener(redisMessageListenerAdapter, topic);
|
redisMessageListenerContainer.addMessageListener(redisMessageListenerAdapter, topic);
|
||||||
subscribedTopics.put(newTopic, topic);
|
subscribedTopics.put(newTopic, topic);
|
||||||
log.info("已新增订阅:{}", newTopic);
|
log.info("已新增订阅:{}", newTopic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消所有现有订阅(兜底方法)
|
* 取消所有现有订阅(兜底方法)
|
||||||
@ -181,7 +274,7 @@ public class RedisSubscribeManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 遍历所有已订阅主题,取消订阅
|
// 遍历所有已订阅主题,取消订阅
|
||||||
for (Map.Entry<String, PatternTopic> entry : subscribedTopics.entrySet()) {
|
for (Map.Entry<String, ChannelTopic> entry : subscribedTopics.entrySet()) {
|
||||||
redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, entry.getValue());
|
redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, entry.getValue());
|
||||||
log.info("已取消订阅:{}", entry.getKey());
|
log.info("已取消订阅:{}", entry.getKey());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
package org.dromara.bigscreen.manager;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目启动完成后执行 Redis 订阅初始化
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RedisSubscribeStartupRunner implements ApplicationRunner {
|
||||||
|
|
||||||
|
// 注入 Redis 订阅管理器
|
||||||
|
@Resource
|
||||||
|
private RedisSubscribeManager redisSubscribeManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目完全启动后执行(替代 @PostConstruct)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) throws Exception {
|
||||||
|
// 此时 Spring 容器已完全启动,所有组件就绪
|
||||||
|
redisSubscribeManager.initSubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package org.dromara.bigscreen.service;
|
||||||
|
|
||||||
|
import org.springframework.data.redis.connection.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步消息处理服务接口(扩展原有接口,增加Redis消息处理方法)
|
||||||
|
*/
|
||||||
|
public interface IAsyncMessageHandlerService {
|
||||||
|
/**
|
||||||
|
* 异步处理Redis消息(核心:接收Message对象,处理业务逻辑)
|
||||||
|
* @param message Redis消息对象
|
||||||
|
*/
|
||||||
|
void handleRedisMessageAsync(Message message);
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
package org.dromara.bigscreen.service;
|
package org.dromara.bigscreen.service;
|
||||||
|
|
||||||
|
import org.dromara.bigscreen.domain.dto.ProjectImageProgressDetailReq;
|
||||||
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
|
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
|
||||||
|
import org.dromara.bigscreen.domain.vo.ProjectImageProgressDetailVo;
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
|
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
|
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectSafetyInspectionVo;
|
import org.dromara.bigscreen.domain.vo.ProjectSafetyInspectionVo;
|
||||||
@ -66,6 +68,14 @@ public interface ProjectBigScreenService {
|
|||||||
*/
|
*/
|
||||||
List<ProjectImageProgressVo> getProjectImageProgress(Long projectId);
|
List<ProjectImageProgressVo> getProjectImageProgress(Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目形象进度详情
|
||||||
|
*
|
||||||
|
* @param req 项目形象进度名称
|
||||||
|
* @return 项目形象进度详情
|
||||||
|
*/
|
||||||
|
List<ProjectImageProgressDetailVo> getProjectImageProgressDetail(ProjectImageProgressDetailReq req);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取项目概括
|
* 获取项目概括
|
||||||
*
|
*
|
||||||
@ -79,6 +89,7 @@ public interface ProjectBigScreenService {
|
|||||||
void setList(GpsEquipmentBo bo);
|
void setList(GpsEquipmentBo bo);
|
||||||
|
|
||||||
List<Map<String, Object>> getClientList(Long projectId);
|
List<Map<String, Object>> getClientList(Long projectId);
|
||||||
|
List<String> getUeClientList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新无人机缓存
|
* 更新无人机缓存
|
||||||
|
|||||||
@ -0,0 +1,181 @@
|
|||||||
|
package org.dromara.bigscreen.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.bigscreen.service.IAsyncMessageHandlerService;
|
||||||
|
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
||||||
|
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
|
||||||
|
import org.dromara.common.websocket.utils.WebSocketUtils;
|
||||||
|
import org.dromara.drone.domain.DroProjectDrone;
|
||||||
|
import org.dromara.drone.mapper.DroProjectDroneMapper;
|
||||||
|
import org.dromara.websocket.websocket.service.InitOnStartWebSocketServer;
|
||||||
|
import org.springframework.data.redis.connection.Message;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步消息处理实现(核心:原onMessage逻辑迁移至此)
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class AsyncMessageHandlerServiceImpl implements IAsyncMessageHandlerService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DroProjectDroneMapper droProjectDroneMapper; // 直接注入Mapper,减少Service层调用开销
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步处理消息(指定线程池:messageAsyncExecutor)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Async("messageAsyncExecutor")
|
||||||
|
public void handleRedisMessageAsync(Message message) {
|
||||||
|
try {
|
||||||
|
// 1. 解析消息体(只解析一次,避免重复操作)
|
||||||
|
byte[] messageBody = message.getBody();
|
||||||
|
if (messageBody == null || messageBody.length == 0) {
|
||||||
|
log.warn("【异步消息处理】消息体为空,忽略处理");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String messageContent = stringRedisTemplate.getStringSerializer().deserialize(messageBody);
|
||||||
|
if (messageContent == null) {
|
||||||
|
log.warn("【异步消息处理】消息反序列化失败,内容:{}", new String(messageBody));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 解析JSON(使用框架兼容的JSONUtil,处理可能的解析异常)
|
||||||
|
JSONObject messageJson;
|
||||||
|
try {
|
||||||
|
messageJson = JSONUtil.parseObj(messageContent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("【异步消息处理】JSON解析失败,内容:{}", messageContent, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取gateway字段(核心业务参数)
|
||||||
|
String gateway = messageJson.getStr("gateway");
|
||||||
|
if (gateway == null) {
|
||||||
|
log.warn("【异步消息处理】消息缺少gateway字段,内容:{}", messageContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 解析data字段,判断key类型
|
||||||
|
JSONObject dataJson = messageJson.getJSONObject("data");
|
||||||
|
if (dataJson == null) {
|
||||||
|
log.warn("【异步消息处理】消息缺少data字段,gateway:{}", gateway);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = "";
|
||||||
|
DroProjectDrone droProjectDrone = null;
|
||||||
|
boolean isOsd4 = false;
|
||||||
|
|
||||||
|
// 5. 根据data字段内容判断key类型,并查询无人机信息
|
||||||
|
if (dataJson.get("job_number") != null) {
|
||||||
|
key = "wrj:osd1:" + gateway;
|
||||||
|
} else if (dataJson.get("wireless_link") != null) {
|
||||||
|
key = "wrj:osd2:" + gateway;
|
||||||
|
} else if (dataJson.get("network_state") != null) {
|
||||||
|
key = "wrj:osd3:" + gateway;
|
||||||
|
// 查询无人机信息(使用LambdaQueryWrapper,框架规范)
|
||||||
|
droProjectDrone = droProjectDroneMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<DroProjectDrone>()
|
||||||
|
.eq(DroProjectDrone::getDroneSn, gateway)
|
||||||
|
);
|
||||||
|
// log.info("【异步消息处理】osd3类型消息,gateway:{}", gateway);
|
||||||
|
// 调用setWs方法(非osd4类型)
|
||||||
|
setWs(messageContent, gateway, droProjectDrone, false);
|
||||||
|
} else {
|
||||||
|
key = "wrj:osd4:" + gateway;
|
||||||
|
droProjectDrone = droProjectDroneMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<DroProjectDrone>()
|
||||||
|
.eq(DroProjectDrone::getAirplaneSn, gateway)
|
||||||
|
);
|
||||||
|
// log.info("【异步消息处理】osd4类型消息,gateway:{}", gateway);
|
||||||
|
isOsd4 = true;
|
||||||
|
// 调用setWs方法(osd4类型)
|
||||||
|
setWs(messageContent, gateway, droProjectDrone, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 存储消息到Redis(使用Objects.requireNonNull避免空指针)
|
||||||
|
stringRedisTemplate.opsForValue().set(key, Objects.requireNonNull(messageContent));
|
||||||
|
// log.info("【异步消息处理】Redis存储成功,key:{}", key);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获所有异常,避免线程终止
|
||||||
|
log.error("【异步消息处理】整体逻辑异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setWs(String message, String gateway, DroProjectDrone droProjectDrone, boolean b) {
|
||||||
|
//判断是否有连接
|
||||||
|
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
|
||||||
|
if (!sessionsAll.isEmpty()) {
|
||||||
|
String pushContent = buildPushMessage(gateway, message, droProjectDrone.getProjectId());
|
||||||
|
// 发送给指定用户(equipment.getUserId())
|
||||||
|
WebSocketMessageDto messageDto = new WebSocketMessageDto();
|
||||||
|
messageDto.setMessage(pushContent);
|
||||||
|
messageDto.setSessionKeys(Collections.singletonList(droProjectDrone.getProjectId()));
|
||||||
|
WebSocketUtils.publishMessage(messageDto);
|
||||||
|
System.out.println("大屏已推送消息");
|
||||||
|
}
|
||||||
|
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
|
||||||
|
if (onlineCount > 0){
|
||||||
|
String modleId = b ? droProjectDrone.getAirplaneModleId() : droProjectDrone.getDroneModleId();
|
||||||
|
if (modleId != null) {
|
||||||
|
String ued = ueStructureJsonMessage(message, modleId);
|
||||||
|
InitOnStartWebSocketServer.sendToAll(ued);
|
||||||
|
System.out.println("ue已推送消息");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildPushMessage(String key, String message, Long projectId) {
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("type", "wrj_DATA_UPDATE");
|
||||||
|
messageObj.put("projectId",projectId.toString());
|
||||||
|
messageObj.put("clientId",key);
|
||||||
|
// 位置信息
|
||||||
|
JSONObject locationObj = new JSONObject();
|
||||||
|
locationObj.put("latitude", JSONUtil.parseObj(message).getJSONObject("data").get("latitude").toString()); // 纬度
|
||||||
|
locationObj.put("longitude", JSONUtil.parseObj(message).getJSONObject("data").get("longitude").toString()); // 经度
|
||||||
|
messageObj.put("location", locationObj);
|
||||||
|
return messageObj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建推送消息内容(String类型)
|
||||||
|
*/
|
||||||
|
private String ueStructureJsonMessage(String message, String modelId) {
|
||||||
|
// 构造消息对象(包含关键信息)
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("type", "location"); // 消息类型
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("id", modelId);
|
||||||
|
|
||||||
|
// 位置信息
|
||||||
|
JSONObject position = new JSONObject();
|
||||||
|
position.put("lat", JSONUtil.parseObj(message).getJSONObject("data").get("latitude")); // 纬度
|
||||||
|
position.put("lng", JSONUtil.parseObj(message).getJSONObject("data").get("longitude")); // 经度
|
||||||
|
position.put("alt", JSONUtil.parseObj(message).getJSONObject("data").get("height")); // 海拔
|
||||||
|
|
||||||
|
data.put("position", position);
|
||||||
|
messageObj.put("data", data); // 设备唯一标识
|
||||||
|
|
||||||
|
// 转换为String类型返回
|
||||||
|
return messageObj.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,12 +4,11 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.json.JSONArray;
|
import cn.hutool.json.JSONArray;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.dromara.bigscreen.domain.dto.ProjectImageProgressDetailReq;
|
||||||
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
|
import org.dromara.bigscreen.domain.dto.TanchuangInfoReq;
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
|
import org.dromara.bigscreen.domain.vo.*;
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
|
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectSafetyInspectionVo;
|
|
||||||
import org.dromara.bigscreen.domain.vo.ProjectTeamAttendanceVo;
|
|
||||||
import org.dromara.bigscreen.service.ProjectBigScreenService;
|
import org.dromara.bigscreen.service.ProjectBigScreenService;
|
||||||
import org.dromara.common.core.constant.HttpStatus;
|
import org.dromara.common.core.constant.HttpStatus;
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
@ -17,9 +16,9 @@ import org.dromara.common.redis.utils.RedisUtils;
|
|||||||
import org.dromara.common.utils.BigDecimalUtil;
|
import org.dromara.common.utils.BigDecimalUtil;
|
||||||
import org.dromara.contractor.domain.SubConstructionUser;
|
import org.dromara.contractor.domain.SubConstructionUser;
|
||||||
import org.dromara.contractor.service.ISubConstructionUserService;
|
import org.dromara.contractor.service.ISubConstructionUserService;
|
||||||
|
import org.dromara.drone.domain.vo.DroProjectDroneVo;
|
||||||
import org.dromara.drone.service.IDroProjectDroneService;
|
import org.dromara.drone.service.IDroProjectDroneService;
|
||||||
import org.dromara.gps.domain.bo.GpsEquipmentBo;
|
import org.dromara.gps.domain.bo.GpsEquipmentBo;
|
||||||
import org.dromara.gps.domain.vo.DeviceVo;
|
|
||||||
import org.dromara.gps.domain.vo.GpsEquipmentSonVo;
|
import org.dromara.gps.domain.vo.GpsEquipmentSonVo;
|
||||||
import org.dromara.gps.domain.vo.LocationVo;
|
import org.dromara.gps.domain.vo.LocationVo;
|
||||||
import org.dromara.gps.service.IDeviceService;
|
import org.dromara.gps.service.IDeviceService;
|
||||||
@ -286,13 +285,19 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
|
|||||||
// 如果有叶子节点,统计进度和状态;否则初始化为未完成
|
// 如果有叶子节点,统计进度和状态;否则初始化为未完成
|
||||||
if (CollUtil.isNotEmpty(leafNodesByTopIds)) {
|
if (CollUtil.isNotEmpty(leafNodesByTopIds)) {
|
||||||
topVo.setProgressTotal(progressCategoryService.getCompletedPercentage(leafNodesByTopIds));
|
topVo.setProgressTotal(progressCategoryService.getCompletedPercentage(leafNodesByTopIds));
|
||||||
|
topVo.setValueTotal(progressCategoryService.getValueTotal(leafNodesByTopIds, true));
|
||||||
} else {
|
} else {
|
||||||
topVo.setProgressTotal(BigDecimal.ZERO);
|
topVo.setProgressTotal(BigDecimal.ZERO);
|
||||||
|
topVo.setValueTotal(BigDecimal.ZERO);
|
||||||
}
|
}
|
||||||
// 加入结果集
|
// 加入结果集
|
||||||
topList.add(topVo);
|
topList.add(topVo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 计算总完成产值
|
||||||
|
BigDecimal valueTotal = topList.stream().map(ProjectImageProgressVo::getValueTotal)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
topList.forEach(vo -> vo.setValuePercentage(BigDecimalUtil.toPercentage(vo.getValueTotal(), valueTotal)));
|
||||||
return topList;
|
return topList;
|
||||||
/* ProjectImageProgressVo vo = new ProjectImageProgressVo();
|
/* ProjectImageProgressVo vo = new ProjectImageProgressVo();
|
||||||
// 获取所有子项目列表
|
// 获取所有子项目列表
|
||||||
@ -422,6 +427,79 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
|
|||||||
return vo;*/
|
return vo;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目形象进度详情
|
||||||
|
*
|
||||||
|
* @param req 项目形象进度名称
|
||||||
|
* @return 项目形象进度详情
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<ProjectImageProgressDetailVo> getProjectImageProgressDetail(ProjectImageProgressDetailReq req) {
|
||||||
|
Long projectId = req.getProjectId();
|
||||||
|
String progressName = req.getProgressName();
|
||||||
|
checkProject(projectId);
|
||||||
|
// 获取对应进度
|
||||||
|
PgsProgressCategory progressCategory = progressCategoryService.lambdaQuery()
|
||||||
|
.eq(PgsProgressCategory::getProjectId, projectId)
|
||||||
|
.eq(PgsProgressCategory::getName, progressName)
|
||||||
|
.last("limit 1")
|
||||||
|
.one();
|
||||||
|
if (progressCategory == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
Long topId = progressCategory.getId();
|
||||||
|
// 查出所有属于该顶级节点的子孙节点
|
||||||
|
List<PgsProgressCategory> allChildren = progressCategoryService.list(
|
||||||
|
Wrappers.<PgsProgressCategory>lambdaQuery()
|
||||||
|
.and(wrapper -> {
|
||||||
|
wrapper.like(PgsProgressCategory::getAncestors, "," + topId + ",")
|
||||||
|
.or()
|
||||||
|
.like(PgsProgressCategory::getAncestors, "," + topId);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (allChildren.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
// 判断层级
|
||||||
|
Set<Long> parentIds = allChildren.stream()
|
||||||
|
.map(PgsProgressCategory::getParentId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
List<PgsProgressCategory> dierList = allChildren.stream()
|
||||||
|
.filter(item -> parentIds.contains(item.getId()))
|
||||||
|
.toList();
|
||||||
|
if (CollUtil.isEmpty(dierList)) {
|
||||||
|
return allChildren.stream().map(c -> {
|
||||||
|
ProjectImageProgressDetailVo vo = new ProjectImageProgressDetailVo();
|
||||||
|
vo.setProgressName(c.getName());
|
||||||
|
vo.setPlanProgress(c.getPlanTotal());
|
||||||
|
vo.setActualProgress(c.getCompleted());
|
||||||
|
vo.setTotalProgress(c.getTotal());
|
||||||
|
return vo;
|
||||||
|
}).toList();
|
||||||
|
} else {
|
||||||
|
return dierList.stream().map(c -> {
|
||||||
|
// 获取子集
|
||||||
|
List<PgsProgressCategory> children = allChildren.stream()
|
||||||
|
.filter(item -> item.getParentId().equals(c.getId()))
|
||||||
|
.toList();
|
||||||
|
// 计算
|
||||||
|
BigDecimal plan = children.stream().map(PgsProgressCategory::getPlanTotal)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
BigDecimal actual = children.stream().map(PgsProgressCategory::getCompleted)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
BigDecimal total = children.stream().map(PgsProgressCategory::getTotal)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
ProjectImageProgressDetailVo vo = new ProjectImageProgressDetailVo();
|
||||||
|
vo.setProgressName(c.getName());
|
||||||
|
vo.setPlanProgress(plan);
|
||||||
|
vo.setActualProgress(actual);
|
||||||
|
vo.setTotalProgress(total);
|
||||||
|
return vo;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取项目概括
|
* 获取项目概括
|
||||||
*
|
*
|
||||||
@ -577,7 +655,7 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
|
|||||||
wrj.put("lng", object1.getJSONObject("data").get("longitude"));
|
wrj.put("lng", object1.getJSONObject("data").get("longitude"));
|
||||||
wrj.put("alt", object1.getJSONObject("data").get("height"));
|
wrj.put("alt", object1.getJSONObject("data").get("height"));
|
||||||
wrjChildrenMap.add(wrj);
|
wrjChildrenMap.add(wrj);
|
||||||
}else {
|
} else {
|
||||||
Object object2 = stringRedisTemplate.opsForValue().get("wrj:osd3:" + key);
|
Object object2 = stringRedisTemplate.opsForValue().get("wrj:osd3:" + key);
|
||||||
if (object2 != null) {
|
if (object2 != null) {
|
||||||
JSONObject object3 = JSONUtil.parseObj(object2);
|
JSONObject object3 = JSONUtil.parseObj(object2);
|
||||||
@ -625,9 +703,114 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
|
|||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getUeClientList() {
|
||||||
|
// 获取当天的开始时间(00:00:00)
|
||||||
|
LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIN);
|
||||||
|
|
||||||
|
// 获取当前时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
List<GpsEquipmentSonVo> voList = gpsEquipmentService.getUeClientList(startOfDay, now);
|
||||||
|
List<GpsEquipmentSonVo> appList = gpsEquipmentService.getUeUserListByProjectId( startOfDay, now);
|
||||||
|
// List<LocationVo> anqmList = deviceService.getUeUserListByProjectId( startOfDay, now);
|
||||||
|
// List<OthYs7Device> othYs7DeviceList = othYs7DeviceService.lambdaQuery()
|
||||||
|
// .list();
|
||||||
|
List<DroProjectDroneVo> wrjKeys = droProjectDroneService.getUeTopicsByProjectId();
|
||||||
|
List<String> maps = new ArrayList<>();
|
||||||
|
if (voList != null && !voList.isEmpty()) {
|
||||||
|
for (GpsEquipmentSonVo item : voList) {
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("type", "location"); // 消息类型
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("id", item.getModelId());
|
||||||
|
|
||||||
|
// 位置信息
|
||||||
|
JSONObject position = new JSONObject();
|
||||||
|
position.put("lat", item.getLocLatitude()); // 纬度
|
||||||
|
position.put("lng", item.getLocLongitude()); // 经度
|
||||||
|
position.put("alt", item.getLocAltitude()); // 海拔
|
||||||
|
|
||||||
|
data.put("position", position);
|
||||||
|
messageObj.put("data", data); // 设备唯一标识
|
||||||
|
maps.add(messageObj.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (appList != null && !appList.isEmpty()) {
|
||||||
|
for (GpsEquipmentSonVo item : appList) {
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("type", "location"); // 消息类型
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("id", item.getModelId());
|
||||||
|
|
||||||
|
// 位置信息
|
||||||
|
JSONObject position = new JSONObject();
|
||||||
|
position.put("lat", item.getLocLatitude()); // 纬度
|
||||||
|
position.put("lng", item.getLocLongitude()); // 经度
|
||||||
|
position.put("alt", item.getLocAltitude()); // 海拔
|
||||||
|
|
||||||
|
data.put("position", position);
|
||||||
|
messageObj.put("data", data); // 设备唯一标识
|
||||||
|
maps.add(messageObj.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wrjKeys != null && !wrjKeys.isEmpty()) {
|
||||||
|
for (DroProjectDroneVo key : wrjKeys) {
|
||||||
|
Object object = stringRedisTemplate.opsForValue().get("wrj:osd4:" + key.getAirplaneSn());
|
||||||
|
Object object6 = stringRedisTemplate.opsForValue().get("wrj:osd2:" + key.getDroneSn());
|
||||||
|
String status = "";
|
||||||
|
if (object6 != null) {
|
||||||
|
JSONObject object1 = JSONUtil.parseObj(object6);
|
||||||
|
status = object1.getJSONObject("data").get("flighttask_step_code").toString();
|
||||||
|
}
|
||||||
|
if (object != null) {
|
||||||
|
JSONObject object1 = JSONUtil.parseObj(object);
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("type", "location"); // 消息类型
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("id", key.getAirplaneModleId());
|
||||||
|
|
||||||
|
// 位置信息
|
||||||
|
JSONObject position = new JSONObject();
|
||||||
|
position.put("lat", object1.getJSONObject("data").get("latitude")); // 纬度
|
||||||
|
position.put("lng", object1.getJSONObject("data").get("longitude")); // 经度
|
||||||
|
position.put("alt", object1.getJSONObject("data").get("height")); // 海拔
|
||||||
|
|
||||||
|
data.put("position", position);
|
||||||
|
messageObj.put("data", data); // 设备唯一标识
|
||||||
|
maps.add(messageObj.toString());
|
||||||
|
} else {
|
||||||
|
Object object2 = stringRedisTemplate.opsForValue().get("wrj:osd3:" + key);
|
||||||
|
if (object2 != null) {
|
||||||
|
JSONObject object3 = JSONUtil.parseObj(object2);
|
||||||
|
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("type", "location"); // 消息类型
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("id", key.getDroneModleId());
|
||||||
|
|
||||||
|
// 位置信息
|
||||||
|
JSONObject position = new JSONObject();
|
||||||
|
position.put("lat", object3.getJSONObject("data").get("latitude")); // 纬度
|
||||||
|
position.put("lng", object3.getJSONObject("data").get("longitude")); // 经度
|
||||||
|
position.put("alt", object3.getJSONObject("data").get("height")); // 海拔
|
||||||
|
|
||||||
|
data.put("position", position);
|
||||||
|
messageObj.put("data", data); // 设备唯一标识
|
||||||
|
maps.add(messageObj.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setWrjHc() {
|
public void setWrjHc() {
|
||||||
RedisUtils.setCacheObject("xmjdap:ws",System.currentTimeMillis() );
|
RedisUtils.setCacheObject("xmjdap:ws", System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -318,11 +318,13 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
|
|||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
}
|
}
|
||||||
map.put("remainingQuantity",quantity.subtract(reduce));
|
map.put("remainingQuantity",quantity.subtract(reduce));
|
||||||
map.put("specification",busBillofquantities.getSpecification());
|
|
||||||
map.put("suppliespriceId",limitListId);
|
map.put("suppliespriceId",limitListId);
|
||||||
|
if (busBillofquantities != null) {
|
||||||
|
map.put("specification",busBillofquantities.getSpecification());
|
||||||
map.put("unit",busBillofquantities.getUnit());
|
map.put("unit",busBillofquantities.getUnit());
|
||||||
map.put("remark",busBillofquantities.getRemark());
|
map.put("remark",busBillofquantities.getRemark());
|
||||||
map.put("name",busBillofquantities.getName());
|
map.put("name",busBillofquantities.getName());
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,21 @@
|
|||||||
package org.dromara.common.utils;
|
package org.dromara.common.utils;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.utils.MessageUtils;
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
import org.dromara.common.sse.dto.SseMessageDto;
|
import org.dromara.common.sse.dto.SseMessageDto;
|
||||||
import org.dromara.common.sse.utils.SseMessageUtils;
|
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.SmsBlend;
|
||||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
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.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@ -17,6 +26,16 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AsyncUtil {
|
public class AsyncUtil {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Lazy
|
||||||
|
private DeviceMessageSender deviceMessageSender;
|
||||||
|
@Resource
|
||||||
|
@Lazy
|
||||||
|
private ISysOssService ossService;
|
||||||
|
@Resource
|
||||||
|
@Lazy
|
||||||
|
private IBusAttendanceMachineService attendanceMachineService;
|
||||||
|
|
||||||
//发送短信
|
//发送短信
|
||||||
@Async
|
@Async
|
||||||
public void sendSms(List<String> mobileList, String config) {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,93 @@
|
|||||||
|
package org.dromara.common.utils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口错误码匹配工具(用户展示用)
|
||||||
|
*/
|
||||||
|
public class ErrorCodeMatcher {
|
||||||
|
|
||||||
|
private static final Map<String, String> ERROR_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 基础错误码
|
||||||
|
ERROR_MAP.put("1", "抱歉,出现未知错误,请您重试。若问题持续,请联系管理员。");
|
||||||
|
ERROR_MAP.put("2", "服务暂时不可用,请稍后重试。若多次尝试仍有问题,请联系管理员。");
|
||||||
|
ERROR_MAP.put("3", "调用的接口不存在,请检查请求地址是否正确(建议避免特殊字符,如手动输入重试)。");
|
||||||
|
ERROR_MAP.put("4", "当前集群请求量超限,请稍后再试。持续异常请联系管理员。");
|
||||||
|
ERROR_MAP.put("6", "暂无该接口调用权限,请联系管理员开通相关权限。");
|
||||||
|
ERROR_MAP.put("14", "鉴权失败,请检查密钥是否正确,或联系管理员协助处理。");
|
||||||
|
ERROR_MAP.put("17", "今日免费额度已用完。如需继续使用,请联系管理员获取更多资源。");
|
||||||
|
ERROR_MAP.put("18", "请求频率超限(QPS限制)。如需提高限制,请联系管理员咨询。");
|
||||||
|
ERROR_MAP.put("19", "总请求量已达限额,请联系管理员获取更多资源。");
|
||||||
|
ERROR_MAP.put("100", "访问令牌无效,请联系管理员重新获取。");
|
||||||
|
ERROR_MAP.put("110", "访问令牌无效(有效期30天),请联系管理员更新。");
|
||||||
|
ERROR_MAP.put("111", "访问令牌已过期,请联系管理员重新获取。");
|
||||||
|
|
||||||
|
// 216xxx系列错误码
|
||||||
|
ERROR_MAP.put("216100", "请求中包含无效参数,请检查后重新提交。");
|
||||||
|
ERROR_MAP.put("216101", "缺少必要的请求参数,请核对后补充完整。");
|
||||||
|
ERROR_MAP.put("216102", "请求的服务暂不支持,请检查调用地址是否正确,或联系管理员确认。");
|
||||||
|
ERROR_MAP.put("216103", "部分参数长度超限,请缩短后重新尝试。");
|
||||||
|
ERROR_MAP.put("216110", "应用ID不存在,请核对是否为正确ID,或联系管理员确认。");
|
||||||
|
ERROR_MAP.put("216200", "上传的图片为空,请检查图片是否正确上传。");
|
||||||
|
ERROR_MAP.put("216201", "图片格式不支持,目前仅支持PNG、JPG、JPEG、BMP格式,请转码后重试。");
|
||||||
|
ERROR_MAP.put("216202", "图片大小不符合要求,请参考接口文档的限制重新上传,或联系管理员咨询。");
|
||||||
|
ERROR_MAP.put("216205", "请求体过大(base64编码后需小于10M),请压缩内容后重试。");
|
||||||
|
ERROR_MAP.put("216306", "文件上传失败,请检查请求参数是否符合要求。");
|
||||||
|
ERROR_MAP.put("216307", "图片解析失败,若多次尝试仍有问题,请联系管理员。");
|
||||||
|
ERROR_MAP.put("216308", "PDF页码参数超过实际页数,请核对后修改。");
|
||||||
|
ERROR_MAP.put("216401", "请求提交失败,请稍后重试。");
|
||||||
|
ERROR_MAP.put("216402", "获取结果失败,请稍后重试或检查任务状态。");
|
||||||
|
ERROR_MAP.put("216603", "无法获取PDF页数,请检查文件是否完整或编码是否正确。");
|
||||||
|
ERROR_MAP.put("216604", "请求总量已达限额,请联系管理员获取更多额度。");
|
||||||
|
ERROR_MAP.put("216630", "识别失败,请重试。若问题持续,请联系管理员。");
|
||||||
|
ERROR_MAP.put("216631", "银行卡识别失败,请确保上传的是银行卡正面完整清晰的图片(异形卡可能无法识别)。");
|
||||||
|
ERROR_MAP.put("216633", "身份证识别失败,请确保上传的是身份证完整清晰的图片(非身份证或模糊图片无法识别)。");
|
||||||
|
ERROR_MAP.put("216634", "检测失败,请重试。若持续异常,请联系管理员。");
|
||||||
|
ERROR_MAP.put("216600", "企业核验服务请求失败,请重试。持续问题请联系管理员(适用于工商信息查询、要素核验等)。");
|
||||||
|
ERROR_MAP.put("216601", "企业核验查询成功,但未找到相关结果,请确认信息是否正确后重试。");
|
||||||
|
ERROR_MAP.put("216602", "企业核验服务超时,请稍后重试。持续问题请联系管理员。");
|
||||||
|
|
||||||
|
// 282xxx系列错误码
|
||||||
|
ERROR_MAP.put("282000", "服务器内部错误。若使用通用文字识别,可能因图片文字过多导致超时,建议分割图片后重试;其他情况可稍后再试,持续问题请联系管理员。");
|
||||||
|
ERROR_MAP.put("282003", "请求缺少必要参数,请核对后补充。");
|
||||||
|
ERROR_MAP.put("282005", "批量任务处理出错,请根据具体错误提示排查。");
|
||||||
|
ERROR_MAP.put("282006", "批量任务数量超限,单次最多处理10个任务,请减少数量后重试。");
|
||||||
|
ERROR_MAP.put("282100", "图片压缩转码失败,请更换图片后重试。");
|
||||||
|
ERROR_MAP.put("282102", "未检测到可识别的卡证/票据,请确保图片包含清晰完整的目标(非卡证或模糊图片无法识别)。");
|
||||||
|
ERROR_MAP.put("282103", "卡证/票据识别失败,请确保图片包含清晰完整的目标(非对应类型或模糊图片无法识别)。");
|
||||||
|
ERROR_MAP.put("282110", "图片URL不存在,请核对地址后重新提交。");
|
||||||
|
ERROR_MAP.put("282111", "图片URL格式错误,请检查是否符合接口要求的格式。");
|
||||||
|
ERROR_MAP.put("282112", "图片URL下载超时,可能因图片过大(超过3M)、地址无效或存在防盗链。建议更换图片地址或下载后直接上传。");
|
||||||
|
ERROR_MAP.put("282113", "图片URL返回无效内容,请检查地址是否正确。");
|
||||||
|
ERROR_MAP.put("282114", "图片URL长度不符合要求(需1-1024字节),请修改后重试。");
|
||||||
|
ERROR_MAP.put("282134", "增值税发票验真失败,可能因系统维护,建议次日再试。持续问题请联系管理员。");
|
||||||
|
ERROR_MAP.put("282808", "请求ID不存在,请核对ID是否正确。");
|
||||||
|
ERROR_MAP.put("282809", "返回结果格式错误(需为excel或json),请稍后重试。");
|
||||||
|
ERROR_MAP.put("282810", "图像识别失败,请重试。若问题持续,请联系管理员。");
|
||||||
|
ERROR_MAP.put("282160", "行驶证核验资源超限,请联系管理员咨询。");
|
||||||
|
ERROR_MAP.put("282161", "行驶证核验请求过于频繁,请稍后再试。");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据错误码获取用户友好的错误信息
|
||||||
|
* @param errorCode 错误码(字符串/整数)
|
||||||
|
* @return 优化后的错误提示,未匹配时返回"未知错误(错误码:xxx)"
|
||||||
|
*/
|
||||||
|
public static String getFriendlyMessage(String errorCode) {
|
||||||
|
return ERROR_MAP.getOrDefault(errorCode, "未知错误(错误码:" + errorCode + "),请重试或联系管理员。");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFriendlyMessage(int errorCode) {
|
||||||
|
return getFriendlyMessage(String.valueOf(errorCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试示例
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(getFriendlyMessage(1)); // 输出:抱歉,出现未知错误,请您重试...
|
||||||
|
System.out.println(getFriendlyMessage("216201")); // 输出:图片格式不支持,目前仅支持PNG...
|
||||||
|
System.out.println(getFriendlyMessage(9999)); // 输出:未知错误(错误码:9999)...
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package org.dromara.common.utils.baiduUtil;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.utils.ErrorCodeMatcher;
|
||||||
import org.dromara.common.utils.baiduUtil.entity.face.ComparisonRes;
|
import org.dromara.common.utils.baiduUtil.entity.face.ComparisonRes;
|
||||||
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq;
|
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq;
|
||||||
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceRes;
|
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceRes;
|
||||||
@ -208,8 +209,7 @@ public class BaiDuFace {
|
|||||||
|
|
||||||
// 8. 处理API返回错误
|
// 8. 处理API返回错误
|
||||||
if (comparisonRep.getErrorCode() != 0) {
|
if (comparisonRep.getErrorCode() != 0) {
|
||||||
throw new RuntimeException("人脸对比失败:" + comparisonRep.getErrorMsg()
|
throw new RuntimeException("人脸对比失败:" + ErrorCodeMatcher.getFriendlyMessage(comparisonRep.getErrorCode()));
|
||||||
+ "(错误码:" + comparisonRep.getErrorCode() + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. 校验对比结果
|
// 9. 校验对比结果
|
||||||
|
|||||||
@ -364,10 +364,11 @@ public class SubConstructionUserController extends BaseController {
|
|||||||
if (user.getEntryDate() != null) {
|
if (user.getEntryDate() != null) {
|
||||||
vo.setEntryDate(DateUtils.formatDateTime(user.getEntryDate()));
|
vo.setEntryDate(DateUtils.formatDateTime(user.getEntryDate()));
|
||||||
}
|
}
|
||||||
|
//0男 1女 2未知
|
||||||
String sex = vo.getSex();
|
String sex = vo.getSex();
|
||||||
if (sex != null && sex.equals("1")) {
|
if (sex != null && sex.equals("0")) {
|
||||||
vo.setSex("男");
|
vo.setSex("男");
|
||||||
} else if (sex != null && sex.equals("2")) {
|
} else if (sex != null && sex.equals("1")) {
|
||||||
vo.setSex("女");
|
vo.setSex("女");
|
||||||
} else {
|
} else {
|
||||||
vo.setSex("未知");
|
vo.setSex("未知");
|
||||||
|
|||||||
@ -74,8 +74,8 @@ public class SubConstructionUserFileController extends BaseController {
|
|||||||
@Log(title = "施工人员文件存储", businessType = BusinessType.INSERT)
|
@Log(title = "施工人员文件存储", businessType = BusinessType.INSERT)
|
||||||
@RepeatSubmit()
|
@RepeatSubmit()
|
||||||
@PostMapping("/upload/zip")
|
@PostMapping("/upload/zip")
|
||||||
public R<Boolean> uploadFileZip(@RequestParam("file") MultipartFile file) {
|
public R<Boolean> uploadFileZip(@RequestParam("file") MultipartFile file, Long projectId) {
|
||||||
Boolean result = constructionUserFileService.batchUploadFileByZip(file);
|
Boolean result = constructionUserFileService.batchUploadFileByZip(file, projectId);
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class SubConstructionUserAuthenticationReq implements Serializable {
|
|||||||
private String phone;
|
private String phone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 0:保密 1:男 2女
|
* 0男1女2未知
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "性别不能为空")
|
@NotBlank(message = "性别不能为空")
|
||||||
private String sex;
|
private String sex;
|
||||||
|
|||||||
@ -101,4 +101,7 @@ public class SubConstructionUserQueryReq implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
|
|
||||||
|
private String phone;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,9 +49,10 @@ public interface ISubConstructionUserFileService extends IService<SubConstructio
|
|||||||
* 通过zip文件批量上传施工人员文件
|
* 通过zip文件批量上传施工人员文件
|
||||||
*
|
*
|
||||||
* @param multipartFile zip文件
|
* @param multipartFile zip文件
|
||||||
|
* @param projectId 项目id
|
||||||
* @return 是否上传成功
|
* @return 是否上传成功
|
||||||
*/
|
*/
|
||||||
Boolean batchUploadFileByZip(MultipartFile multipartFile);
|
Boolean batchUploadFileByZip(MultipartFile multipartFile, Long projectId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存施工人员文件存储
|
* 保存施工人员文件存储
|
||||||
|
|||||||
@ -186,11 +186,18 @@ public class SubConstructionUserFileServiceImpl extends ServiceImpl<SubConstruct
|
|||||||
* 通过zip文件批量上传施工人员文件
|
* 通过zip文件批量上传施工人员文件
|
||||||
*
|
*
|
||||||
* @param multipartFile zip文件
|
* @param multipartFile zip文件
|
||||||
|
* @param projectId 项目id
|
||||||
* @return 是否上传成功
|
* @return 是否上传成功
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean batchUploadFileByZip(MultipartFile multipartFile) {
|
public Boolean batchUploadFileByZip(MultipartFile multipartFile, Long projectId) {
|
||||||
|
if (projectId == null) {
|
||||||
|
throw new ServiceException("项目不存在", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
if (multipartFile == null) {
|
||||||
|
throw new ServiceException("请上传文件", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
// 获取文件原始名字
|
// 获取文件原始名字
|
||||||
String originalFilename = multipartFile.getOriginalFilename();
|
String originalFilename = multipartFile.getOriginalFilename();
|
||||||
// 校验
|
// 校验
|
||||||
@ -202,9 +209,6 @@ public class SubConstructionUserFileServiceImpl extends ServiceImpl<SubConstruct
|
|||||||
if (!suffix.equals("zip")) {
|
if (!suffix.equals("zip")) {
|
||||||
throw new ServiceException("请上传zip格式的文件", HttpStatus.BAD_REQUEST);
|
throw new ServiceException("请上传zip格式的文件", HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
// 1. 获取项目id
|
|
||||||
String[] parts = originalFilename.split("_");
|
|
||||||
long projectId = Long.parseLong(parts[1]);
|
|
||||||
// 压缩包临时文件路径
|
// 压缩包临时文件路径
|
||||||
String randomStr = RandomUtil.randomString(16);
|
String randomStr = RandomUtil.randomString(16);
|
||||||
File tempZipFile = null;
|
File tempZipFile = null;
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import org.dromara.common.oss.core.OssClient;
|
|||||||
import org.dromara.common.oss.exception.OssException;
|
import org.dromara.common.oss.exception.OssException;
|
||||||
import org.dromara.common.oss.factory.OssFactory;
|
import org.dromara.common.oss.factory.OssFactory;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
import org.dromara.common.utils.AsyncUtil;
|
||||||
import org.dromara.common.utils.IdCardEncryptorUtil;
|
import org.dromara.common.utils.IdCardEncryptorUtil;
|
||||||
import org.dromara.common.utils.baiduUtil.BaiDuFace;
|
import org.dromara.common.utils.baiduUtil.BaiDuFace;
|
||||||
import org.dromara.common.utils.baiduUtil.BaiDuOCR;
|
import org.dromara.common.utils.baiduUtil.BaiDuOCR;
|
||||||
@ -46,6 +47,7 @@ import org.dromara.contractor.mapper.SubConstructionUserMapper;
|
|||||||
import org.dromara.contractor.service.ISubConstructionUserFileService;
|
import org.dromara.contractor.service.ISubConstructionUserFileService;
|
||||||
import org.dromara.contractor.service.ISubConstructionUserService;
|
import org.dromara.contractor.service.ISubConstructionUserService;
|
||||||
import org.dromara.contractor.service.ISubContractorService;
|
import org.dromara.contractor.service.ISubContractorService;
|
||||||
|
import org.dromara.mobileAttendanceMachine.DeviceMessageSender;
|
||||||
import org.dromara.project.domain.*;
|
import org.dromara.project.domain.*;
|
||||||
import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum;
|
import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum;
|
||||||
import org.dromara.project.domain.enums.BusAttendanceCommuterEnum;
|
import org.dromara.project.domain.enums.BusAttendanceCommuterEnum;
|
||||||
@ -151,6 +153,11 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
|
|||||||
@Resource
|
@Resource
|
||||||
private ISysRoleService roleService;
|
private ISysRoleService roleService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AsyncUtil asyncUtil;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询施工人员
|
* 查询施工人员
|
||||||
*
|
*
|
||||||
@ -202,8 +209,10 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
|
|||||||
LambdaQueryWrapper<SubConstructionUser> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<SubConstructionUser> lqw = Wrappers.lambdaQuery();
|
||||||
// 从对象中取值
|
// 从对象中取值
|
||||||
String userName = req.getUserName();
|
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.like(StringUtils.isNotBlank(userName), SubConstructionUser::getUserName, userName);
|
||||||
lqw.isNull(SubConstructionUser::getProjectId);
|
lqw.isNull(SubConstructionUser::getProjectId);
|
||||||
lqw.isNull(SubConstructionUser::getTeamId);
|
lqw.isNull(SubConstructionUser::getTeamId);
|
||||||
@ -290,6 +299,8 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
|
|||||||
//强退
|
//强退
|
||||||
roleService.cleanOnlineUser(Collections.singletonList(constructionUser.getSysUserId()));
|
roleService.cleanOnlineUser(Collections.singletonList(constructionUser.getSysUserId()));
|
||||||
|
|
||||||
|
asyncUtil.sendPersonnel(dto.getTeamId(), constructionUser);
|
||||||
|
|
||||||
return i > 0;
|
return i > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,159 @@
|
|||||||
|
package org.dromara.dataTransmission;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class TokenUtils {
|
||||||
|
|
||||||
|
// Token过期时间:25分钟(与Redis存储过期时间保持一致)
|
||||||
|
private static final long TOKEN_EXPIRE_SECONDS = 1500;
|
||||||
|
// HTTP请求超时时间:10秒
|
||||||
|
private static final Duration HTTP_TIMEOUT = Duration.ofSeconds(10);
|
||||||
|
//通用字符
|
||||||
|
private static final String USER_NAME = "username";
|
||||||
|
private static final String PASSWORD = "password";
|
||||||
|
private static final String PUBLIC_KEY = "publicKey";
|
||||||
|
private static final String URL = "url";
|
||||||
|
|
||||||
|
//clientId
|
||||||
|
public static final String CLARITYPM = "claritypm";
|
||||||
|
|
||||||
|
//接口信息及参数
|
||||||
|
private static final Map<String, Map<String, String>> tokenMap = new HashMap<>(){
|
||||||
|
{
|
||||||
|
put(CLARITYPM, new HashMap<>(){{
|
||||||
|
put(URL, "https://claritypm.powerchina.cn/neSmartsite-api/loginCli");
|
||||||
|
put(USER_NAME, "zhangweiwei");
|
||||||
|
put(PASSWORD, "Hkrsoft@#2023");
|
||||||
|
put(PUBLIC_KEY, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoaejbjbttHyHuEzHL8lIX5GZZ6zIYrqJpEDlPM4V5LHn19rSAYp2FyAr8y5Ctny9uUdaYbkoFiVQgxWrAYo4X/3O0OFDsowE25FMOLQY0Mn5B6CvVR7Sdt3DqzIzM1tUnJCIbVGNfDMgxLrLwFN8RvOW8MPlB6LgOvlGMDbj+OQIDAQAB");
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//字段参数
|
||||||
|
private static final Map<String, Map<String, String>> paramMap = new HashMap<>(){
|
||||||
|
{
|
||||||
|
put(CLARITYPM, new HashMap<>(){{
|
||||||
|
|
||||||
|
put(USER_NAME, "username");
|
||||||
|
put(PASSWORD, "password");
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先从Redis获取Token,不存在则调用接口获取并存储
|
||||||
|
* @param clientId 客户端ID
|
||||||
|
* @return 有效的Token字符串
|
||||||
|
* @throws Exception 当获取过程发生异常时抛出
|
||||||
|
*/
|
||||||
|
public static String getToken(String clientId) throws Exception {
|
||||||
|
// 1. 定义Redis中的Token键名
|
||||||
|
// String redisKey = "data_auth:token:" + clientId;
|
||||||
|
//
|
||||||
|
// // 2. 尝试从Redis获取Token
|
||||||
|
// String token = RedisUtils.getCacheObject(redisKey);
|
||||||
|
// if (token != null && !token.isBlank()) {
|
||||||
|
// // 缓存命中,直接返回
|
||||||
|
// return token;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 3. 缓存未命中,调用接口获取Token
|
||||||
|
Map<String, String> map = tokenMap.get(clientId);
|
||||||
|
String token = fetchTokenFromApi(clientId,map.get(URL), map.get(USER_NAME), rsaEncrypt(map.get(PASSWORD),map.get(PUBLIC_KEY)));
|
||||||
|
|
||||||
|
// 4. 存储到Redis(设置25分钟过期)
|
||||||
|
// RedisUtils.setCacheObject(redisKey, token,Duration.ofSeconds(TOKEN_EXPIRE_SECONDS) );
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从接口获取Token的内部方法
|
||||||
|
*/
|
||||||
|
private static String fetchTokenFromApi(String clientId, String authUrl, String username, String encryptedPassword) throws Exception {
|
||||||
|
// 构建JSON请求体(包含加密后的密码)
|
||||||
|
// 1. 构建请求头(与原 Go 代码一致的头信息)
|
||||||
|
// Map<String, String> headers = new HashMap<>();
|
||||||
|
// headers.put(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
|
||||||
|
// headers.put("User-Agent", "Mozilla/5.0");
|
||||||
|
// headers.put(HttpHeaders.ACCEPT, "application/json");
|
||||||
|
// headers.put("Origin", "https://claritypm.powerchina.cn");
|
||||||
|
// headers.put("Referer", "https://claritypm.powerchina.cn/");
|
||||||
|
|
||||||
|
// 2. 构建请求体(保持你的原有逻辑)
|
||||||
|
Map<String, String> map = paramMap.get(clientId);
|
||||||
|
JSONObject requestBody = new JSONObject();
|
||||||
|
requestBody.put(map.get(USER_NAME), username); // 从 map 中获取接口要求的用户名参数名
|
||||||
|
requestBody.put(map.get(PASSWORD), encryptedPassword); // 加密后的密码
|
||||||
|
String jsonBody = requestBody.toJSONString();
|
||||||
|
System.out.println("请求体:" + jsonBody);
|
||||||
|
|
||||||
|
// 3. 带 Header 发送 POST 请求(Hutool HttpUtil 重载方法)
|
||||||
|
String post = HttpUtil.createPost(authUrl)
|
||||||
|
// .addHeaders(headers) // 设置所有请求头
|
||||||
|
.body(jsonBody) // 设置请求体
|
||||||
|
.execute() // 执行请求
|
||||||
|
.body(); // 获取响应体
|
||||||
|
|
||||||
|
System.out.println("响应结果:" + post);
|
||||||
|
// 3. 解析响应(核心解析逻辑)
|
||||||
|
JSONObject responseJson = JSONObject.parseObject(post);
|
||||||
|
int code = responseJson.getIntValue("code");
|
||||||
|
String msg = responseJson.getString("msg");
|
||||||
|
String token = responseJson.getString("token");
|
||||||
|
|
||||||
|
// 4. 校验响应有效性
|
||||||
|
if (code != 200) {
|
||||||
|
throw new RuntimeException("获取 Token 失败,响应信息:" + msg + ",状态码:" + code);
|
||||||
|
}
|
||||||
|
if (token == null || token.trim().isEmpty()) {
|
||||||
|
throw new RuntimeException("响应中未包含有效 Token,响应内容:" + post);
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA公钥加密(核心加密方法)
|
||||||
|
* @param content 待加密内容(原始密码)
|
||||||
|
* @param publicKeyStr 公钥字符串(Base64编码)
|
||||||
|
* @return 加密后的Base64字符串
|
||||||
|
* @throws Exception 加密过程异常
|
||||||
|
*/
|
||||||
|
public static String rsaEncrypt(String content, String publicKeyStr) throws Exception {
|
||||||
|
RSA rsa = new RSA(null, publicKeyStr);
|
||||||
|
byte[] encryptedBytes = rsa.encrypt(content, KeyType.PublicKey);
|
||||||
|
return Base64.getEncoder().encodeToString(encryptedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
String claritypm = getToken(CLARITYPM);
|
||||||
|
System.out.println(claritypm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package org.dromara.dataTransmission.claritypm;
|
||||||
|
|
||||||
|
|
||||||
|
import org.dromara.dataTransmission.TokenUtils;
|
||||||
|
import org.dromara.dataTransmission.claritypm.dto.RealUser;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ClaritypmClient {
|
||||||
|
|
||||||
|
// 接口地址
|
||||||
|
private static final String INSERT_REAL_USER_URL = "https://claritypm.powerchina.cn/neSmartsite-api/realUser/tiandong/insertRealUser";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TokenUtils tokenUtils; // 依赖之前的Token获取服务
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量新增实名制用户信息
|
||||||
|
* @param userList 用户信息列表(建议单次不超过10条)
|
||||||
|
* @return 接口响应结果(JSON格式)
|
||||||
|
* @throws Exception 调用异常
|
||||||
|
*/
|
||||||
|
public static String batchInsertRealUser(List<RealUser> userList) throws Exception {
|
||||||
|
// 1. 校验列表大小(建议不超过10条)
|
||||||
|
if (userList == null || userList.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("用户列表不能为空");
|
||||||
|
}
|
||||||
|
if (userList.size() > 10) {
|
||||||
|
throw new IllegalArgumentException("单次批量新增不能超过10条数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取Token(从之前的TokenService获取)
|
||||||
|
String token = TokenUtils.getToken(TokenUtils.CLARITYPM);
|
||||||
|
if ( token.trim().isEmpty()) {
|
||||||
|
throw new RuntimeException("获取Token失败,无法调用接口");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 构建请求头(包含Token认证)
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", "application/json; charset=UTF-8");
|
||||||
|
headers.put("User-Agent", "Mozilla/5.0");
|
||||||
|
headers.put("Accept", "application/json");
|
||||||
|
headers.put("Origin", "https://claritypm.powerchina.cn");
|
||||||
|
headers.put("Referer", "https://claritypm.powerchina.cn/");
|
||||||
|
headers.put("Authorization", "Bearer " + token); // 假设接口使用Bearer Token认证
|
||||||
|
|
||||||
|
// 4. 构建请求体(JSONArray格式)
|
||||||
|
JSONArray requestBody = JSONArray.parseArray(JSON.toJSONString(userList));
|
||||||
|
String jsonBody = requestBody.toJSONString();
|
||||||
|
System.out.println("批量新增用户请求体:" + jsonBody);
|
||||||
|
|
||||||
|
// 5. 发送POST请求
|
||||||
|
String response = HttpUtil.createPost(INSERT_REAL_USER_URL)
|
||||||
|
.addHeaders(headers)
|
||||||
|
.body(jsonBody)
|
||||||
|
.execute()
|
||||||
|
.body();
|
||||||
|
System.out.println("批量新增用户响应:" + response);
|
||||||
|
|
||||||
|
// 6. 解析响应(根据实际响应结构调整,此处假设与登录接口类似)
|
||||||
|
JSONObject responseJson = JSONObject.parseObject(response);
|
||||||
|
int code = responseJson.getIntValue("code");
|
||||||
|
String msg = responseJson.getString("msg");
|
||||||
|
if (code != 200) {
|
||||||
|
throw new RuntimeException("批量新增用户失败:" + msg + "(状态码:" + code + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package org.dromara.dataTransmission.claritypm.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实名制用户信息实体类(对应接口参数)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RealUser {
|
||||||
|
// 人员姓名(必填)
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
// 是否班组长(非必填,0-否 1-是)
|
||||||
|
private String classManagerFlag;
|
||||||
|
|
||||||
|
// 手机号码(必填)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
// 性别(必填,1.男 2.女 3.未知)
|
||||||
|
private String sex;
|
||||||
|
|
||||||
|
// 证件类型(必填,0.身份证)
|
||||||
|
private String cardType;
|
||||||
|
|
||||||
|
// 证件号码(必填,身份证号码)
|
||||||
|
private String cardNumber;
|
||||||
|
|
||||||
|
// 人员类型(必填,0.作业人员 1.管理人员)
|
||||||
|
private String userType;
|
||||||
|
|
||||||
|
// 发证机关(非必填)
|
||||||
|
private String cardDept;
|
||||||
|
|
||||||
|
// 民族(非必填)
|
||||||
|
private String nation;
|
||||||
|
|
||||||
|
// 生日(非必填,datetime格式)
|
||||||
|
private LocalDate birthday;
|
||||||
|
|
||||||
|
// 住址(非必填)
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
// 头像(非必填,http地址)
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
// 采集相片(非必填,http地址)
|
||||||
|
private String pic;
|
||||||
|
|
||||||
|
// 安全教育表(非必填,http地址)
|
||||||
|
private String safetyEdu;
|
||||||
|
|
||||||
|
// 技术交底表(非必填,http地址)
|
||||||
|
private String technology;
|
||||||
|
|
||||||
|
// 证件有效期始(非必填,datetime格式)
|
||||||
|
private LocalDate cardStartTime;
|
||||||
|
|
||||||
|
// 证件有效期至(非必填,datetime格式)
|
||||||
|
private LocalDate cardEndTime;
|
||||||
|
|
||||||
|
// 学历(非必填,1-9对应小学至其他)
|
||||||
|
private String studyLevel;
|
||||||
|
|
||||||
|
// 政治面貌(非必填,0-3对应党员至民主党派)
|
||||||
|
private String politicsType;
|
||||||
|
|
||||||
|
// 购买保险(非必填,0-否 1-是)
|
||||||
|
private String insuranceFlag;
|
||||||
|
|
||||||
|
// 重大病史(非必填,0-否 1-是)
|
||||||
|
private String diseaseFlag;
|
||||||
|
|
||||||
|
// 劳动合同(非必填,0-未签订 1-已签订)
|
||||||
|
private String laborContractFlag;
|
||||||
|
|
||||||
|
// 紧急联系人(非必填)
|
||||||
|
private String contacts;
|
||||||
|
|
||||||
|
// 紧急联系人电话(非必填)
|
||||||
|
private String contactsPhone;
|
||||||
|
|
||||||
|
// 婚姻状况(非必填,0-3对应未婚至丧偶)
|
||||||
|
private String marryRemark;
|
||||||
|
|
||||||
|
// 企业名称(非必填)
|
||||||
|
private String companyName;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,8 +1,15 @@
|
|||||||
package org.dromara.design.controller;
|
package org.dromara.design.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
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 jakarta.validation.constraints.NotNull;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
import org.dromara.common.log.annotation.Log;
|
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.PageQuery;
|
||||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.dromara.common.web.core.BaseController;
|
import org.dromara.common.web.core.BaseController;
|
||||||
import org.dromara.design.domain.bo.ImportExcelFileReq;
|
import org.dromara.design.domain.bo.*;
|
||||||
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.vo.*;
|
import org.dromara.design.domain.vo.*;
|
||||||
|
import org.dromara.design.exportUtil.bill.*;
|
||||||
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
|
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
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));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * 导入物资设备清单
|
// * 导入物资设备清单
|
||||||
|
|||||||
@ -1,17 +1,40 @@
|
|||||||
package org.dromara.design.controller;
|
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.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 jakarta.annotation.Resource;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.*;
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
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.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.DesUserVo;
|
||||||
|
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
|
||||||
|
import org.dromara.design.service.IDesDesignChangeService;
|
||||||
import org.dromara.design.service.IDesUserService;
|
import org.dromara.design.service.IDesUserService;
|
||||||
|
import org.dromara.project.domain.BusProject;
|
||||||
import org.dromara.project.domain.vo.project.BusSubProjectVo;
|
import org.dromara.project.domain.vo.project.BusSubProjectVo;
|
||||||
import org.dromara.project.service.IBusProjectService;
|
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.web.bind.annotation.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
@ -44,6 +67,12 @@ public class BusDrawingreviewReceiptsController extends BaseController {
|
|||||||
|
|
||||||
private final IDesUserService desUserService;
|
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));
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,31 @@
|
|||||||
package org.dromara.design.controller;
|
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 lombok.RequiredArgsConstructor;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.*;
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import org.dromara.design.domain.DesCollectFile;
|
||||||
import org.dromara.design.domain.bo.DesCollectFileBo;
|
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.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.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.web.bind.annotation.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
@ -38,6 +55,8 @@ public class DesCollectFileController extends BaseController {
|
|||||||
|
|
||||||
private final IDesCollectFileService desCollectFileService;
|
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));
|
return toAjax(desCollectFileService.deleteWithValidByIds(List.of(ids), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传资料文件
|
* 上传资料文件
|
||||||
*/
|
*/
|
||||||
@ -117,4 +135,93 @@ public class DesCollectFileController extends BaseController {
|
|||||||
@NotNull(message = "请先选择项目")Long projectId) {
|
@NotNull(message = "请先选择项目")Long projectId) {
|
||||||
return toAjax(desCollectFileService.addFile(file, catalogueId, 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,13 @@ package org.dromara.design.controller;
|
|||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.apache.poi.xwpf.usermodel.*;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.excel.utils.ExcelUtil;
|
import org.dromara.common.excel.utils.ExcelUtil;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
@ -13,17 +16,37 @@ import org.dromara.common.log.annotation.Log;
|
|||||||
import org.dromara.common.log.enums.BusinessType;
|
import org.dromara.common.log.enums.BusinessType;
|
||||||
import org.dromara.common.web.core.BaseController;
|
import org.dromara.common.web.core.BaseController;
|
||||||
import org.dromara.design.domain.DesConstructionSchedulePlan;
|
import org.dromara.design.domain.DesConstructionSchedulePlan;
|
||||||
|
import org.dromara.design.domain.DesUser;
|
||||||
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||||
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
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.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.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
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;
|
||||||
/**
|
/**
|
||||||
* 设计计划
|
* 设计计划
|
||||||
*
|
*
|
||||||
@ -37,7 +60,10 @@ public class DesConstructionSchedulePlanController extends BaseController {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IDesConstructionSchedulePlanService desConstructionSchedulePlanService;
|
private IDesConstructionSchedulePlanService desConstructionSchedulePlanService;
|
||||||
|
@Resource
|
||||||
|
private IDesUserService desUserService;
|
||||||
|
@Resource
|
||||||
|
private ISysDictTypeService dictTypeService;
|
||||||
/**
|
/**
|
||||||
* 查询设计计划列表
|
* 查询设计计划列表
|
||||||
*/
|
*/
|
||||||
@ -143,4 +169,174 @@ public class DesConstructionSchedulePlanController extends BaseController {
|
|||||||
@PathVariable Long[] ids) {
|
@PathVariable Long[] ids) {
|
||||||
return toAjax(desConstructionSchedulePlanService.deleteByIds(List.of(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package org.dromara.design.controller;
|
|||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.deepoove.poi.XWPFTemplate;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
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.PageQuery;
|
||||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.dromara.common.web.core.BaseController;
|
import org.dromara.common.web.core.BaseController;
|
||||||
|
import org.dromara.design.domain.DesUser;
|
||||||
import org.dromara.design.domain.DesVolumeCatalog;
|
import org.dromara.design.domain.DesVolumeCatalog;
|
||||||
import org.dromara.design.domain.DesVolumeFile;
|
import org.dromara.design.domain.DesVolumeFile;
|
||||||
import org.dromara.design.domain.dto.designchange.DesDesignChangeCreateReq;
|
import org.dromara.design.domain.dto.designchange.DesDesignChangeCreateReq;
|
||||||
import org.dromara.design.domain.dto.designchange.DesDesignChangeQueryReq;
|
import org.dromara.design.domain.dto.designchange.DesDesignChangeQueryReq;
|
||||||
import org.dromara.design.domain.dto.designchange.DesDesignChangeUpdateReq;
|
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.dto.volumecatalog.DesVolumeCatalogQueryReq;
|
||||||
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
|
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
|
||||||
import org.dromara.design.domain.vo.volumecatalog.DesVolumeCatalogVo;
|
import org.dromara.design.domain.vo.volumecatalog.DesVolumeCatalogVo;
|
||||||
import org.dromara.design.service.IDesDesignChangeService;
|
import org.dromara.design.service.IDesDesignChangeService;
|
||||||
import org.dromara.design.service.IDesVolumeCatalogService;
|
import org.dromara.design.service.IDesVolumeCatalogService;
|
||||||
import org.dromara.design.service.IDesVolumeFileService;
|
import org.dromara.design.service.IDesVolumeFileService;
|
||||||
|
import org.dromara.project.domain.BusProject;
|
||||||
import org.dromara.system.domain.vo.SysOssVo;
|
import org.dromara.system.domain.vo.SysOssVo;
|
||||||
|
import org.dromara.system.service.ISysDictDataService;
|
||||||
import org.dromara.system.service.ISysOssService;
|
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.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
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.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +69,9 @@ public class DesDesignChangeController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private IDesVolumeFileService desVolumeFileService;
|
private IDesVolumeFileService desVolumeFileService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISysDictDataService dictDataService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设计变更管理列表
|
* 查询设计变更管理列表
|
||||||
*/
|
*/
|
||||||
@ -146,4 +162,92 @@ public class DesDesignChangeController extends BaseController {
|
|||||||
return R.ok(list);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,6 +129,7 @@ public class DesExtractController extends BaseController {
|
|||||||
@SaCheckPermission("design:extract:userMajor")
|
@SaCheckPermission("design:extract:userMajor")
|
||||||
@GetMapping("/userMajor")
|
@GetMapping("/userMajor")
|
||||||
public R<List<DesUserVo>> selectUserMajor(DesUserBo bo) {
|
public R<List<DesUserVo>> selectUserMajor(DesUserBo bo) {
|
||||||
|
bo.setUserType("2");
|
||||||
return R.ok( deUserService.queryList(bo));
|
return R.ok( deUserService.queryList(bo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.*;
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import org.dromara.design.domain.dto.ExportDto;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
@ -128,4 +129,9 @@ public class DesPrelimSchemeController extends BaseController {
|
|||||||
return toAjax(desPrelimSchemeService.updateFile(file, projectId,id));
|
return toAjax(desPrelimSchemeService.updateFile(file, projectId,id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/exportZipWithStatus")
|
||||||
|
public void exportZipWithStatus(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||||
|
desPrelimSchemeService.exportAsZipWithStatusPrefix(dto, response);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.*;
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import org.dromara.design.domain.dto.ExportDto;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
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) {
|
public R<Void> updateFile(MultipartFile file, Long projectId, @NotNull(message = "主键不能为空")@PathVariable Long id) {
|
||||||
return toAjax(desSchemeService.updateFile(file, projectId,id));
|
return toAjax(desSchemeService.updateFile(file, projectId,id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/exportZipWithStatus")
|
||||||
|
public void exportZipWithStatus(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||||
|
desSchemeService.exportAsZipWithStatusPrefix(dto, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
package org.dromara.design.controller;
|
package org.dromara.design.controller;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.rmi.ServerException;
|
import java.rmi.ServerException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.deepoove.poi.XWPFTemplate;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.*;
|
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.XSSFSheet;
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
//import org.dromara.design.domain.DesUserExcelData;
|
//import org.dromara.design.domain.DesUserExcelData;
|
||||||
|
import org.dromara.design.domain.DesUser;
|
||||||
import org.dromara.design.domain.DesUserExcelData;
|
import org.dromara.design.domain.DesUserExcelData;
|
||||||
import org.dromara.design.domain.dto.desUser.DesUserBatchDto;
|
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.web.bind.annotation.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
@ -54,6 +63,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
public class DesUserController extends BaseController {
|
public class DesUserController extends BaseController {
|
||||||
|
|
||||||
private final IDesUserService desUserService;
|
private final IDesUserService desUserService;
|
||||||
|
private final IBusProjectService projectService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设计人员列表
|
* 查询设计人员列表
|
||||||
@ -142,5 +152,75 @@ public class DesUserController extends BaseController {
|
|||||||
return toAjax(desUserService.batchAddOrUpdate(dto));
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package org.dromara.design.domain.vo;
|
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 org.dromara.design.domain.DesSubcontract;
|
||||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
@ -31,13 +33,13 @@ public class DesSubcontractVo implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 主键ID
|
* 主键ID
|
||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "主键ID")
|
@ExcelIgnore
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目id
|
* 项目id
|
||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "项目id")
|
@ExcelIgnore
|
||||||
private Long projectId;
|
private Long projectId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +51,13 @@ public class DesSubcontractVo implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 分包要求
|
* 分包要求
|
||||||
*/
|
*/
|
||||||
|
@ExcelProperty(value = "说明")
|
||||||
private String requirement;
|
private String requirement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat("yyyy-MM-dd")
|
||||||
|
@ExcelProperty(value = "创建时间")
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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; // 审定人职称
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
||||||
|
}
|
||||||
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,14 +3,15 @@ package org.dromara.design.service;
|
|||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
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.BusBillofquantitiesVersions;
|
||||||
import org.dromara.design.domain.bo.*;
|
import org.dromara.design.domain.bo.*;
|
||||||
import org.dromara.design.domain.vo.*;
|
import org.dromara.design.domain.vo.*;
|
||||||
|
import org.dromara.design.exportUtil.bill.BillOfQuantitiesExport;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工程量清单版本Service接口
|
* 工程量清单版本Service接口
|
||||||
@ -100,4 +101,7 @@ public interface IBusBillofquantitiesVersionsService extends IService<BusBillofq
|
|||||||
List<BusBillofquantitiesMaterialTotalVo> queryMaterialTotalListByProject(Long projectId);
|
List<BusBillofquantitiesMaterialTotalVo> queryMaterialTotalListByProject(Long projectId);
|
||||||
|
|
||||||
List<BusBillofquantitiesVo> obtainAllClassification(ObtainAllVersionNumbersReq bo);
|
List<BusBillofquantitiesVo> obtainAllClassification(ObtainAllVersionNumbersReq bo);
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, List<BillOfQuantitiesExport>> export(String versions,Long projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
package org.dromara.design.service;
|
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.TableDataInfo;
|
||||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import org.dromara.design.domain.DesCollectFile;
|
import org.dromara.design.domain.DesCollectFile;
|
||||||
import org.dromara.design.domain.bo.DesCollectFileBo;
|
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.domain.vo.DesCollectFileVo;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@ -79,5 +81,6 @@ public interface IDesCollectFileService extends IService<DesCollectFile>{
|
|||||||
Boolean addFile(MultipartFile file, Long catalogueId, Long projectId);
|
Boolean addFile(MultipartFile file, Long catalogueId, Long projectId);
|
||||||
|
|
||||||
|
|
||||||
|
void exportAsZip(ExportDto dto, HttpServletResponse response) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
|||||||
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -114,4 +115,7 @@ public interface IDesConstructionSchedulePlanService extends IService<DesConstru
|
|||||||
* @return 实体列表
|
* @return 实体列表
|
||||||
*/
|
*/
|
||||||
List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList);
|
List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList);
|
||||||
|
|
||||||
|
|
||||||
|
void exportSchedule(HttpServletResponse response, Long projectId) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package org.dromara.design.service;
|
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.vo.DesPrelimSchemeVo;
|
||||||
import org.dromara.design.domain.bo.DesPrelimSchemeBo;
|
import org.dromara.design.domain.bo.DesPrelimSchemeBo;
|
||||||
import org.dromara.design.domain.DesPrelimScheme;
|
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 org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -81,4 +81,7 @@ public interface IDesPrelimSchemeService extends IService<DesPrelimScheme>{
|
|||||||
* 修改文件
|
* 修改文件
|
||||||
*/
|
*/
|
||||||
Boolean updateFile(MultipartFile file, Long projectId, Long id);
|
Boolean updateFile(MultipartFile file, Long projectId, Long id);
|
||||||
|
|
||||||
|
|
||||||
|
void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
package org.dromara.design.service;
|
package org.dromara.design.service;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import org.dromara.common.core.domain.R;
|
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.vo.DesSchemeVo;
|
||||||
import org.dromara.design.domain.bo.DesSchemeBo;
|
import org.dromara.design.domain.bo.DesSchemeBo;
|
||||||
import org.dromara.design.domain.DesScheme;
|
import org.dromara.design.domain.DesScheme;
|
||||||
@ -82,4 +84,7 @@ public interface IDesSchemeService extends IService<DesScheme>{
|
|||||||
* 修改文件
|
* 修改文件
|
||||||
*/
|
*/
|
||||||
Boolean updateFile(MultipartFile file, Long projectId, Long id);
|
Boolean updateFile(MultipartFile file, Long projectId, Long id);
|
||||||
|
|
||||||
|
|
||||||
|
void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package org.dromara.design.service.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
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.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
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.PageQuery;
|
||||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.dromara.common.utils.BatchNumberGenerator;
|
import org.dromara.common.utils.BatchNumberGenerator;
|
||||||
import org.dromara.common.utils.excel.ExcelDynamicReader;
|
|
||||||
import org.dromara.design.domain.BusBillofquantities;
|
import org.dromara.design.domain.BusBillofquantities;
|
||||||
import org.dromara.design.domain.BusBillofquantitiesVersions;
|
import org.dromara.design.domain.BusBillofquantitiesVersions;
|
||||||
import org.dromara.design.domain.bo.*;
|
import org.dromara.design.domain.bo.*;
|
||||||
import org.dromara.design.domain.dto.MaterialsAndEquipmentExcelDto;
|
|
||||||
import org.dromara.design.domain.vo.*;
|
import org.dromara.design.domain.vo.*;
|
||||||
|
import org.dromara.design.exportUtil.bill.BillOfQuantitiesExport;
|
||||||
import org.dromara.design.mapper.BusBillofquantitiesVersionsMapper;
|
import org.dromara.design.mapper.BusBillofquantitiesVersionsMapper;
|
||||||
import org.dromara.design.service.IBusBillofquantitiesService;
|
import org.dromara.design.service.IBusBillofquantitiesService;
|
||||||
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
|
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
|
||||||
@ -363,6 +362,35 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
|
|||||||
return BeanUtil.copyToList(busBillofquantities,BusBillofquantitiesVo.class);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归构建树形结构
|
* 递归构建树形结构
|
||||||
*
|
*
|
||||||
|
|||||||
@ -2,8 +2,11 @@ package org.dromara.design.service.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.ProcessDeleteEvent;
|
||||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
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 com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.design.domain.DesCollect;
|
import org.dromara.design.domain.DesCollect;
|
||||||
|
import org.dromara.design.domain.DesCollectCatalogue;
|
||||||
import org.dromara.design.domain.DesCollectFile;
|
import org.dromara.design.domain.DesCollectFile;
|
||||||
import org.dromara.design.domain.bo.DesCollectFileBo;
|
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.domain.vo.DesCollectFileVo;
|
||||||
import org.dromara.design.mapper.DesCollectFileMapper;
|
import org.dromara.design.mapper.DesCollectFileMapper;
|
||||||
|
import org.dromara.design.service.IDesCollectCatalogueService;
|
||||||
import org.dromara.design.service.IDesCollectFileService;
|
import org.dromara.design.service.IDesCollectFileService;
|
||||||
import org.dromara.system.domain.vo.SysOssUploadVo;
|
import org.dromara.system.domain.vo.SysOssUploadVo;
|
||||||
import org.dromara.system.domain.vo.SysOssVo;
|
import org.dromara.system.domain.vo.SysOssVo;
|
||||||
@ -31,10 +37,22 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.io.FileInputStream;
|
||||||
import java.util.Map;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
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;
|
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
||||||
|
|
||||||
@ -53,6 +71,8 @@ public class DesCollectFileServiceImpl extends ServiceImpl<DesCollectFileMapper,
|
|||||||
|
|
||||||
private final ISysOssService ossService;
|
private final ISysOssService ossService;
|
||||||
|
|
||||||
|
private final IDesCollectCatalogueService collectCatalogueService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询收资文件
|
* 查询收资文件
|
||||||
*
|
*
|
||||||
@ -217,4 +237,144 @@ public class DesCollectFileServiceImpl extends ServiceImpl<DesCollectFileMapper,
|
|||||||
log.info("监听删除流程事件,上传资料审核任务执行了{}", processDeleteEvent.toString());
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
package org.dromara.design.service.impl;
|
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.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
@ -14,15 +17,19 @@ import org.dromara.common.core.constant.HttpStatus;
|
|||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.utils.ObjectUtils;
|
import org.dromara.common.core.utils.ObjectUtils;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
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.DesConstructionSchedulePlan;
|
||||||
import org.dromara.design.domain.bo.DesUserBo;
|
import org.dromara.design.domain.bo.DesUserBo;
|
||||||
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||||
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
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.mapper.DesConstructionSchedulePlanMapper;
|
||||||
import org.dromara.design.service.IDesConstructionSchedulePlanService;
|
import org.dromara.design.service.IDesConstructionSchedulePlanService;
|
||||||
|
|
||||||
import org.dromara.project.service.IBusProjectService;
|
import org.dromara.project.service.IBusProjectService;
|
||||||
import org.dromara.system.domain.vo.SysDictDataVo;
|
import org.dromara.system.domain.vo.SysDictDataVo;
|
||||||
|
import org.dromara.system.domain.vo.SysUserExportVo;
|
||||||
import org.dromara.system.service.ISysDictDataService;
|
import org.dromara.system.service.ISysDictDataService;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -36,6 +43,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 施工进度计划Service业务层处理
|
* 施工进度计划Service业务层处理
|
||||||
@ -624,4 +632,97 @@ public class DesConstructionSchedulePlanServiceImpl extends ServiceImpl<DesConst
|
|||||||
}
|
}
|
||||||
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package org.dromara.design.service.impl;
|
package org.dromara.design.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.ProcessDeleteEvent;
|
||||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
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.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.design.domain.DesScheme;
|
import org.dromara.design.domain.dto.ExportDto;
|
||||||
import org.dromara.message.domain.MsgConfig;
|
|
||||||
import org.dromara.message.domain.bo.MsgNoticeBo;
|
|
||||||
import org.dromara.message.domain.dto.SendMsgDto;
|
|
||||||
import org.dromara.message.service.IMsgConfigService;
|
import org.dromara.message.service.IMsgConfigService;
|
||||||
import org.dromara.message.service.IMsgNoticeService;
|
import org.dromara.message.service.IMsgNoticeService;
|
||||||
import org.dromara.system.domain.vo.SysOssUploadVo;
|
|
||||||
import org.dromara.system.domain.vo.SysOssVo;
|
import org.dromara.system.domain.vo.SysOssVo;
|
||||||
import org.dromara.system.service.ISysOssService;
|
import org.dromara.system.service.ISysOssService;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
@ -33,8 +32,18 @@ import org.dromara.design.mapper.DesPrelimSchemeMapper;
|
|||||||
import org.dromara.design.service.IDesPrelimSchemeService;
|
import org.dromara.design.service.IDesPrelimSchemeService;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
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.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
||||||
|
|
||||||
@ -189,6 +198,105 @@ public class DesPrelimSchemeServiceImpl extends ServiceImpl<DesPrelimSchemeMappe
|
|||||||
return baseMapper.updateById(desPrelimScheme)>0;
|
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'
|
* 正常使用只需#processEvent.flowCode=='leave1'
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package org.dromara.design.service.impl;
|
package org.dromara.design.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.ProcessDeleteEvent;
|
||||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
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.common.oss.factory.OssFactory;
|
||||||
import org.dromara.design.domain.DesCollectFile;
|
import org.dromara.design.domain.DesCollectFile;
|
||||||
import org.dromara.design.domain.DesPrelimScheme;
|
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.SysOssUploadVo;
|
||||||
import org.dromara.system.domain.vo.SysOssVo;
|
import org.dromara.system.domain.vo.SysOssVo;
|
||||||
import org.dromara.system.service.ISysOssService;
|
import org.dromara.system.service.ISysOssService;
|
||||||
@ -31,10 +35,23 @@ import org.dromara.design.mapper.DesSchemeMapper;
|
|||||||
import org.dromara.design.service.IDesSchemeService;
|
import org.dromara.design.service.IDesSchemeService;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
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.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
||||||
|
|
||||||
@ -183,6 +200,101 @@ public class DesSchemeServiceImpl extends ServiceImpl<DesSchemeMapper, DesScheme
|
|||||||
return baseMapper.updateById(desScheme)>0;
|
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'
|
* 正常使用只需#processEvent.flowCode=='leave1'
|
||||||
|
|||||||
@ -40,6 +40,18 @@ public class DroProjectDrone implements Serializable {
|
|||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
|
/**
|
||||||
|
* 飞机sn
|
||||||
|
*/
|
||||||
|
private String airplaneSn;
|
||||||
|
/**
|
||||||
|
* 无人机机场模型id
|
||||||
|
*/
|
||||||
|
private String droneModleId;
|
||||||
|
/**
|
||||||
|
* 飞机模型id
|
||||||
|
*/
|
||||||
|
private String airplaneModleId;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,5 +43,18 @@ public class DroProjectDroneBo extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞机sn
|
||||||
|
*/
|
||||||
|
private String airplaneSn;
|
||||||
|
/**
|
||||||
|
* 无人机机场模型id
|
||||||
|
*/
|
||||||
|
private String droneModleId;
|
||||||
|
/**
|
||||||
|
* 飞机模型id
|
||||||
|
*/
|
||||||
|
private String airplaneModleId;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user