Compare commits

..

14 Commits

Author SHA1 Message Date
chenchuchuan 792a8cc96f fix(online): 过滤表格字段类型避免误识别为新增字段 4 weeks ago
chenchuchuan 8d491ff216 docs(common): 更新在线表单控制器逻辑并添加项目总览文档 1 month ago
chenchuchuan 446eeba6be docs(common): 更新在线表单控制器逻辑并添加项目总览文档 1 month ago
chenchuchuan d26d2b14a5 feat(online): 添加从表关联字段同步功能和表单类型查询接口 1 month ago
chenchuchuan ed03e2a488 Merge branch 'master' of http://124.222.6.97:3000/sunquan/apelet_tenant into online-form-direct-creation 1 month ago
chenchuchuan 3104f1b026 refactor(common-generator): 优化在线表单头服务实现 2 months ago
chenchuchuan 6fc400ab77 feat(online): 表单字段保存时,修改字段备注取值逻辑 2 months ago
chenchuchuan e9c3b36512 feat(online): 添加数据表字段保存功能 2 months ago
chenchuchuan 7dcb3bb5c9 refactor(online): 使用常量替换硬编码的元数据源字符串 2 months ago
chenchuchuan f405113552 fix(controller): 修正在线页面控制器返回类型 2 months ago
sunquan 15dd345744 fix:补在线表单控件和数据库字段类型 2 months ago
sunquan 1d42dfc702 fix:去掉nacos配置 app-config、t-admin-dev 2 months ago
sunquan c665cde835 fix:去掉nacos配置 2 months ago
sunquan 610a4d10ed feature-online-form-direct-creation:收到请求后自动创建数据库物理表 + 元数据记录 + 数据源配置 2 months ago
  1. 161
      application-tenant/tenant-admin/src/main/resources/application-config.yml
  2. 22
      application-tenant/tenant-admin/src/main/resources/bootstrap.yml
  3. 82
      application-tenant/tenant-admin/src/main/resources/logback-spring.xml
  4. 355
      application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml
  5. 34
      common/common-flow-online/pom.xml
  6. 88
      common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteAspect.java
  7. 38
      common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteExAspect.java
  8. 13
      common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineAutoConfig.java
  9. 20
      common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineProperties.java
  10. 1437
      common/common-flow-online/src/main/java/apelet/common/flow/online/controller/FlowOnlineOperationController.java
  11. 94
      common/common-flow-online/src/main/java/apelet/common/flow/online/listener/ApprovalSettingsByCreateListener.java
  12. 101
      common/common-flow-online/src/main/java/apelet/common/flow/online/object/TransactionalFlowBusinessData.java
  13. 13
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowEntryOnlineService.java
  14. 163
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowOnlineOperationService.java
  15. 319
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowEntryOnlineServiceImpl.java
  16. 102
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java
  17. 434
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineOperationServiceImpl.java
  18. 2
      common/common-flow-online/src/main/resources/META-INF/spring.factories
  19. 5
      common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java
  20. 11
      common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java
  21. 39
      common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java
  22. 539
      common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java
  23. 91
      common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java
  24. 14
      common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java
  25. 10
      common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java
  26. 17
      common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java
  27. 8
      common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java
  28. 7
      common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java
  29. 29
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java
  30. 218
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java
  31. 6
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java
  32. 20
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java
  33. 5
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java
  34. 87
      common/common-online/src/main/java/apelet/common/online/util/WidgetFieldTypeMapping.java
  35. 3
      common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java
  36. 1
      common/pom.xml

161
application-tenant/tenant-admin/src/main/resources/application-config.yml

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
server:
tomcat:
uri-encoding: UTF-8
threads:
max: 100
min-spare: 10
servlet:
encoding:
force: true
charset: UTF-8
enabled: true
logging:
config: classpath:logback-spring.xml
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
mvc:
converters:
preferred-json-mapper: fastjson
freemarker:
template-loader-path: classpath:/template/
cache: false
charset: UTF-8
check-template-location: true
content-type: text/html
expose-request-attributes: false
expose-session-attributes: false
request-context-attribute: request
suffix: .ftl
cloud:
sentinel:
transport:
# sentinel控制台地址。
# 由于8080端口容易与其他服务端口冲突,可以在启动sentinel控制台时动态修改,如:
# java -Dserver.port=8858 -jar sentinel-dashboard-$VERSION.jar
# 改为8858后,下面的配置端口也需要改为:localhost:8858。
dashboard: localhost:8858
# feign 配置
feign:
sentinel:
enabled: false
httpclient:
enabled: true
max-connections: 200
max-connections-per-route: 50
client:
config:
default:
# 输出feignclient的日志,缺省值为none,可选值为none/basic/headers/full
# 注意:需要把feignclient类所在包的日志级别设置为debug时才生效。如:
# logging:
# level:
# com.demo.multi: info
# com.demo.multi.coursepaperinterface.client: debug
loggerLevel: full
connectTimeout: 5000
readTimeout: 10000
compression:
request:
enabled: true
response:
enabled: true
common-core:
# 可选值为 mysql / postgresql / oracle / dm8 / kingbase
databaseType: mysql
swagger:
# 当enabled为false的时候,则可禁用swagger。
enabled: true
# 工程的基础包名。
basePackage: apelet
# 工程服务的基础包名。
serviceBasePackage:
title: 小猿微服务工程
description: 小猿微服务工程详情
version: 1.0
knife4j:
# 打开knife4j增强模式。
enable: true
aliyun:
oss:
enabled: false
expireSeconds: 1000
# 下面几项均需在申请阿里云OSS后,根据自己的实际情况进行配置。
endpoint:
accessKey:
secretKey:
bucketName:
minio:
enabled: false
endpoint:
accessKey:
secretKey:
bucketName:
# 暴露监控端点
management:
metrics:
tags:
application: ${spring.application.name}
endpoints:
web:
exposure:
include: '*'
jmx:
exposure:
include: '*'
endpoint:
# 与中间件相关的健康详情也会被展示
health:
show-details: always
configprops:
# 在/actuator/configprops中,所有包含password的配置,将用 * 隐藏。
# 如果不想隐藏任何配置项的值,可以直接使用如下被注释的空值。
# keys-to-sanitize:
keys-to-sanitize: password
server:
base-path: "/"
# 存储session数据的Redis,所有服务均需要,因此放到公共配置中。
# 根据实际情况,该Redis也可以用于存储其他数据。
common-redis:
# redisson的配置。每个服务可以自己的配置文件中覆盖此选项。
redisson:
# 如果该值为false,系统将不会创建RedissionClient的bean。
enabled: true
# mode的可用值为,single/cluster/sentinel/master-slave
mode: single
# single: 单机模式
# address: redis://localhost:6379
# cluster: 集群模式
# 每个节点逗号分隔,同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# sentinel:
# 每个节点逗号分隔,同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# master-slave:
# 每个节点逗号分隔,第一个为主节点,其余为从节点。同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
address: redis://192.168.100.25:6379
# 链接超时,单位毫秒。
timeout: 6000
# 单位毫秒。分布式锁的超时检测时长。
# 如果一次锁内操作超该毫秒数,或在释放锁之前异常退出,Redis会在该时长之后主动删除该锁使用的key。
lockWatchdogTimeout: 60000
# redis 密码,空可以不填。
password:
database: 0
pool:
# 连接池数量。
poolSize: 20
# 连接池中最小空闲数量。
minIdle: 5

22
application-tenant/tenant-admin/src/main/resources/bootstrap.yml

@ -3,20 +3,14 @@ spring: @@ -3,20 +3,14 @@ spring:
name: tenant-admin
profiles:
active: @profile.name@
cloud:
nacos:
discovery:
server-addr: @discovery.server-addr@
config:
server-addr: @config.server-addr@
group: @config.group@
namespace: @config.namespace@
file-extension: yaml
# 共享配置文件,排序越高后,优先级越高。
shared-configs:
- data-id: application-${spring.profiles.active}.yaml
group: @config.group@
refresh: true
main:
allow-bean-definition-overriding: true
allow-circular-references: true
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
config:
import:
- classpath:tenant-admin-dev.yml
- classpath:application-config.yml # 引入共享配置

82
application-tenant/tenant-admin/src/main/resources/logback-spring.xml

@ -16,6 +16,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, @@ -16,6 +16,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息,
<property name="LOG_PATTERN" value="[%-5level] [%d{yyyy-MM-dd HH:mm:ss}] [%thread] ==> [%tid] %msg%n" />
<property name="LOG_PATTERN_EX" value="[%-5level] [%d{yyyy-MM-dd HH:mm:ss}] T:[%X{traceId}] S:[%X{sessionId}] U:[%X{userId}] [%thread] ==> [%tid] %msg%n" />
<!--
<appender name="kafka_log" class="com.github.danielwegener.logback.kafka.KafkaAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
@ -27,6 +28,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, @@ -27,6 +28,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息,
<deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
<producerConfig>bootstrap.servers=@kafka.log-server-addr@</producerConfig>
</appender>
-->
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
@ -89,48 +91,48 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, @@ -89,48 +91,48 @@ debug:当此属性设置为true时,将打印出logback内部日志信息,
</logger>
<logger name="apelet" additivity="false" level="info">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<!-- 这里将dao的日志级别设置为DEBUG,是为了SQL语句的输出 -->
<!-- <appender-ref ref="kafka_log"/> -->
<appender-ref ref="file_log" />
</logger>
<!-- 这里将dao的日志级别设置为DEBUG,是为了SQL语句的输出 -->
<logger name="apelet.tenantadmin.tenant.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.upms.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.app.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.mobile.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.online.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.flow.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="org.flowable" additivity="false" level="info">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.report.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.upms.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.app.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.mobile.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.online.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.flow.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="org.flowable" additivity="false" level="info">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.report.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>

355
application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml

@ -0,0 +1,355 @@ @@ -0,0 +1,355 @@
logging:
level:
# 这里设置的日志级别优先于logback-spring.xml文件Loggers中的日志级别。
apelet: info
config: classpath:logback-spring.xml
server:
port: 8075
application:
# Jwt令牌加密的签名值。该值的长度要超过10个字符(过短会报错)。
tokenSigningKey: tenantadmin-signing-key-signing-key
# Jwt令牌在Http Header中的键名称。
tokenHeaderKey: Authorization
# Jwt令牌刷新后在Http Header中的键名称。
refreshedTokenHeaderKey: RefreshedToken
# Jwt令牌过期时间(毫秒)。
expiration: 72000000
# 初始化密码。
defaultUserPassword: 123456
# 缺省的文件上传根目录。
uploadFileBaseDir: ./zz-resource/upload-files/tenant-admin
# 跨域的IP(http://192.168.10.10:8086)白名单列表,多个IP之间逗号分隔(* 表示全部信任,空白表示禁用跨域信任)。
credentialIpList: "*"
# 同步租户公共管理数据到消息队列的主题,再由同步消费者服务将数据同步到目标数据库中。
tenantSyncTopic: TenantSync
# Session会话和用户和权限在Redis中的过期时间(秒)。
sessionExpiredSeconds: 86400
# Session的数据权限信息在Redis中的过期时间(秒)。
dataPermExpiredSeconds: 86460
# 是否排他登录。
excludeLogin: false
common-eas:
urlPrefix: /tenantadmin/eas
common-ext:
urlPrefix: /tenantadmin/commonext
common-mobile:
# 注意不要以反斜杠(/)结尾。
urlPrefix: /tenantadmin/mobile
# 在线表单业务数据上传资源路径
uploadFileBaseDir: ./zz-resource/upload-files/mobile
common-online:
# 注意不要以反斜杠(/)结尾。
urlPrefix: /tenantadmin/online
# 打印接口的路径,不要以反斜杠(/)结尾。
printUrlPath: /tenantadmin/report/reportPrint/print
# 在线表单业务数据上传资源路径
uploadFileBaseDir: ./zz-resource/upload-files/online
# 如果为false,在线表单模块中所有Controller接口将不能使用。
operationEnabled: true
# 1: minio 2: aliyun-oss 3: qcloud-cos。
distributeStoreType: 2
# 调用render接口时候,是否打开一级缓存加速。
enableRenderCache: false
# 脱敏字段的掩码字符,只能为单个字符。
maskChar: '*'
# 下面的url列表,请保持反斜杠(/)结尾。
viewUrlList:
- ${common-online.urlPrefix}/onlineOperation/viewByDatasourceId/
- ${common-online.urlPrefix}/onlineOperation/viewByOneToManyRelationId/
- ${common-online.urlPrefix}/onlineOperation/listByDatasourceId/
- ${common-online.urlPrefix}/onlineOperation/listByOneToManyRelationId/
- ${common-online.urlPrefix}/onlineOperation/exportByDatasourceId/
- ${common-online.urlPrefix}/onlineOperation/exportByOneToManyRelationId/
- ${common-online.urlPrefix}/onlineOperation/downloadDatasource/
- ${common-online.urlPrefix}/onlineOperation/downloadOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/print/
editUrlList:
- ${common-online.urlPrefix}/onlineOperation/addDatasource/
- ${common-online.urlPrefix}/onlineOperation/addOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/updateDatasource/
- ${common-online.urlPrefix}/onlineOperation/updateOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/deleteDatasource/
- ${common-online.urlPrefix}/onlineOperation/deleteOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/deleteBatchDatasource/
- ${common-online.urlPrefix}/onlineOperation/deleteBatchOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/uploadDatasource/
- ${common-online.urlPrefix}/onlineOperation/uploadOneToManyRelation/
common-flow:
# 请慎重修改urlPrefix的缺省配置,注意不要以反斜杠(/)结尾。如必须修改其他路径,请同步修改数据库脚本。
urlPrefix: /tenantadmin/flow
# 如果为false,流程模块的所有Controller中的接口将不能使用。
operationEnabled: true
common-report:
# 注意不要以反斜杠(/)结尾。
urlPrefix: /tenantadmin/report
# 如果为false,报表模块的所有Controller中的接口将不能使用。
operationEnabled: true
# 该配置项仅当打印模板中,打印图片字段时,才会需要。
# 这里的url配置只是一个示例,并不能保证开箱即用,代码示例和说明可参考common-report模块
# example包内的ReportExampleController中的代码和详细说明。
imageDownloadUrl: "http://localhost:8083/tenantadmin/report/example/downloadDirectly"
# 该配置用于报表部分的数据权限过滤功能。
# 当前数据权限需要获取指定部门Ids的所有下级子部门Ids的时候,会调用该接口。
dataPermAllChildrenDeptIdUrl: "http://localhost:8083/tenantadmin/upms/sysDept/listAllChildDeptIdByParentIds"
# 当前服务是否为可视化后台服务。
isVisualization: false
# 下面的url列表,请保持反斜杠(/)结尾。
viewUrlList:
- ${common-report.urlPrefix}/reportOperation/listData/
datasync:
producer:
# upms服务作为生产者,需要打开该选项。
enabled: true
# Redisson 分布式锁的名称。
# 当存在多个发送者实例时,需要分布式锁控制只有一个实例可以执行消息流水读取并发送到MQ的操作。
distributeLockName: tenant-datasync
# 每次读取生产者消息流水的等待时间间隔,单位分钟。
fetchTransIntervalMins: 2
# 每次读取生产者消息流水的记录数量。
fetchCount: 10
rocketmq:
name-server: 127.0.0.1:9876
producer:
group: apelet_saas
datafilter:
tenant:
enabled: false
dataperm:
enabled: true
# 在拼接数据权限过滤的SQL时,我们会用到sys_dept_relation表,该表的前缀由此配置项指定。
# 如果没有前缀,请使用 "" 。
deptRelationTablePrefix: xy_
# 是否在每次执行数据权限查询过滤时,都要进行菜单Id和URL之间的越权验证。
enableMenuPermVerify: true
sequence:
# 是否使用基于美团Leaf的分布式Id生成器。
advanceIdGenerator: true
# 多个zk服务之间逗号分隔。
zkAddress: localhost:2181
# 与本机的ip一起构成zk中标识不同服务实例的key值。
idPort: 18083
# zk中生成WorkNode的路径。不同的业务可以使用不同的路径,以免冲突。
zkPath: apelet
common-log:
# 操作日志配置,对应配置文件common-log/OperationLogProperties.java
operation-log:
enabled: false
kafkaTopic: SysOperationLog-TenantAdmin
mybatis-plus:
mapper-locations: classpath:apelet/tenantadmin/tenant/dao/mapper/*Mapper.xml,apelet/tenantadmin/upms/dao/mapper/*Mapper.xml,apelet/common/report/dao/mapper/*Mapper.xml,apelet/common/datasync/dao/mapper/*Mapper.xml,apelet/common/log/dao/mapper/*Mapper.xml,apelet/common/flow/dao/mapper/*Mapper.xml,apelet/common/online/dao/mapper/*Mapper.xml,apelet/common/generator/dao/mapper/*Mapper.xml,apelet/common/botp/dao/mapper/*Mapper.xml,apelet/tenantadmin/app/dao/mapper/*Mapper.xml,apelet/common/mobile/dao/mapper/*Mapper.xml,apelet/common/eas/dao/mapper/*Mapper.xml,classpath:mapper/*Mapper.xml
type-aliases-package: apelet.tenantadmin.*.model,apelet.common.report.model,apelet.common.datasync.model,apelet.common.log.model,apelet.common.flow.model,apelet.common.online.model,apelet.common.mobile.model,apelet.common.eas.model
global-config:
db-config:
logic-delete-value: -1
logic-not-delete-value: 1
# 自动分页的配置
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: false
params: count=countSql
aj:
captcha:
# 缓存local/redis..
cache-type: redis
# 验证码类型default两种都实例化。
type: default
# 右下角水印文字(我的水印)
water-mark: 我的水印
#点选字体样式 默认Font.BOLD
font-style: 1
#点选字体字体大小
font-size: 25
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/jigsaw
jigsaw: classpath:images/jigsaw
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/pic-click
pic-click: classpath:images/pic-click
history-data-clear-enable: false
# 接口请求次数一分钟限制是否开启 true|false
req-frequency-limit-enable: false
# 验证失败5次,get接口锁定
req-get-lock-limit: 5
# 验证失败后,锁定时间间隔,s
req-get-lock-seconds: 360
# get接口一分钟内请求数限制
req-get-minute-limit: 30
# check接口一分钟内请求数限制
req-check-minute-limit: 30
# verify接口一分钟内请求数限制
req-verify-minute-limit: 60
# 校验滑动拼图允许误差偏移量(默认5像素)
slip-offset: 5
# aes加密坐标开启或者禁用(true|false)
aes-status: true
# 滑动干扰项(0/1/2)
interference-options: 2
# 开发数据库相关配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 租户管理数据源。
tenant-admin:
url: jdbc:mysql://192.168.100.18:3306/apelet_admin_xk?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: X7&9p8L2@6z4K7!8
# 租户业务平台的通用业务数据,如字典、在线表单、流程、报表的定义等数据。
tenant-common:
url: jdbc:mysql://192.168.100.18:3306/apelet_admin_xk?characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
username: root
password: X7&9p8L2@6z4K7!8
# 默认生成的操作日志数据源配置。
operation-log:
url: jdbc:mysql://192.168.100.18:3306/apelet_admin_xk?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: X7&9p8L2@6z4K7!8
driverClassName: com.mysql.cj.jdbc.Driver
name: tenant-admin
initialSize: 10
minIdle: 10
maxActive: 50
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
maxOpenPreparedStatements: 20
validationQuery: SELECT 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall
useGlobalDataSourceStat: true
web-stat-filter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*,/actuator/*,/ai/*"
stat-view-servlet:
enabled: true
urlPattern: /druid/*
resetEnable: true
eas:
config:
easurl:
userName:
password: 1
dcName:
dbType: 2
ltpaToken: C:\work\company\project\plat\orm\apelet_saas\common\common-eas\src\main\resources\LtpaToken.properties
#easurl: https://ehr.jztweb.com
#userName: SSQadmin
#password: SSQadmin
#dcName: EHR_001
#dbType: 2
#ltpaToken: C:\work\company\project\plat\orm\apelet_saas\common\common-eas\src\main\resources\LtpaToken_zs.properties
hubCloud:
#测试
#secret: RXmdRE0lf5ngGfLJSSj4
#appId: 501019058
secret:
appId:
dingTalk:
secret: 95gAMy_7nWGqRwFbt_kLzRpI9BqKhB7LM3SPgpcvV8Q7s0Az_PMei_yRxWE6LmhV
project:
server:
url: https://jgzy-api.nbjgcloud.com
sync:
url: http://10.22.98.37:9999
loginUrl: /sys/login
loginName: nbjg
loginPassword: NBjj123456
userUrl:
projectUrl:
ai:
dify:
baseUrl:
datasetKey:
appKey:
appUrl:
pandoc:
panurl: pandoc
genurl:
templateFileName: sample-orgin-change.docx
luaFileName: parse-html.lua
preUrl:
mineru:
workspace:
batchfile:
timeout: 10000
baseurl: http://192.168.77.6:8000
pdfurl: /file_parse
mdSaveDir: /app/work/pdf
cqurl:
config:
url:
gys:
#client_id: xy_mobile
#client_secret: asdasdnsandlkadnlkas!@#@!3WWW
#username: sunquan
client_id:
client_secret:
username:
accountId:
msg:
notice:
# 是否启用定时消息提醒(true启用,false禁用)
enabled: true
# 定时任务cron表达式,控制提醒执行频率
# 默认值:0 0 * * * ? (每小时整点执行)
# 常用示例:
# 0 0 * * * ? - 每小时执行一次
# 0 0 */2 * * ? - 每2小时执行一次
# 0 0 9 * * ? - 每天上午9点执行
# 0 0 9,18 * * ? - 每天上午9点和下午6点执行
# 0 */30 * * * ? - 每30分钟执行一次
cron: "0 0 * * * ?"
# 默认提醒消息内容
default-message: hello
# ====================================
# 短信通知配置
# ====================================
sms:
# 是否启用短信通知
enabled: false
# 短信API地址(根据实际短信服务商配置)
api-url: https://sms.example.com/send
# 短信API密钥
api-key: your-sms-api-key
# 短信签名
sign: "【您的签名】"
# ====================================
# 邮箱通知配置
# ====================================
email:
# 是否启用邮箱通知
enabled: true
# SMTP服务器地址
smtp-host: smtp.qq.com
# SMTP端口(25或465或587)
smtp-port: 465
# 发件人邮箱地址
from-email: 287962685@qq.com
# 发件人密码或授权码
password: evccxezdmdnpbhbh
# 是否启用SSL(如果使用465端口,建议启用SSL)
ssl-enabled: true

34
common/common-flow-online/pom.xml

@ -1,34 +0,0 @@ @@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>apelet</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-flow-online</artifactId>
<version>1.0.0</version>
<name>common-flow-online</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>apelet</groupId>
<artifactId>common-flow</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>apelet</groupId>
<artifactId>common-online</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>apelet</groupId>
<artifactId>common-orm</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

88
common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteAspect.java

@ -1,88 +0,0 @@ @@ -1,88 +0,0 @@
package apelet.common.flow.online.aop;
import apelet.common.core.object.TokenData;
import apelet.common.core.util.AopTargetUtil;
import apelet.common.core.util.MyDateUtil;
import apelet.common.flow.dao.FlowTransProducerMapper;
import apelet.common.flow.model.FlowTaskComment;
import apelet.common.flow.model.FlowTransProducer;
import apelet.common.flow.online.object.TransactionalFlowBusinessData;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
/**
* 拦截流程业务多数据库数据写入的AOP对象
*
* @author guifc
* @date 2023-08-04
*/
@Aspect
@Component
@Slf4j
public class FlowMultiDatabaseWriteAspect {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private FlowTransProducerMapper flowTransProducerMapper;
@Pointcut("execution(public * apelet.common.flow..service.impl..*(..)) " +
"&& @annotation(apelet.common.core.annotation.MultiDatabaseWriteMethod)")
public void multiDatabaseWriteMethodPointCut() {
// 空注释,避免sonar警告
}
@Around("multiDatabaseWriteMethodPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String initMethod = AopTargetUtil.getFullMethodName(joinPoint);
TransactionalFlowBusinessData data = TransactionalFlowBusinessData.getOrCreateFromRequestAttribute();
if (data.getInitMethod() == null) {
data.setInitMethod(initMethod);
}
try {
// 调用原来的方法
Object result = joinPoint.proceed();
if (StrUtil.equals(initMethod, data.getInitMethod()) && CollUtil.isNotEmpty(data.getSqlDataList())) {
this.fillupTransactionalBusinessData();
FlowTransProducer producerData = BeanUtil.copyProperties(data, FlowTransProducer.class);
producerData.setAppCode(TokenData.takeFromRequest().getAppCode());
producerData.setTryTimes(1);
String sqlData = JSON.toJSONStringWithDateFormat(
data.getSqlDataList(), MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
producerData.setSqlData(sqlData);
flowTransProducerMapper.insert(producerData);
eventPublisher.publishEvent(data);
}
return result;
} catch (Exception e) {
TransactionalFlowBusinessData.removeFromRequestAttribute();
throw e;
} finally {
if (StrUtil.equals(initMethod, data.getInitMethod())) {
TransactionalFlowBusinessData.removeFromRequestAttribute();
}
}
}
private void fillupTransactionalBusinessData() {
FlowTaskComment comment = FlowTaskComment.getFromRequest();
TransactionalFlowBusinessData data = TransactionalFlowBusinessData.getFromRequestAttribute();
if (comment != null) {
data.setProcessInstanceId(comment.getProcessInstanceId());
data.setTaskId(comment.getTaskId());
data.setTaskKey(comment.getTaskKey());
data.setTaskName(comment.getTaskName());
data.setTaskComment(comment.getTaskComment());
}
}
}

38
common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteExAspect.java

@ -1,38 +0,0 @@ @@ -1,38 +0,0 @@
package apelet.common.flow.online.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 主要用于拦截FlowTransactionBusinessDataListener监听器类抛出的异常
*
* @author guifc
* @date 2023-08-04
*/
@Aspect
@Component
@Order(1)
@Slf4j
public class FlowMultiDatabaseWriteExAspect {
@Pointcut("execution(public * apelet.common.flow.online.service.impl..*(..)) " +
"&& @annotation(apelet.common.core.annotation.MultiDatabaseWriteMethod)")
public void multiDatabaseWriteMethodPointCut() {
// 空注释,避免sonar警告
}
@Around("multiDatabaseWriteMethodPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Exception e) {
log.error("FlowMultiDatabaseWriteExAspect throw", e);
throw e;
}
}
}

13
common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineAutoConfig.java

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
package apelet.common.flow.online.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* common-flow-online模块的自动配置引导类
*
* @author guifc
* @date 2023-08-04
*/
@EnableConfigurationProperties({FlowOnlineProperties.class})
public class FlowOnlineAutoConfig {
}

20
common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineProperties.java

@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
package apelet.common.flow.online.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 在线表单工作流模块的配置对象
*
* @author guifc
* @date 2023-08-04
*/
@Data
@ConfigurationProperties(prefix = "common-flow-online")
public class FlowOnlineProperties {
/**
* 在线表单的URL前缀
*/
private String urlPrefix;
}

1437
common/common-flow-online/src/main/java/apelet/common/flow/online/controller/FlowOnlineOperationController.java

File diff suppressed because it is too large Load Diff

94
common/common-flow-online/src/main/java/apelet/common/flow/online/listener/ApprovalSettingsByCreateListener.java

@ -1,94 +0,0 @@ @@ -1,94 +0,0 @@
package apelet.common.flow.online.listener;
import apelet.common.core.util.ApplicationContextHolder;
import apelet.common.flow.model.FlowEntry;
import apelet.common.flow.model.FlowNodeTasks;
import apelet.common.flow.model.FlowWorkOrder;
import apelet.common.flow.service.FlowEntryService;
import apelet.common.flow.service.FlowNodeTasksService;
import apelet.common.flow.service.FlowWorkOrderService;
import apelet.common.online.dto.OnlinePluginExecuteDto;
import apelet.common.online.model.OnlineForm;
import apelet.common.online.service.OnlineFormService;
import apelet.common.online.service.OnlineOperationService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.ArrayList;
import java.util.List;
/**
* 审批设置插件by创建事件
*/
public class ApprovalSettingsByCreateListener implements TaskListener {
private final transient FlowNodeTasksService flowNodeTasksService =
ApplicationContextHolder.getBean(FlowNodeTasksService.class);
private final transient FlowEntryService flowEntryService =
ApplicationContextHolder.getBean(FlowEntryService.class);
private final transient OnlineFormService onlineFormService =
ApplicationContextHolder.getBean(OnlineFormService.class);
private final transient FlowWorkOrderService flowWorkOrderService =
ApplicationContextHolder.getBean(FlowWorkOrderService.class);
private final transient OnlineOperationService onlineOperationService =
ApplicationContextHolder.getBean(OnlineOperationService.class);
private final transient RuntimeService runtimeService =
ApplicationContextHolder.getBean(RuntimeService.class);
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
List<Integer> setList = new ArrayList<>();
if (eventName.equals("create")) {
setList.add(1);
} else if (eventName.equals("assignment")) {
setList.add(2);
} else if (eventName.equals("complete")) {
String operationType = (String)delegateTask.getVariable("operationType");
if(operationType != null){
if(operationType.equals("agree")){
setList.add(5);
}else if(operationType.equals("reject")) {
setList.add(6);
}else if(operationType.equals("refuse")){
}
}
setList.add(3);
} else if (eventName.equals("delete")) {
setList.add(4);
}
String taskKey = delegateTask.getTaskDefinitionKey();
String processDefinitionId = delegateTask.getProcessDefinitionId();
LambdaQueryWrapper<FlowNodeTasks> wrapper = new LambdaQueryWrapper<>();
// wrapper.eq(FlowNodeTasks::getFlowId, processDefinitionId);
wrapper.eq(FlowNodeTasks::getTaskKey, taskKey);
wrapper.in(FlowNodeTasks::getExecutionTiming, setList);
List<FlowNodeTasks> list = flowNodeTasksService.list(wrapper);
if (list != null && list.size() > 0) {
for (FlowNodeTasks flowNodeTasks : list) {
LambdaQueryWrapper<FlowEntry> wrapperFlowEntry = new LambdaQueryWrapper<>();
wrapperFlowEntry.eq(FlowEntry::getEntryId, flowNodeTasks.getFlowId());
FlowEntry flowEntry = flowEntryService.getById(flowNodeTasks.getFlowId());
OnlineForm form = onlineFormService.getOnlineFormFromCache(flowEntry.getDefaultFormId());
Long formId = form.getFormId();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(delegateTask.getProcessInstanceId())
.singleResult();
String billId = processInstance.getBusinessKey();
if (billId != null && formId > 0) {
OnlinePluginExecuteDto onlinePluginExecuteDto = new OnlinePluginExecuteDto();
onlinePluginExecuteDto.setBillId(String.valueOf(billId));
onlinePluginExecuteDto.setFormId(formId);
onlinePluginExecuteDto.setButtonName(flowNodeTasks.getOperation());
onlineOperationService.executePlugin(onlinePluginExecuteDto, null, null);
}
}
}
}
}

101
common/common-flow-online/src/main/java/apelet/common/flow/online/object/TransactionalFlowBusinessData.java

@ -1,101 +0,0 @@ @@ -1,101 +0,0 @@
package apelet.common.flow.online.object;
import apelet.common.core.util.ContextUtil;
import apelet.common.online.object.TransactionalBusinessData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.servlet.http.HttpServletRequest;
/**
* 跨库存储工作流的业务数据的事物性事件数据
*
* @author guifc
* @date 2023-08-04
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class TransactionalFlowBusinessData extends TransactionalBusinessData {
/**
* 流程实例Id
*/
private String processInstanceId;
/**
* 流程任务Id
*/
private String taskId;
/**
* 流程任务定义标识
*/
private String taskKey;
/**
* 流程任务定义名称
*/
private String taskName;
/**
* 审批注释
*/
private String taskComment;
private static final ThreadLocal<TransactionalFlowBusinessData> FLOW_BUSINESS_DATA_THREAD_LOCAL = new ThreadLocal<>();
public TransactionalFlowBusinessData() {
super();
}
public TransactionalFlowBusinessData(HttpServletRequest request) {
super(request);
}
/**
* 获取HttpServletRequest属性中的事物性时间对象如果没有情趣对象则从线程本地存储中获取
*
* @return 返回设置在当前请求属性中的事务性时间对象如果不存在则返回NULL
*/
public static TransactionalFlowBusinessData getFromRequestAttribute() {
HttpServletRequest request = ContextUtil.getHttpRequest();
if (request != null) {
return (TransactionalFlowBusinessData) request.getAttribute(BUSINESS_DATA_ATTR_KEY);
}
return FLOW_BUSINESS_DATA_THREAD_LOCAL.get();
}
/**
* 获取HttpServletRequest属性中的事物性时间对象如果存在直接返回否则创建新对象并设置到当前请求的指定属性中
* 如果没有情趣对象则从线程本地存储中创建
*
* @return 返回设置在当前请求属性中的事务性时间对象
*/
public static TransactionalFlowBusinessData getOrCreateFromRequestAttribute() {
HttpServletRequest request = ContextUtil.getHttpRequest();
TransactionalFlowBusinessData data;
if (request != null) {
data = (TransactionalFlowBusinessData) request.getAttribute(BUSINESS_DATA_ATTR_KEY);
if (data != null) {
return data;
}
data = new TransactionalFlowBusinessData(request);
request.setAttribute(BUSINESS_DATA_ATTR_KEY, data);
} else {
data = FLOW_BUSINESS_DATA_THREAD_LOCAL.get();
if (data != null) {
return data;
}
data = new TransactionalFlowBusinessData();
FLOW_BUSINESS_DATA_THREAD_LOCAL.set(data);
}
return data;
}
/**
* 移除该对象
*/
public static void removeFromRequestAttribute() {
if (ContextUtil.hasRequestContext()) {
ContextUtil.getHttpRequest().removeAttribute(BUSINESS_DATA_ATTR_KEY);
} else {
FLOW_BUSINESS_DATA_THREAD_LOCAL.remove();
}
}
}

13
common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowEntryOnlineService.java

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
package apelet.common.flow.online.service;
import apelet.common.flow.model.FlowEntry;
import javax.xml.stream.XMLStreamException;
public interface FlowEntryOnlineService {
void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException;
void processFlowTaskExtList(FlowEntry flowEntry, String taskInfo) throws XMLStreamException;
}

163
common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowOnlineOperationService.java

@ -1,163 +0,0 @@ @@ -1,163 +0,0 @@
package apelet.common.flow.online.service;
import apelet.common.flow.model.FlowTaskComment;
import apelet.common.flow.model.FlowTransProducer;
import apelet.common.flow.model.FlowWorkOrder;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.model.OnlineDatasourceRelation;
import apelet.common.online.model.OnlineTable;
import com.alibaba.fastjson.JSONObject;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 流程操作服务接口
*
* @author guifc
* @date 2023-08-04
*/
public interface FlowOnlineOperationService {
/**
* 启动流程实例并将业务主键Id传给新启动的流程实例
*
* @param processDefinitionId 流程实例Id
* @param dataId 业务主键Id
*/
ProcessInstance startWithBusinessKey(String processDefinitionId, String dataId);
/**
* 启动流程实例并将业务主键Id传给新启动的流程实例
*
* @param processDefinitionId 流程实例Id
* @param dataId 业务主键Id
*/
void startWithBusinessKeyExt(String processDefinitionId, String dataId,Long formId);
/**
* 保存在线表单的数据同时启动流程如果当前用户是第一个用户任务的Assignee
* 或者第一个用户任务的Assignee是流程发起人变量该方法还会自动Take第一个任务
*
* @param processDefinitionId 流程定义Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param table 表对象
* @param data 表数据
*/
void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data);
/**
* 保存在线表单的数据同时启动流程如果当前用户是第一个用户任务的Assignee
* 或者第一个用户任务的Assignee是流程发起人变量该方法还会自动Take第一个任务
*
* @param processDefinitionId 流程定义Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param masterTable 主表对象
* @param masterData 主表数据
* @param slaveDataListMap 关联从表数据Map
*/
void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap);
/**
* 保存在线表单的草稿数据同时启动一个流程实例
*
* @param processDefinitionId 流程定义Id
* @param tableId 在线表单主表Id
* @param masterData 主表数据
* @param slaveData 所有关联从表数据
* @return 流程工单对象
*/
FlowWorkOrder saveNewDraftAndStartProcess(
String processDefinitionId, Long tableId, JSONObject masterData, JSONObject slaveData);
/**
* 保存在线表单的数据同时Take用户任务
*
* @param processInstanceId 流程实例Id
* @param taskId 流程任务Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param table 表对象
* @param data 表数据
*/
void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data);
/**
* 保存在线表单的数据同时Take用户任务
*
* @param processInstanceId 流程实例Id
* @param taskId 流程任务Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param masterTable 主表对象
* @param masterData 主表数据
* @param slaveDataListMap 关联从表数据Map
*/
void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap);
/**
* 保存业务表数据同时接收流程任务
*
* @param task 流程任务
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param datasource 主表所在数据源
* @param masterData 主表数据
* @param masterDataId 主表数据主键
* @param slaveDataListMap 从表数据
*/
void updateAndTakeTask(
Task task,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineDatasource datasource,
JSONObject masterData,
String masterDataId,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap);
/**
* 修复业务数据
*
* @param flowTransProducer 流程操作流水生产者对象
*/
void fixBusinessData(FlowTransProducer flowTransProducer);
/**
* 获取在线表单工作流Id所关联的权限数据包括权限字列表和权限资源列表
*
* @param onlineFormEntryIds 在线表单工作流Id集合
* @return 参数中在线表单工作流Id集合所关联的权限数据
*/
List<Map<String, Object>> calculatePermData(Set<Long> onlineFormEntryIds);
}

319
common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowEntryOnlineServiceImpl.java

@ -1,319 +0,0 @@ @@ -1,319 +0,0 @@
package apelet.common.flow.online.service.impl;
import apelet.common.core.object.TokenData;
import apelet.common.core.util.MyModelUtil;
import apelet.common.flow.constant.FlowConstant;
import apelet.common.flow.dao.FlowEntryMapper;
import apelet.common.flow.dao.FlowEntryPublishMapper;
import apelet.common.flow.dao.FlowEntryPublishVariableMapper;
import apelet.common.flow.listener.*;
import apelet.common.flow.model.*;
import apelet.common.flow.model.constant.FlowEntryStatus;
import apelet.common.flow.object.*;
import apelet.common.flow.online.listener.ApprovalSettingsByCreateListener;
import apelet.common.flow.online.service.FlowEntryOnlineService;
import apelet.common.flow.service.*;
import apelet.common.flow.util.BaseFlowIdentityExtHelper;
import apelet.common.flow.util.FlowCustomExtFactory;
import apelet.common.flow.util.FlowRedisKeyUtil;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Cleanup;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
@Service("flowEntryOnlineService")
public class FlowEntryOnlineServiceImpl implements FlowEntryOnlineService {
@Autowired
private FlowEntryMapper flowEntryMapper;
@Autowired
private FlowEntryPublishMapper flowEntryPublishMapper;
@Autowired
private FlowEntryPublishVariableMapper flowEntryPublishVariableMapper;
@Autowired
private FlowEntryVariableService flowEntryVariableService;
@Autowired
private FlowCategoryService flowCategoryService;
@Autowired
private FlowTaskExtService flowTaskExtService;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private RepositoryService repositoryService;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private CommonRedisUtil commonRedisUtil;
@Transactional(rollbackFor = Exception.class)
@Override
public void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey()));
FlowCategory flowCategory = flowCategoryService.getById(flowEntry.getCategoryId());
InputStream xmlStream = new ByteArrayInputStream(
flowEntry.getBpmnXml().getBytes(StandardCharsets.UTF_8));
@Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(xmlStream);
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
bpmnModel.getMainProcess().setName(flowEntry.getProcessDefinitionName());
bpmnModel.getMainProcess().setId(flowEntry.getProcessDefinitionKey());
flowApiService.addProcessInstanceEndListener(bpmnModel, FlowFinishedListener.class);
List<FlowTaskExt> flowTaskExtList = flowTaskExtService.buildTaskExtList(bpmnModel);
if (StrUtil.isNotBlank(flowEntry.getExtensionData())) {
FlowEntryExtensionData flowEntryExtensionData =
JSON.parseObject(flowEntry.getExtensionData(), FlowEntryExtensionData.class);
this.mergeTaskNotifyData(flowEntryExtensionData, flowTaskExtList);
this.processFlowTaskRevive(flowEntryExtensionData, bpmnModel);
}
this.processFlowTaskExtList(flowTaskExtList, bpmnModel);
TokenData tokenData = TokenData.takeFromRequest();
Deployment deploy = repositoryService.createDeployment()
.addBpmnModel(flowEntry.getProcessDefinitionKey() + ".bpmn", bpmnModel)
.tenantId(tokenData.getTenantId() != null ? tokenData.getTenantId().toString() : tokenData.getAppCode())
.name(flowEntry.getProcessDefinitionName())
.key(flowEntry.getProcessDefinitionKey())
.category(flowCategory.getCode())
.deploy();
ProcessDefinition processDefinition = flowApiService.getProcessDefinitionByDeployId(deploy.getId());
FlowEntryPublish flowEntryPublish = new FlowEntryPublish();
flowEntryPublish.setEntryPublishId(idGenerator.nextLongId());
flowEntryPublish.setEntryId(flowEntry.getEntryId());
flowEntryPublish.setProcessDefinitionId(processDefinition.getId());
flowEntryPublish.setDeployId(processDefinition.getDeploymentId());
flowEntryPublish.setPublishVersion(processDefinition.getVersion());
flowEntryPublish.setActiveStatus(true);
flowEntryPublish.setMainVersion(flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED));
flowEntryPublish.setCreateUserId(TokenData.takeFromRequest().getUserId());
flowEntryPublish.setPublishTime(new Date());
flowEntryPublish.setInitTaskInfo(initTaskInfo);
flowEntryPublish.setExtensionData(flowEntry.getExtensionData());
flowEntryPublish.setStartConditions(flowEntry.getStartConditions());
AnalyzedNode rootNode = flowApiService.analyzeBpmnRoads(processDefinition.getId());
flowEntryPublish.setAnalyzedNodeJson(JSON.toJSONString(rootNode));
flowEntryPublishMapper.insert(flowEntryPublish);
FlowEntry updatedFlowEntry = new FlowEntry();
updatedFlowEntry.setEntryId(flowEntry.getEntryId());
updatedFlowEntry.setStatus(FlowEntryStatus.PUBLISHED);
updatedFlowEntry.setLatestPublishTime(new Date());
// 对于从未发布过的工作,第一次发布的时候会将本地发布置位主版本。
if (flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED)) {
updatedFlowEntry.setMainEntryPublishId(flowEntryPublish.getEntryPublishId());
}
flowEntryMapper.updateById(updatedFlowEntry);
FlowEntryVariable flowEntryVariableFilter = new FlowEntryVariable();
flowEntryVariableFilter.setEntryId(flowEntry.getEntryId());
List<FlowEntryVariable> flowEntryVariableList =
flowEntryVariableService.getFlowEntryVariableList(flowEntryVariableFilter, null);
if (CollUtil.isNotEmpty(flowTaskExtList)) {
flowTaskExtList.forEach(t -> t.setProcessDefinitionId(processDefinition.getId()));
flowTaskExtService.saveBatch(flowTaskExtList);
}
this.insertEntryPublishVariables(flowEntryVariableList, flowEntryPublish.getEntryPublishId());
}
@Override
public void processFlowTaskExtList(FlowEntry flowEntry,String taskInfo) throws XMLStreamException {
InputStream xmlStream = new ByteArrayInputStream(
flowEntry.getBpmnXml().getBytes(StandardCharsets.UTF_8));
@Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(xmlStream);
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
bpmnModel.getMainProcess().setName(flowEntry.getProcessDefinitionName());
bpmnModel.getMainProcess().setId(flowEntry.getProcessDefinitionKey());
List<FlowTaskExt> flowTaskExtList = flowTaskExtService.buildTaskExtList(bpmnModel);
List<FlowElement> elementList = new LinkedList<>();
this.calculateAllElementList(bpmnModel.getMainProcess().getFlowElements(), elementList);
Map<String, FlowElement> elementMap = elementList.stream()
.filter(UserTask.class::isInstance).collect(Collectors.toMap(FlowElement::getId, c -> c));
for (FlowTaskExt t : flowTaskExtList) {
UserTask userTask = (UserTask) elementMap.get(t.getTaskId());
// create 创建
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "create");
// assignment 分配/指派
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "assignment");
// complete 完成
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "complete");
// delete 删除
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "delete");
}
}
private void calculateAllElementList(Collection<FlowElement> elements, List<FlowElement> resultList) {
resultList.addAll(elements);
for (FlowElement element : elements) {
if (element instanceof SubProcess) {
this.calculateAllElementList(((SubProcess) element).getFlowElements(), resultList);
}
}
}
private void mergeTaskNotifyData(FlowEntryExtensionData flowEntryExtensionData, List<FlowTaskExt> flowTaskExtList) {
if (CollUtil.isEmpty(flowEntryExtensionData.getNotifyTypes())) {
return;
}
List<String> flowTaskNotifyTypes =
flowEntryExtensionData.getNotifyTypes().stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
if (CollUtil.isEmpty(flowTaskNotifyTypes)) {
return;
}
for (FlowTaskExt flowTaskExt : flowTaskExtList) {
if (flowTaskExt.getExtraDataJson() == null) {
JSONObject o = new JSONObject();
o.put(FlowConstant.USER_TASK_NOTIFY_TYPES_KEY, flowTaskNotifyTypes);
flowTaskExt.setExtraDataJson(o.toJSONString());
} else {
FlowUserTaskExtData taskExtData =
JSON.parseObject(flowTaskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isEmpty(taskExtData.getFlowNotifyTypeList())) {
taskExtData.setFlowNotifyTypeList(flowTaskNotifyTypes);
} else {
Set<String> notifyTypesSet = taskExtData.getFlowNotifyTypeList()
.stream().filter(StrUtil::isNotBlank).collect(Collectors.toSet());
notifyTypesSet.addAll(flowTaskNotifyTypes);
taskExtData.setFlowNotifyTypeList(new LinkedList<>(notifyTypesSet));
}
flowTaskExt.setExtraDataJson(JSON.toJSONString(taskExtData));
}
}
}
private void processFlowTaskRevive(FlowEntryExtensionData flowEntryExtensionData, BpmnModel bpmnModel) {
if (BooleanUtil.isTrue(flowEntryExtensionData.getSupportRevive())) {
List<UserTask> mainProcessUserTasks = bpmnModel.getMainProcess().getFlowElements()
.stream().filter(UserTask.class::isInstance).map(t -> (UserTask) t).collect(Collectors.toList());
mainProcessUserTasks.forEach(t -> flowApiService.addTaskCreateListener(t, FlowTaskReviveListener.class));
}
}
private void processFlowTaskExtList(List<FlowTaskExt> flowTaskExtList, BpmnModel bpmnModel) {
List<FlowElement> elementList = new LinkedList<>();
this.calculateAllElementList(bpmnModel.getMainProcess().getFlowElements(), elementList);
this.doAddLatestApprovalStatusListener(elementList);
Map<String, FlowElement> elementMap = elementList.stream()
.filter(UserTask.class::isInstance).collect(Collectors.toMap(FlowElement::getId, c -> c));
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
for (FlowTaskExt t : flowTaskExtList) {
UserTask userTask = (UserTask) elementMap.get(t.getTaskId());
flowApiService.addTaskCreateListener(userTask, FlowUserTaskListener.class);
// create 创建
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "create");
// assignment 分配/指派
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "assignment");
// complete 完成
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "complete");
// delete 删除
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "delete");
Map<String, List<ExtensionAttribute>> attributes = userTask.getAttributes();
if (CollUtil.isNotEmpty(attributes.get(FlowConstant.USER_TASK_AUTO_SKIP_KEY))) {
flowApiService.addTaskCreateListener(userTask, AutoSkipTaskListener.class);
}
// 如果流程图中包含部门领导审批和上级部门领导审批的选项,就需要注册 FlowCustomExtFactory 工厂中的
// BaseFlowIdentityExtHelper 对象,该注册操作需要业务模块中实现。
if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER)) {
userTask.setCandidateGroups(
CollUtil.newArrayList("${" + FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR + "}"));
Assert.notNull(flowIdentityExtHelper);
flowApiService.addTaskCreateListener(userTask, flowIdentityExtHelper.getUpDeptPostLeaderListener());
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_DEPT_POST_LEADER)) {
userTask.setCandidateGroups(
CollUtil.newArrayList("${" + FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR + "}"));
Assert.notNull(flowIdentityExtHelper);
flowApiService.addTaskCreateListener(userTask, flowIdentityExtHelper.getDeptPostLeaderListener());
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_POST)) {
Assert.notNull(t.getDeptPostListJson());
List<FlowTaskPostCandidateGroup> groupDataList =
JSON.parseArray(t.getDeptPostListJson(), FlowTaskPostCandidateGroup.class);
List<String> candidateGroupList =
FlowTaskPostCandidateGroup.buildCandidateGroupList(groupDataList);
userTask.setCandidateGroups(candidateGroupList);
}
this.processFlowTaskExtListener(userTask, t);
}
}
private void insertEntryPublishVariables(List<FlowEntryVariable> entryVariableList, Long entryPublishId) {
if (CollUtil.isEmpty(entryVariableList)) {
return;
}
List<FlowEntryPublishVariable> entryPublishVariableList =
MyModelUtil.copyCollectionTo(entryVariableList, FlowEntryPublishVariable.class);
for (FlowEntryPublishVariable variable : entryPublishVariableList) {
variable.setVariableId(idGenerator.nextLongId());
variable.setEntryPublishId(entryPublishId);
}
flowEntryPublishVariableMapper.insertList(entryPublishVariableList);
}
private void doAddLatestApprovalStatusListener(Collection<FlowElement> elementList) {
List<FlowElement> sequenceFlowList =
elementList.stream().filter(SequenceFlow.class::isInstance).collect(Collectors.toList());
for (FlowElement sequenceFlow : sequenceFlowList) {
FlowElementExtProperty extProperty = flowTaskExtService.buildFlowElementExt(sequenceFlow);
if (extProperty != null && extProperty.getLatestApprovalStatus() != null) {
List<FieldExtension> fieldExtensions = new LinkedList<>();
FieldExtension fieldExtension = new FieldExtension();
fieldExtension.setFieldName(FlowConstant.LATEST_APPROVAL_STATUS_KEY);
fieldExtension.setStringValue(extProperty.getLatestApprovalStatus().toString());
fieldExtensions.add(fieldExtension);
flowApiService.addExecutionListener(
sequenceFlow, UpdateLatestApprovalStatusListener.class, "start", fieldExtensions);
}
}
List<SubProcess> subProcesseList = elementList.stream()
.filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList());
for (SubProcess subProcess : subProcesseList) {
this.doAddLatestApprovalStatusListener(subProcess.getFlowElements());
}
}
private void processFlowTaskExtListener(UserTask userTask, FlowTaskExt taskExt) {
if (StrUtil.isBlank(taskExt.getExtraDataJson())) {
return;
}
FlowUserTaskExtData userTaskExtData =
JSON.parseObject(taskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isNotEmpty(userTaskExtData.getFlowNotifyTypeList())) {
flowApiService.addTaskCreateListener(userTask, FlowTaskNotifyListener.class);
}
if (userTaskExtData.getRejectType().equals(FlowUserTaskExtData.REJECT_TYPE_BACK_TO_SOURCE)) {
flowApiService.addTaskCreateListener(userTask, FlowTaskRejectBackListener.class);
}
if (StrUtil.isNotBlank(userTaskExtData.getTimeoutHandleWay())) {
flowApiService.addTaskCreateListener(userTask, FlowTaskTimeoutListener.class);
}
if (StrUtil.isNotBlank(userTaskExtData.getEmptyUserHandleWay())) {
flowApiService.addTaskCreateListener(userTask, FlowEmptyUserHandleListener.class);
}
}
}

102
common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java

@ -1,102 +0,0 @@ @@ -1,102 +0,0 @@
package apelet.common.flow.online.service.impl;
import apelet.common.core.annotation.MyDataSource;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.flow.base.service.BaseFlowOnlineService;
import apelet.common.flow.model.FlowWorkOrder;
import apelet.common.flow.online.object.TransactionalFlowBusinessData;
import apelet.common.flow.util.FlowCustomExtFactory;
import apelet.common.online.exception.OnlineRuntimeException;
import apelet.common.online.model.*;
import apelet.common.online.model.constant.FieldKind;
import apelet.common.online.service.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* 在线表单和流程监听器进行数据对接时的服务实现类
*
* @author guifc
* @date 2023-08-04
*/
@Slf4j
@MyDataSource(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowOnlineBusinessService")
public class FlowOnlineBusinessServiceImpl implements BaseFlowOnlineService {
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private OnlineTableService onlineTableService;
@Autowired
private OnlineDatasourceService onlineDatasourceService;
@Autowired
private OnlineDatasourceRelationService onlineDatasourceRelationService;
@Autowired
private OnlineOperationService onlineOperationService;
@PostConstruct
public void doRegister() {
flowCustomExtFactory.getOnlineBusinessDataExtHelper().setOnlineBusinessService(this);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateFlowStatus(FlowWorkOrder workOrder) {
OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(workOrder.getOnlineTableId());
if (onlineTable == null) {
log.error("OnlineTableId [{}] doesn't exist while calling FlowOnlineBusinessServiceImpl.updateFlowStatus",
workOrder.getOnlineTableId());
return;
}
String dataId = workOrder.getBusinessKey();
this.handleTransactionalFlowBusinessData(workOrder, "FlowOnlineBusinessServiceImpl.updateFlowStatus");
for (OnlineColumn column : onlineTable.getColumnMap().values()) {
if (ObjectUtil.equals(column.getFieldKind(), FieldKind.FLOW_FINISHED_STATUS)) {
onlineOperationService.updateColumn(onlineTable, dataId, column, workOrder.getFlowStatus());
}
if (ObjectUtil.equals(column.getFieldKind(), FieldKind.FLOW_APPROVAL_STATUS)) {
onlineOperationService.updateColumn(onlineTable, dataId, column, workOrder.getLatestApprovalStatus());
}
}
}
@Override
public void deleteBusinessData(FlowWorkOrder workOrder) {
OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(workOrder.getOnlineTableId());
if (onlineTable == null) {
log.error("OnlineTableId [{}] doesn't exist while calling FlowOnlineBusinessServiceImpl.deleteBusinessData",
workOrder.getOnlineTableId());
return;
}
OnlineDatasource datasource =
onlineDatasourceService.getOnlineDatasourceByMasterTableId(onlineTable.getTableId());
List<OnlineDatasourceRelation> relationList =
onlineDatasourceRelationService.getOnlineDatasourceRelationListFromCache(CollUtil.newHashSet(datasource.getDatasourceId()));
String dataId = workOrder.getBusinessKey();
for (OnlineDatasourceRelation relation : relationList) {
OnlineTable slaveTable = onlineTableService.getOnlineTableFromCache(relation.getSlaveTableId());
if (slaveTable == null) {
throw new OnlineRuntimeException("数据验证失败,数据源关联 [" + relation.getRelationName() + "] 的从表Id不存在!");
}
relation.setSlaveTable(slaveTable);
}
this.handleTransactionalFlowBusinessData(workOrder, "FlowOnlineBusinessServiceImpl.deleteBusinessData");
onlineOperationService.delete(onlineTable, relationList, dataId);
}
private void handleTransactionalFlowBusinessData(FlowWorkOrder workOrder, String desc) {
TransactionalFlowBusinessData eventData = TransactionalFlowBusinessData.getFromRequestAttribute();
if (eventData != null) {
eventData.setProcessInstanceId(workOrder.getProcessInstanceId());
eventData.setOperationDesc(desc);
}
}
}

434
common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineOperationServiceImpl.java

@ -1,434 +0,0 @@ @@ -1,434 +0,0 @@
package apelet.common.flow.online.service.impl;
import apelet.common.core.annotation.MultiDatabaseWriteMethod;
import apelet.common.core.annotation.MyDataSource;
import apelet.common.core.config.CoreProperties;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.ObjectValue;
import apelet.common.dbutil.provider.DataSourceProvider;
import apelet.common.dbutil.provider.PostgreSqlProvider;
import apelet.common.flow.config.FlowProperties;
import apelet.common.flow.constant.FlowApprovalType;
import apelet.common.flow.constant.FlowConstant;
import apelet.common.flow.constant.FlowTaskStatus;
import apelet.common.flow.dao.FlowTransProducerMapper;
import apelet.common.flow.exception.FlowOperationException;
import apelet.common.flow.model.*;
import apelet.common.flow.online.object.TransactionalFlowBusinessData;
import apelet.common.flow.online.service.FlowOnlineOperationService;
import apelet.common.flow.service.FlowApiService;
import apelet.common.flow.service.FlowEntryService;
import apelet.common.flow.service.FlowWorkOrderService;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.online.config.OnlineProperties;
import apelet.common.online.exception.OnlineRuntimeException;
import apelet.common.online.model.*;
import apelet.common.online.object.TransactionalBusinessData;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineDataSourceUtil;
import apelet.common.orm.impl.Selector;
import apelet.common.orm.impl.SelectorItem;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@MyDataSource(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowOnlineOperationService")
public class FlowOnlineOperationServiceImpl implements FlowOnlineOperationService {
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowWorkOrderService flowWorkOrderService;
@Autowired
private FlowEntryService flowEntryService;
@Autowired
private OnlineOperationService onlineOperationService;
@Autowired
private OnlineDatasourceService onlineDatasourceService;
@Autowired
private OnlineProperties onlineProperties;
@Autowired
private FlowProperties flowProperties;
@Autowired
private CoreProperties coreProperties;
@Autowired
private OnlineDataSourceUtil dataSourceUtil;
@Autowired
private FlowTransProducerMapper flowTransProducerMapper;
@Autowired
private OnlineFormService onlineFormService;
@Autowired
private OnlineTableService onlineTableService;
@Autowired
private OrmGenDataSourceUtil ormGenDataSourceUtil;
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public ProcessInstance startWithBusinessKey(String processDefinitionId, String dataId) {
ProcessInstance instance = flowApiService.start(processDefinitionId, dataId);
flowWorkOrderService.saveNew(instance, dataId, null, null);
return instance;
}
@Override
public void startWithBusinessKeyExt(String processDefinitionId, String dataId, Long formId) {
OnlineForm OnlineForm = onlineFormService.getOnlineFormFromCache(formId);
OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(OnlineForm.getMasterTableId());
Selector selector = new Selector();
selector.getList().add(new SelectorItem("billstatus"));
ObjectValue objectValue = ormGenDataSourceUtil.queryOne(onlineTable.getTableName(),Long.valueOf(dataId),selector);
if(objectValue == null){
String errorMessage = "无效数据!";
throw new RuntimeException(errorMessage);
}
if(!objectValue.get("billstatus").equals("A")){
throw new RuntimeException("只有保存状态才能提交");
}
objectValue.put("billstatus","B");
try {
ormGenDataSourceUtil.update(onlineTable.getTableName(),objectValue,selector);
}catch (Exception e){
e.printStackTrace();
String errorMessage = "更新失败!错误信息:"+e.getMessage();
throw new RuntimeException(errorMessage);
}
if(processDefinitionId != null){
JSONObject taskVariableData = new JSONObject();
ProcessInstance instance = this.startWithBusinessKey(processDefinitionId,dataId);
FlowTaskComment flowTaskComment = new FlowTaskComment();
flowTaskComment.setApprovalType("agree");
flowTaskComment.setTaskComment("发起人");
flowApiService.takeFirstTask(instance.getProcessInstanceId(), flowTaskComment, taskVariableData);
}
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data) {
this.saveNewAndStartProcess(processDefinitionId, flowTaskComment, taskVariableData, table, data, null);
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
Object dataId = onlineOperationService.saveNewWithRelation(masterTable, masterData, slaveDataListMap);
Assert.notNull(dataId);
if (taskVariableData == null) {
taskVariableData = new JSONObject();
}
taskVariableData.put(FlowConstant.MASTER_DATA_KEY, masterData);
taskVariableData.put(FlowConstant.SLAVE_DATA_KEY, this.normailizeSlaveDataListMap(slaveDataListMap));
taskVariableData.put(FlowConstant.MASTER_TABLE_KEY, masterTable);
ProcessInstance instance = flowApiService.start(processDefinitionId, dataId);
flowWorkOrderService.saveNew(instance, dataId, masterTable.getTableId(), null);
flowApiService.takeFirstTask(instance.getProcessInstanceId(), flowTaskComment, taskVariableData);
// 这里需要在创建工单后再次更新一下工单状态,在flowApiService.completeTask中的更新,
// 因为当时没有创建工单对象,更新会不起任何作用,所以这里要补偿一下。
Integer approvalStatus = MapUtil.getInt(taskVariableData, FlowConstant.LATEST_APPROVAL_STATUS_KEY);
flowWorkOrderService.updateLatestApprovalStatusByProcessInstanceId(instance.getId(), approvalStatus);
}
@Transactional(rollbackFor = Exception.class)
@Override
public FlowWorkOrder saveNewDraftAndStartProcess(
String processDefinitionId, Long tableId, JSONObject masterData, JSONObject slaveData) {
ProcessInstance instance = flowApiService.start(processDefinitionId, null);
return flowWorkOrderService.saveNewWithDraft(
instance, tableId, null, JSON.toJSONString(masterData), JSON.toJSONString(slaveData));
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data) {
this.saveNewAndTakeTask(
processInstanceId, taskId, flowTaskComment, taskVariableData, table, data, null);
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
Object dataId = onlineOperationService.saveNewWithRelation(masterTable, masterData, slaveDataListMap);
Assert.notNull(dataId);
Task task = flowApiService.getProcessInstanceActiveTask(processInstanceId, taskId);
flowApiService.setBusinessKeyForProcessInstance(processInstanceId, dataId);
Map<String, Object> variables =
flowApiService.initAndGetProcessInstanceVariables(task.getProcessDefinitionId());
if (taskVariableData == null) {
taskVariableData = new JSONObject();
}
taskVariableData.putAll(variables);
taskVariableData.put(FlowConstant.MASTER_DATA_KEY, masterData);
taskVariableData.put(FlowConstant.SLAVE_DATA_KEY, this.normailizeSlaveDataListMap(slaveDataListMap));
taskVariableData.put(FlowConstant.MASTER_TABLE_KEY, masterTable);
flowApiService.completeTask(task, flowTaskComment, taskVariableData, null);
ProcessInstance instance = flowApiService.getProcessInstance(processInstanceId);
FlowWorkOrder flowWorkOrder =
flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(instance.getProcessInstanceId());
if (flowWorkOrder == null) {
flowWorkOrderService.saveNew(instance, dataId, masterTable.getTableId(), null);
} else {
flowWorkOrder.setBusinessKey(dataId.toString());
flowWorkOrder.setUpdateTime(new Date());
flowWorkOrder.setFlowStatus(FlowTaskStatus.SUBMITTED);
flowWorkOrderService.updateById(flowWorkOrder);
}
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void updateAndTakeTask(
Task task,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineDatasource datasource,
JSONObject masterData,
String masterDataId,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
int flowStatus = FlowTaskStatus.APPROVING;
if (flowTaskComment.getApprovalType().equals(FlowApprovalType.REFUSE)) {
flowStatus = FlowTaskStatus.REFUSED;
} else if (flowTaskComment.getApprovalType().equals(FlowApprovalType.STOP)) {
flowStatus = FlowTaskStatus.FINISHED;
}
OnlineTable masterTable = datasource.getMasterTable();
Long datasourceId = datasource.getDatasourceId();
flowWorkOrderService.updateFlowStatusByProcessInstanceId(task.getProcessInstanceId(), flowStatus);
this.updateMasterData(masterTable, masterData, masterDataId);
if (slaveDataListMap != null) {
for (Map.Entry<OnlineDatasourceRelation, List<JSONObject>> relationEntry : slaveDataListMap.entrySet()) {
Long relationId = relationEntry.getKey().getRelationId();
onlineOperationService.updateRelationData(
masterTable, masterData, masterDataId, datasourceId, relationId, relationEntry.getValue());
}
}
if (flowTaskComment.getApprovalType().equals(FlowApprovalType.STOP)) {
Integer s = MapUtil.getInt(taskVariableData, FlowConstant.LATEST_APPROVAL_STATUS_KEY);
flowWorkOrderService.updateLatestApprovalStatusByProcessInstanceId(task.getProcessInstanceId(), s);
CallResult stopResult = flowApiService.stopProcessInstance(
task.getProcessInstanceId(), flowTaskComment.getTaskComment(), flowStatus);
if (!stopResult.isSuccess()) {
throw new FlowOperationException(stopResult.getErrorMessage());
}
} else {
if (taskVariableData == null) {
taskVariableData = new JSONObject();
}
taskVariableData.put(FlowConstant.MASTER_DATA_KEY, masterData);
taskVariableData.put(FlowConstant.SLAVE_DATA_KEY, this.normailizeSlaveDataListMap(slaveDataListMap));
taskVariableData.put(FlowConstant.MASTER_TABLE_KEY, masterTable);
flowApiService.completeTask(task, flowTaskComment, taskVariableData, null);
}
}
@Override
public void fixBusinessData(FlowTransProducer flowTransProducer) {
try {
String sql = "SELECT * FROM zz_flow_trans_consumer WHERE trans_id = " + flowTransProducer.getTransId();
List<Map<String, Object>> dataList = dataSourceUtil.query(flowTransProducer.getDblinkId(), sql);
if (CollUtil.isNotEmpty(dataList)) {
return;
}
} catch (Exception e) {
log.error("Failed to call dataSourceUtil.query", e);
throw new OnlineRuntimeException(e.getMessage());
}
TransactionalFlowBusinessData businessData = new TransactionalFlowBusinessData();
businessData.setTransId(flowTransProducer.getTransId());
businessData.setDblinkId(flowTransProducer.getDblinkId());
List<TransactionalBusinessData.BusinessSqlData> sqlDataList =
JSON.parseArray(flowTransProducer.getSqlData(), TransactionalBusinessData.BusinessSqlData.class);
businessData.setSqlDataList(sqlDataList);
try {
this.doHandle(businessData);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new OnlineRuntimeException(e.getMessage());
}
}
@Override
public List<Map<String, Object>> calculatePermData(Set<Long> onlineFormEntryIds) {
if (CollUtil.isEmpty(onlineFormEntryIds)) {
return new LinkedList<>();
}
List<Map<String, Object>> permDataList = new LinkedList<>();
List<FlowEntry> flowEntries = flowEntryService.getInList(onlineFormEntryIds);
Set<Long> pageIds = flowEntries.stream().map(FlowEntry::getPageId).collect(Collectors.toSet());
Map<Long, String> pageAndVariableNameMap =
onlineDatasourceService.getPageIdAndVariableNameMapByPageIds(pageIds);
for (FlowEntry flowEntry : flowEntries) {
JSONObject permData = new JSONObject();
permData.put("entryId", flowEntry.getEntryId());
String key = StrUtil.upperFirst(flowEntry.getProcessDefinitionKey());
List<String> permCodeList = new LinkedList<>();
String formPermCode = "form" + key;
permCodeList.add(formPermCode);
permCodeList.add(formPermCode + ":fragment" + key);
permData.put("permCodeList", permCodeList);
String flowUrlPrefix = flowProperties.getUrlPrefix();
String onlineUrlPrefix = onlineProperties.getUrlPrefix();
List<String> permList = CollUtil.newLinkedList(
onlineUrlPrefix + "/onlineForm/view",
onlineUrlPrefix + "/onlineForm/render",
onlineUrlPrefix + "/onlineOperation/listByOneToManyRelationId/" + pageAndVariableNameMap.get(flowEntry.getPageId()),
flowUrlPrefix + "/flowOperation/viewInitialHistoricTaskInfo",
flowUrlPrefix + "/flowOperation/startOnly",
flowUrlPrefix + "/flowOperation/viewInitialTaskInfo",
flowUrlPrefix + "/flowOperation/viewRuntimeTaskInfo",
flowUrlPrefix + "/flowOperation/viewProcessBpmn",
flowUrlPrefix + "/flowOperation/viewHighlightFlowData",
flowUrlPrefix + "/flowOperation/listFlowTaskComment",
flowUrlPrefix + "/flowOperation/cancelWorkOrder",
flowUrlPrefix + "/flowOperation/listRuntimeTask",
flowUrlPrefix + "/flowOperation/listHistoricProcessInstance",
flowUrlPrefix + "/flowOperation/listHistoricTask",
flowUrlPrefix + "/flowOperation/freeJumpTo",
flowUrlPrefix + "/flowOnlineOperation/startPreview",
flowUrlPrefix + "/flowOnlineOperation/viewUserTask",
flowUrlPrefix + "/flowOnlineOperation/viewHistoricProcessInstance",
flowUrlPrefix + "/flowOnlineOperation/submitUserTask",
flowUrlPrefix + "/flowOnlineOperation/upload",
flowUrlPrefix + "/flowOnlineOperation/download",
flowUrlPrefix + "/flowOperation/submitConsign",
flowUrlPrefix + "/flowOnlineOperation/startAndTakeUserTask/" + flowEntry.getProcessDefinitionKey(),
flowUrlPrefix + "/flowOnlineOperation/startAndSaveDraft/" + flowEntry.getProcessDefinitionKey(),
flowUrlPrefix + "/flowOnlineOperation/listWorkOrder/" + flowEntry.getProcessDefinitionKey(),
flowUrlPrefix + "/flowOnlineOperation/printWorkOrder/" + flowEntry.getProcessDefinitionKey()
);
permData.put("permList", permList);
permDataList.add(permData);
}
return permDataList;
}
// @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
// public void bulkHandleBusinessData(TransactionalFlowBusinessData businessData) {
// try {
// this.doHandle(businessData);
// // 这里会切换回原有的在线表单表所在的数据库上线文。
// flowTransProducerMapper.deleteById(businessData.getTransId());
// } catch (Exception e) {
// log.error("Failed to commit online business data [** " + JSON.toJSONString(businessData) + " **]", e);
// FlowTransProducer transProducer = flowTransProducerMapper.selectById(businessData.getTransId());
// transProducer.setErrorReason(e.getMessage());
// flowTransProducerMapper.updateById(transProducer);
// businessData.setErrorReason(e.getMessage());
// throw new OnlineRuntimeException(e.getMessage());
// }
// }
private void doHandle(TransactionalFlowBusinessData businessData) throws Exception {
Connection conn = null;
try {
conn = dataSourceUtil.getConnection(businessData.getDblinkId());
conn.setAutoCommit(false);
for (TransactionalBusinessData.BusinessSqlData s : businessData.getSqlDataList()) {
List<Object> paramList = null;
if (CollUtil.isNotEmpty(s.getColumnValueList())) {
paramList = s.getColumnValueList().stream().map(Object.class::cast).collect(Collectors.toList());
}
dataSourceUtil.execute(conn, s.getSql(), paramList);
}
this.insertFlowConsumerTrans(conn, businessData);
conn.commit();
} catch (Exception e) {
if (conn != null) {
conn.rollback();
}
log.error(e.getMessage(), e);
throw new OnlineRuntimeException(e.getMessage());
} finally {
if (conn != null) {
conn.close();
}
}
}
private void insertFlowConsumerTrans(Connection conn, TransactionalFlowBusinessData businessData) {
DataSourceProvider provider = dataSourceUtil.getProvider(businessData.getDblinkId());
Long transId = businessData.getTransId();
String sql = "INSERT INTO zz_flow_trans_consumer VALUES(?,?)";
if (provider instanceof PostgreSqlProvider) {
dataSourceUtil.execute(conn, sql, CollUtil.newArrayList(transId, new java.sql.Date(System.currentTimeMillis())));
} else {
dataSourceUtil.execute(conn, sql, CollUtil.newArrayList(transId, new Date()));
}
}
private void updateMasterData(OnlineTable masterTable, JSONObject masterData, String dataId) {
if (masterData == null) {
return;
}
// 如果存在主表数据,就执行主表数据的更新。
Map<String, Object> originalMasterData =
onlineOperationService.getMasterData(masterTable, null, null, dataId);
for (Map.Entry<String, Object> entry : originalMasterData.entrySet()) {
masterData.putIfAbsent(entry.getKey(), entry.getValue());
}
if (!onlineOperationService.update(masterTable, masterData)) {
throw new FlowOperationException("主表数据不存在!");
}
}
private Map<String, List<JSONObject>> normailizeSlaveDataListMap(
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
if (slaveDataListMap == null || slaveDataListMap.size() == 0) {
return null;
}
Map<String, List<JSONObject>> resultMap = new HashMap<>(slaveDataListMap.size());
for (Map.Entry<OnlineDatasourceRelation, List<JSONObject>> entry : slaveDataListMap.entrySet()) {
resultMap.put(entry.getKey().getSlaveTable().getTableName(), entry.getValue());
}
return resultMap;
}
}

2
common/common-flow-online/src/main/resources/META-INF/spring.factories

@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
apelet.common.flow.online.config.FlowOnlineAutoConfig

5
common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java

@ -56,6 +56,11 @@ public class OnlCgreportItem extends BaseModel implements Serializable { @@ -56,6 +56,11 @@ public class OnlCgreportItem extends BaseModel implements Serializable {
@Schema(description = "是否显示 0否,1显示")
private Integer isShow;
@Schema(description = "是否下拉显示 0否,1显示")
private Integer isSelectShow;
@Schema(description = "排序")
private Integer orderNum;

11
common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java

@ -119,4 +119,15 @@ public interface IOnlFormHeadService extends IService<OnlFormHead> { @@ -119,4 +119,15 @@ public interface IOnlFormHeadService extends IService<OnlFormHead> {
* @return
*/
OnlFormHead getHeadTableNameCache(String tableName);
/**
* 创建元数据含默认字段并同步数据库. 返回创建后的 OnlFormHead.
*
* @param tableName 表名
* @param tableTxt 表描述
* @param metaType 表类型: "0"=附表(分录), "1"=主表(业务单据), "2"=单表(基础资料)
* @param sortFiled 关联字段附表时传主表表名
* @return OnlFormHead
*/
OnlFormHead createMetaAndSync(String tableName, String tableTxt, String metaType, String sortFiled);
}

39
common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java

@ -1660,4 +1660,43 @@ public class OnlFormHeadServiceImpl extends ServiceImpl<OnlFormHeadMapper, OnlFo @@ -1660,4 +1660,43 @@ public class OnlFormHeadServiceImpl extends ServiceImpl<OnlFormHeadMapper, OnlFo
public void saveEasDict(List<OnlineFromDict> list) {
onlFormHeadMapper.saveEasDict(list);
}
@Override
@Transactional(rollbackFor = Exception.class)
public OnlFormHead createMetaAndSync(String tableName, String tableTxt, String metaType, String sortFiled) {
OnlFormHead head = new OnlFormHead();
head.setId(idGenerator.nextLongId());
head.setTableName(tableName);
head.setTableTxt(tableTxt);
head.setTableType(metaType);
head.setStatus("0"); // 未同步状态
head.setLine(CustomUtil.MetaType_Entry.equals(metaType) ? 1 : 0);
if (StringUtils.isNotEmpty(sortFiled)) {
head.setSort(sortFiled);
head.setSortFiled("parent_id");
}
// 初始化审计字段(createUserId/updateUserId/createTime/updateTime/deletedFlag)
addOrUpdateBaseData(head, head.getId());
// Build DTO
OnlFormHeadDto dto = new OnlFormHeadDto();
BeanUtil.copyProperties(head, dto);
// Generate default fields
List<OnlFormField> defaultFields = new ArrayList<>();
defaultData(defaultFields, null, null, metaType);
dto.setOnlFormFieldList(defaultFields);
// Save
this.addAll(dto);
// Reload to get full info
OnlFormHead saved = this.getById(head.getId());
// Sync database (type=0: CREATE TABLE or ALTER TABLE ADD COLUMN)
this.syncDB(saved, 0L);
return saved;
}
}

539
common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java

@ -3,6 +3,7 @@ package apelet.common.online.controller; @@ -3,6 +3,7 @@ package apelet.common.online.controller;
import apelet.common.core.annotation.MyRequestBody;
import apelet.common.core.annotation.NoAuthInterface;
import apelet.common.core.cache.CacheConfig;
import apelet.common.core.cache.CacheKey;
import apelet.common.core.constant.AppDeviceType;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.*;
@ -12,16 +13,21 @@ import apelet.common.core.util.MyPageUtil; @@ -12,16 +13,21 @@ import apelet.common.core.util.MyPageUtil;
import apelet.common.core.validator.UpdateGroup;
import apelet.common.dbutil.object.SqlTable;
import apelet.common.dbutil.object.SqlTableColumn;
import apelet.common.generator.model.OnlFormField;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormFieldService;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.generator.utils.CustomUtil;
import apelet.common.generator.utils.cache.MetaCacheService;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.config.OnlineProperties;
import apelet.common.online.dto.*;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.dto.OnlineFormDto;
import apelet.common.online.model.*;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineRedisKeyUtil;
import apelet.common.online.util.WidgetFieldTypeMapping;
import apelet.common.online.vo.OnlineFormVo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
@ -34,6 +40,7 @@ import com.alibaba.fastjson.JSONObject; @@ -34,6 +40,7 @@ import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.page.PageMethod;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.google.common.collect.Lists;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -47,8 +54,9 @@ import org.springframework.web.bind.annotation.*; @@ -47,8 +54,9 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.groups.Default;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@ -83,12 +91,17 @@ public class OnlineFormController { @@ -83,12 +91,17 @@ public class OnlineFormController {
@Autowired
IOnlFormHeadService onlFormHeadService;
@Autowired
IOnlFormFieldService onlFormFieldService;
@Autowired
OnlinePageService onlinePageService;
@Autowired
private OnlineProperties properties;
@Resource(name = "caffeineCacheManager")
private CacheManager cacheManager;
@Autowired
private MetaCacheService metaCacheService;
/**
* 新增在线表单数据
*
@ -326,6 +339,13 @@ public class OnlineFormController { @@ -326,6 +339,13 @@ public class OnlineFormController {
onlineForm.setWidgetJson(jsonObject.toJSONString());
}
// ★ 新增: 处理新增字段和子表
JSONObject updatedJson = syncNewFieldsAndSubTables(onlineForm, onlineFormDto, JSON.parseObject(onlineForm.getWidgetJson()));
if (updatedJson != null) {
onlineForm.setWidgetJson(updatedJson.toJSONString());
onlineFormDto.setWidgetJson(updatedJson.toJSONString());
}
if (!onlineFormService.update(onlineForm, originalOnlineForm, datasourceIdSet)) {
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineFormKey(onlineForm.getFormId())).delete();
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
@ -685,4 +705,519 @@ public class OnlineFormController { @@ -685,4 +705,519 @@ public class OnlineFormController {
}
return ResponseResult.success(onlineFormList);
}
// ============ 字段diff+同步 相关私有方法 ============
/**
* 对比 widgetJson 与现有 OnlFormField / OnlFormHead 识别新增字段和子表
* 创建 OnlFormField 记录同步数据库创建 OnlineColumn回填 columnId/relationId
*
* @return 修改后的 widgetJson (含回填的 columnId/relationId)无变更则返回 null
*/
private JSONObject syncNewFieldsAndSubTables(OnlineForm form, OnlineFormDto dto, JSONObject widgetJson) {
// 1. 获取数据源 → OnlineTable → OnlFormHead
OnlineDatasource datasource = onlineDatasourceService.getById(dto.getDatasourceIdList().get(0));
OnlineTable onlineTable = onlineTableService.getById(datasource.getMasterTableId());
OnlFormHead onlFormHead = onlFormHeadService.getOne(
new LambdaQueryWrapper<OnlFormHead>()
.eq(OnlFormHead::getTableName, onlineTable.getTableName()));
if (onlFormHead == null) {
return null;
}
boolean changed = false;
// === Diff 新增字段 ===
Set<String> widgetColumnNames = collectAllBindColumnNames(widgetJson);
List<OnlFormField> existingFields = onlFormFieldService.list(
new LambdaQueryWrapper<OnlFormField>().eq(OnlFormField::getHeadId, onlFormHead.getId()));
Set<String> existingFieldNames = existingFields.stream()
.map(OnlFormField::getFieldName).collect(Collectors.toSet());
Set<String> newColumnNames = new HashSet<>(widgetColumnNames);
newColumnNames.removeAll(existingFieldNames);
// 主表/单表已有字段:如果绑定了从表(slaveTableId),同步更新 refPropert
Map<String, JSONObject> fieldNameToWidget = mapFieldNameToWidget(widgetJson);
for (OnlFormField existingField : existingFields) {
JSONObject widget = fieldNameToWidget.get(existingField.getFieldName());
if (widget == null) {
continue;
}
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String slaveTableId = bindData.getString("slaveTableId");
if (StrUtil.isNotEmpty(slaveTableId)) {
existingField.setRefPropert(Long.valueOf(slaveTableId));
onlFormFieldService.updateById(existingField);
}
}
if (!newColumnNames.isEmpty()) {
Map<String, JSONObject> nameToWidget = mapColumnNameToWidget(widgetJson);
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
// 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库
Set<String> validColumnNames = new LinkedHashSet<>();
for (String fieldName : newColumnNames) {
JSONObject widget = nameToWidget.get(fieldName);
Integer widgetType = widget != null ? widget.getInteger("widgetType") : null;
if (widgetType == null || !WidgetFieldTypeMapping.isDataWidget(widgetType)) {
continue;
}
validColumnNames.add(fieldName);
}
if (validColumnNames.isEmpty()) {
return changed ? widgetJson : null;
}
for (String fieldName : validColumnNames) {
JSONObject widget = nameToWidget.get(fieldName);
// 从 widget 的 bindData.columnComment 设置字段备注
JSONObject bindData = widget.getJSONObject("bindData");
String ref = bindData.containsKey("slaveTableId") ? bindData.getString("slaveTableId") : null;
this.saveField(widget.getInteger("widgetType"), bindData.getString("columnComment"), widget.getString("showName"), onlFormHead, fieldName, ref);
}
// ALTER TABLE ADD COLUMN
onlFormHeadService.syncDB(onlFormHead, 0L);
// 批量获取所有列信息,避免 N+1 查询
List<SqlTableColumn> allColumns = onlineDblinkService.getDblinkTableColumnList(
dblink, onlineTable.getTableName());
Map<String, SqlTableColumn> columnMap = new HashMap<>();
if (CollUtil.isNotEmpty(allColumns)) {
for (SqlTableColumn col : allColumns) {
if (col.getColumnName() != null) {
columnMap.put(col.getColumnName().toLowerCase(), col);
}
}
}
// 增量创建 OnlineColumn + 回填 columnId 到 widgetJson
for (String fieldName : validColumnNames) {
SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase());
if (sqlCol != null) {
// 回填 columnId 到 widgetJson
JSONObject widget = nameToWidget.get(fieldName);
String showName = widget == null ? null : widget.getString("showName");
long columnId = onlineColumnService.saveBySqlTable(sqlCol, onlineTable.getTableId(), showName);
if (widget != null) {
JSONObject bindData2 = widget.getJSONObject("bindData");
if (bindData2 != null) {
bindData2.put("columnId", columnId);
bindData2.put("tableId", onlineTable.getTableId());
}
}
}
}
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(onlineTable.getTableId())).delete();
redissonClient.getBucket(CacheKey.makeTableKey(onlineTable.getTableName())).delete();
redissonClient.getBucket(CacheKey.makeChildFieldListKey(onlFormHead.getId())).delete();
metaCacheService.removeData(onlineTable.getTableName());
changed = true;
}
// === Diff 新增子表 ===
// 取 widgetList->bindData -> tableId 在去 zz_online_table 查询,这样就可以判断哪些是新增,哪些是 已有的表
// 如果存在附表
Map<String, JSONObject> tableIdAndPropMap = collectSubTableAndPropMap(widgetJson);
if (MapUtil.isNotEmpty(tableIdAndPropMap)) {
//搜集的所有tableId, 要么是 tableId(已存在),要么是 tableName(不存在)
Set<String> allTableIdSet = tableIdAndPropMap.keySet();
Set<String> newSubTableNames = new HashSet<>();
Set<Long> existingSubTableIdSet = new HashSet<>();
for (String tableId : allTableIdSet) {
if (tableId.matches("\\d+")) {
existingSubTableIdSet.add(Long.parseLong(tableId));
} else {
newSubTableNames.add(tableId);
}
}
//分两步,
//step1 : 看看存在的附表,是否存在不存在的字段
if (!existingSubTableIdSet.isEmpty()) {
// 拿到tableId 下面的所有字段,先查询 zz_online_table ,在根据 tableName 查询 onl_form_head 表
List<OnlineTable> tableList = onlineTableService.listByIds(existingSubTableIdSet);
Map<Long, String> tableIdAndNameMap = tableList.stream().collect(Collectors.toMap(OnlineTable::getTableId, OnlineTable::getTableName));
List<String> tableNames = tableList.stream().map(OnlineTable::getTableName).collect(Collectors.toList());
List<OnlFormHead> onlFormHeadList = onlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().in(OnlFormHead::getTableName, tableNames));
Map<String, OnlFormHead> tableNameAndHeadIdMap = onlFormHeadList.stream().collect(Collectors.toMap(OnlFormHead::getTableName, Function.identity()));
//因为这里是zz_online_column.column_id 的id和 列名的混合,我只要列名
Map<String, JSONObject> nameToWidget = mapColumnIdAndWidget(widgetJson);
Set<String> newSubColumnNameSet = nameToWidget.keySet().stream().filter(s -> !s.matches("\\d+")).collect(Collectors.toSet());
//这里要走 新增逻辑
if (!newSubColumnNameSet.isEmpty()) {
// ALTER TABLE ADD COLUMN, 这里取第一个就行,因为绑定的是一个表
JSONObject columnInfo = nameToWidget.values().iterator().next();
Long tableId = columnInfo.getLong("tableId");
String tableName = tableIdAndNameMap.get(tableId);
OnlFormHead entryHead = tableNameAndHeadIdMap.get(tableName);
for (String newSubColumnName : newSubColumnNameSet) {
//这里取出来的结构是 widgetList -》 props -》 tableColumnList
JSONObject columnInfoJson = nameToWidget.get(newSubColumnName);
//step 1 : 先保存 field 表
String showName = columnInfoJson.getString("showName");
String ref = columnInfoJson.containsKey("slaveTableId") ? columnInfoJson.getString("slaveTableId") : null;
// 默认是字符串类型
this.saveField(750, showName, null, entryHead, newSubColumnName, ref);
}
onlFormHeadService.syncDB(entryHead, 0L);
//step 2 :在保存 OnlineColumn 表
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
List<SqlTableColumn> allColumns = onlineDblinkService.getDblinkTableColumnList(dblink, tableName);
Map<String, SqlTableColumn> columnMap = new HashMap<>();
if (CollUtil.isNotEmpty(allColumns)) {
columnMap = allColumns.stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, Function.identity()));
}
for (String fieldName : newSubColumnNameSet) {
SqlTableColumn sqlCol = columnMap.get(fieldName);
if (sqlCol != null) {
// 回填 columnId 到 widgetJson
JSONObject widget = nameToWidget.get(fieldName);
String showName = widget == null ? null : widget.getString("showName");
long columnId = onlineColumnService.saveBySqlTable(sqlCol, tableId, showName);
if (widget != null) {
//step 3 :回填 columnId + showFieldName 到 json里面去
widget.put("columnId", columnId);
widget.put("showFieldName", fieldName);
}
}
}
//step 4 :删除缓存
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(tableId)).delete();
redissonClient.getBucket(CacheKey.makeTableKey(tableName)).delete();
redissonClient.getBucket(CacheKey.makeChildFieldListKey(entryHead.getId())).delete();
metaCacheService.removeData(tableName);
changed = true;
}
// 附表已有字段:检查 tableColumnList 中 comType=402 的列是否有 slaveTableId,同步更新 refPropert
for (Long tableId : existingSubTableIdSet) {
String tableName = tableIdAndNameMap.get(tableId);
if (tableName == null) {
continue;
}
OnlFormHead subHead = tableNameAndHeadIdMap.get(tableName);
if (subHead == null) {
continue;
}
List<OnlFormField> subFields = onlFormFieldService.list(
new LambdaQueryWrapper<OnlFormField>().eq(OnlFormField::getHeadId, subHead.getId()));
JSONObject props = tableIdAndPropMap.get(String.valueOf(tableId));
JSONArray tableColumnList = props != null ? props.getJSONArray("tableColumnList") : null;
if (tableColumnList == null) {
continue;
}
for (OnlFormField field : subFields) {
for (int i = 0; i < tableColumnList.size(); i++) {
JSONObject col = tableColumnList.getJSONObject(i);
// 匹配 showFieldName,兼容 showName
String showFieldName = col.getString("showFieldName");
if (StrUtil.isEmpty(showFieldName)) {
showFieldName = col.getString("showName");
}
if (!field.getFieldName().equals(showFieldName)) {
continue;
}
// 只有 comType=402(关联选择器)才有 slaveTableId
Integer comType = col.getInteger("comType");
if (comType != null && comType == 402) {
String slaveTableId = col.getString("slaveTableId");
if (StrUtil.isNotEmpty(slaveTableId)) {
field.setRefPropert(Long.valueOf(slaveTableId));
onlFormFieldService.updateById(field);
changed = true;
}
}
break;
}
}
}
}
//step2 : 不存在的附表,新增
if (newSubTableNames.isEmpty()) {
//如果他为空,说明我们走tableId取值没取到,我们就去一下 tableName;
newSubTableNames = this.collectSubTableName(widgetJson);
}
if (!newSubTableNames.isEmpty()) {
for (String subTableName : newSubTableNames) {
OnlFormHead subHead = onlFormHeadService.createMetaAndSync(subTableName, onlFormHead.getTableTxt() + "附表", CustomUtil.MetaType_Entry, onlFormHead.getTableName());
// 获取子表的 OnlineTable
OnlineTable subOnlineTable = onlineTableService.getOne(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, subTableName.toLowerCase()));
if (subOnlineTable == null) {
// 从DB获取表结构并创建 OnlineTable
SqlTable subSqlTable = onlineDblinkService.getDblinkTable(onlineDblinkService.getById(datasource.getDblinkId()), subTableName);
if (subSqlTable != null) {
subOnlineTable = onlineTableService.saveNewFromSqlTable(subSqlTable);
}
}
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, datasource, null, dblink);
if (subOnlineTable != null) {
backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId());
}
}
//这里要将 所有的附表名称 加到 apelet.common.generator.model.OnlFormHead.childName 后面
String newSubTableName = String.join(",", newSubTableNames);
if (StringUtils.isNotBlank(onlFormHead.getChildName())) {
onlFormHead.setChildName(onlFormHead.getChildName() + "," + newSubTableName);
} else {
onlFormHead.setChildName(newSubTableName);
}
onlFormHeadService.updateById(onlFormHead);
changed = true;
}
}
return changed ? widgetJson : null;
}
private void saveField(Integer widgetType, String columnComment, String showName, OnlFormHead onlFormHead, String fieldName, String refPropert) {
OnlFormField field = new OnlFormField();
field.setHeadId(onlFormHead.getId());
field.setFieldName(fieldName);
field.setFieldType(WidgetFieldTypeMapping.getFieldTypeId(widgetType));
Integer length = WidgetFieldTypeMapping.getDefaultLength(widgetType);
if (length != null) {
field.setFieldLength(length);
}
if (StringUtils.isNotBlank(refPropert)) {
field.setRefPropert(Long.valueOf(refPropert));
}
field.setIsEmpty(1);
field.setIsDefault(0);
field.setRowDisabled(0);
field.setSyncFlag(0);
// 从 widget 的 bindData.columnComment 设置字段备注
field.setFieldRemark(columnComment);
if (StringUtils.isNotBlank(showName) && (StringUtils.isBlank(field.getFieldRemark()) || StringUtils.equals("null", field.getFieldRemark()))) {
field.setFieldRemark(showName);
}
onlFormFieldService.save(field);
}
/**
* 遍历 pc/mobile widget 收集所有 bindData.columnName
*/
private Set<String> collectAllBindColumnNames(JSONObject widgetJson) {
Set<String> names = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
names.add(name);
}
}
});
return names;
}
/**
* 遍历 pc/mobile widget 收集 widgetType=100 (TableContainer) bindData 下的 tableId
*/
private Map<String, JSONObject> collectSubTableAndPropMap(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props != null) {
resultMap.put(tableId, props);
}
}
}
});
return resultMap;
}
private Set<String> collectSubTableName(JSONObject widgetJson) {
Set<String> tableNameSet = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
if (bindData.containsKey("tableName")) {
tableNameSet.add(bindData.getString("tableName"));
}
}
}
});
return tableNameSet;
}
/**
* 构建 columnName widget 的映射
*/
private Map<String, JSONObject> mapColumnNameToWidget(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, widget);
}
}
});
return map;
}
/**
* 构建 fieldName widget 的映射用于主表已有字段匹配
* key 优先取 bindData.columnFieldName兼容 bindData.columnName
*/
private Map<String, JSONObject> mapFieldNameToWidget(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData == null) {
return;
}
String fieldName = bindData.getString("columnFieldName");
if (StrUtil.isEmpty(fieldName)) {
fieldName = bindData.getString("columnName");
}
if (StrUtil.isNotEmpty(fieldName)) {
map.put(fieldName, widget);
}
});
return map;
}
private Map<String, JSONObject> mapColumnIdAndWidget(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props == null) {
continue;
}
JSONArray tableColumnList = props.getJSONArray("tableColumnList");
for (int j = 0; j < tableColumnList.size(); j++) {
JSONObject tableColumn = tableColumnList.getJSONObject(j);
// 过滤掉 fieldType=1 的列(如序号、合计等非真实字段),避免被识别为需要新增的字段
Integer fieldType = tableColumn.getInteger("fieldType");
if (fieldType != null && fieldType == 1) {
continue;
}
resultMap.put(tableColumn.getString("columnId"), tableColumn);
}
}
}
});
return resultMap;
}
/**
* widgetJson 中查找匹配 tableName TableContainer 控件回填 tableId
*/
private void backfillRelationId(JSONObject widgetJson, String tableName, Long tableId) {
walkAllModes(widgetJson, widget -> {
if (widget.getInteger("widgetType") != null && 100 == widget.getInteger("widgetType")) {
JSONObject props = widget.getJSONObject("bindData");
if (props != null && tableName.equals(props.getString("tableName"))) {
props.put("tableId", tableId);
}
}
});
}
/**
* 遍历 "pc" "mobile" 两个模式的控件树
*/
private void walkAllModes(JSONObject widgetJson, Consumer<JSONObject> visitor) {
for (String mode : new String[]{"pc", "mobile"}) {
JSONObject modeObj = widgetJson.getJSONObject(mode);
if (modeObj != null) {
walkWidgets(modeObj, visitor);
}
}
}
/**
* 递归遍历控件树访问每个控件及其子控件(widgetList)
*/
private void walkWidgets(JSONObject node, Consumer<JSONObject> visitor) {
if (node == null) {
return;
}
visitor.accept(node);
JSONArray children = node.getJSONArray("widgetList");
if (children != null) {
for (int i = 0; i < children.size(); i++) {
walkWidgets(children.getJSONObject(i), visitor);
}
}
}
/**
* 根据表单类型查询表单列表
*
* @param formType 表单类型
* @return 表单列表
* @see apelet.common.online.model.constant.FormType
*/
@PostMapping(value = "/queryFormByType")
public ResponseResult<List<OnlineForm>> queryFormByType(@MyRequestBody Integer formType) {
if (formType == null) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
return ResponseResult.success(onlineFormService.queryListByFormType(Lists.newArrayList(formType)));
}
}

91
common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java

@ -16,7 +16,10 @@ import apelet.common.generator.service.IOnlFormHeadService; @@ -16,7 +16,10 @@ import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.generator.utils.CustomUtil;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.dto.*;
import apelet.common.online.dto.OnlineDatasourceDto;
import apelet.common.online.dto.OnlineDatasourceRelationDto;
import apelet.common.online.dto.OnlinePageDatasourceDto;
import apelet.common.online.dto.OnlinePageDto;
import apelet.common.online.model.*;
import apelet.common.online.model.constant.PageStatus;
import apelet.common.online.service.*;
@ -36,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; @@ -36,6 +39,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.validation.groups.Default;
@ -74,6 +78,10 @@ public class OnlinePageController { @@ -74,6 +78,10 @@ public class OnlinePageController {
@Autowired
private IOnlFormFieldService onlFormFieldService;
//new=新建元数据,exist=现有元数据
private static final String META_SOURCE_NEW = "new";
private static final String META_SOURCE_EXIST = "exist";
/**
* 新增在线表单页面数据
*
@ -82,41 +90,82 @@ public class OnlinePageController { @@ -82,41 +90,82 @@ public class OnlinePageController {
*/
@ApiOperationSupport(ignoreParameters = {"onlinePageDto.pageId"})
@OperationLog(type = SysOperationLogType.ADD)
@Transactional(rollbackFor = Exception.class)
@PostMapping("/add")
public ResponseResult<Long> add(@MyRequestBody OnlinePageDto onlinePageDto) {
public ResponseResult<OnlinePage> add(@MyRequestBody OnlinePageDto onlinePageDto) {
String errorMessage = MyCommonUtil.getModelValidationError(onlinePageDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// Validate based on metaSource
String metaSource = onlinePageDto.getMetaSource();
if (META_SOURCE_NEW.equals(metaSource)) {
// New metadata flow: metaType and pageCode are required
if (StrUtil.isEmpty(onlinePageDto.getMetaType())) {
errorMessage = "数据验证失败,新建元数据时表类型(metaType)不能为空!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (StrUtil.isEmpty(onlinePageDto.getPageCode())) {
errorMessage = "数据验证失败,新建元数据时页面编码不能为空!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
} else {
// Existing metadata flow: masterTableId is required
if (StrUtil.isEmpty(onlinePageDto.getMasterTableId())) {
errorMessage = "数据验证失败,表单元数据不能为空!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
}
OnlinePage onlinePage = MyModelUtil.copyTo(onlinePageDto, OnlinePage.class);
if (onlinePageService.existByPageCode(onlinePage.getPageCode())) {
errorMessage = "数据验证失败,页面编码已经存在!";
return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, errorMessage);
}
OnlFormHead onlFormHead;
if (META_SOURCE_NEW.equals(metaSource)) {
// === New branch: create fresh metadata ===
String metaType = onlinePageDto.getMetaType();
String tableName = "t_" + onlinePageDto.getPageCode().toLowerCase();
String tableTxt = onlinePageDto.getPageName();
onlFormHead = iOnlFormHeadService.createMetaAndSync(tableName, tableTxt, metaType, null);
Long masterTableId = onlFormHead.getId();
onlinePage.setMasterTableId(String.valueOf(masterTableId));
onlinePage.setMasterTableName(onlFormHead.getTableName());
// For business documents (metaType="1"): auto-create sub-table
if (CustomUtil.MetaType_Main.equals(metaType)) {
String entryTableName = tableName + "_entry";
iOnlFormHeadService.createMetaAndSync(entryTableName, tableTxt + "附表", CustomUtil.MetaType_Entry, tableName);
}
} else {
// === Existing branch: use existing metadata ===
String formMetadata = onlinePageDto.getMasterTableId();
onlFormHead = iOnlFormHeadService.getById(formMetadata);
}
try {
onlinePage = onlinePageService.saveNew(onlinePage);
} catch (DuplicateKeyException e) {
errorMessage = "数据验证失败,当前应用的页面编码已经存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
//根据所传表单元数据id查出对应表单信息
String formMetadata = onlinePageDto.getMasterTableId();
//在线表单主表对象
OnlFormHead onlFormHead = iOnlFormHeadService.getById(formMetadata);
//准备创建数据模型所需的数据
// 准备创建数据模型所需的数据
OnlineDatasourceDto onlineDatasourceDto = new OnlineDatasourceDto();
//数据源标识-取表名驼峰
// 数据源标识-取表名驼峰
onlineDatasourceDto.setVariableName(CustomUtil.UnderlineToCamel(onlFormHead.getTableName()));
//数据主表标识
// 数据主表标识
onlineDatasourceDto.setMasterTableName(onlFormHead.getTableName());
//数据源名称-取表名
// 数据源名称-取表名
onlineDatasourceDto.setDatasourceName((onlFormHead.getTableName()));
//目前系统只有一个数据源,所以默认1740180645717610505
// 目前系统只有一个数据源,所以默认1740180645717610505
OnlineDblink onlineDblink = onlineDblinkService.getById(1740180645717610505L);
onlineDatasourceDto.setDblinkId(1740180645717610505L);
//创建数据模型
// 创建数据模型
SqlTable sqlTable = onlineDblinkService.getDblinkTable(onlineDblink, onlineDatasourceDto.getMasterTableName());
if (sqlTable == null) {
errorMessage = "数据验证失败,指定的数据表名不存在!";
@ -130,22 +179,20 @@ public class OnlinePageController { @@ -130,22 +179,20 @@ public class OnlinePageController {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
/**
*创建数据表关联关系 先查出改元数据表的所有附表关系
* 创建数据表关联关系 先查出改元数据表的所有附表关系
* 一对多
*
*/
addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink);
return ResponseResult.success(onlinePage.getPageId());
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink);
return ResponseResult.success(onlinePage);
}
/**
*创建数据表关联关系 先查出改元数据表的所有附表关系
*
* 方法迁移 onlineDatasourceRelationService.addOnlineDatasourceRelation
*/
@Deprecated
public ResponseResult<Long> addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink) {
//历史数据 --做比较新数据写入,已存在的不用管,不存在的删除
OnlineDatasourceRelation onlineDatasourceRelations = new OnlineDatasourceRelation();
@ -399,7 +446,7 @@ public class OnlinePageController { @@ -399,7 +446,7 @@ public class OnlinePageController {
onlineColumnService.saveNewList(sqlTableColumnList, onlineDatasourceList.get(0).getMasterTableId());
//写表关联关系 --一对多,一对一
onlineDatasourceService.getOnlineDatasourceListByPageId(onlinePageDto.getPageId(), null, null);
addOnlineDatasourceRelation(onlFormHeadList.get(0), onlineDatasourceList.get(0), errorMessage, onlineDblink);
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHeadList.get(0), onlineDatasourceList.get(0), errorMessage, onlineDblink);
ArrayList<OnlineColumn> onlineColumns = new ArrayList<>();
List<OnlFormField> filedByHeadIdCache = onlFormFieldService.getFiledByHeadIdCache(onlFormHeadList.get(0).getId());

14
common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java

@ -60,13 +60,23 @@ public class OnlinePageDto { @@ -60,13 +60,23 @@ public class OnlinePageDto {
* 表单元数据
*/
@Schema(description = "表单元数据")
@NotNull(message = "数据验证失败,表单元数据不能为空!")
private String masterTableId;
/**
* 表单元数据
*/
@Schema(description = "表单元数据表名")
// @NotNull(message = "数据验证失败,表单元数据不能为空!")
private String masterTableName;
/**
* 数据来源: "new"=新建元数据, "existing"=使用已有元数据
*/
@Schema(description = "数据来源: new=新建元数据, existing=使用已有元数据")
private String metaSource;
/**
* 表类型: "2"=基础资料(单表), "1"=业务单据(主表+附表)仅在metaSource="new"时有效
*/
@Schema(description = "表类型: 2=基础资料(单表), 1=业务单据(主表+附表)。仅在metaSource=new时有效")
private String metaType;
}

10
common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java

@ -27,6 +27,16 @@ public interface OnlineColumnService extends IBaseService<OnlineColumn, Long> { @@ -27,6 +27,16 @@ public interface OnlineColumnService extends IBaseService<OnlineColumn, Long> {
List<OnlineColumn> saveNewList(List<SqlTableColumn> columnList, Long onlineTableId);
/**
* 保存新增数据表字段
*
* @param sqlTableColumn 新增数据表字段对象
* @param tableId 在线表对象的主键Id
* @param columnComment 字段名
* @return 插入的在线表字段数据
*/
long saveBySqlTable(SqlTableColumn sqlTableColumn, Long tableId, String columnComment);
/**
* 更新数据对象
*
* @param onlineColumn 更新的对象

17
common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java

@ -1,9 +1,13 @@ @@ -1,9 +1,13 @@
package apelet.common.online.service;
import apelet.common.core.base.service.IBaseService;
import apelet.common.core.object.ResponseResult;
import apelet.common.dbutil.object.SqlTable;
import apelet.common.dbutil.object.SqlTableColumn;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.model.OnlineDatasourceRelation;
import apelet.common.online.model.OnlineDblink;
import java.util.List;
import java.util.Set;
@ -82,4 +86,17 @@ public interface OnlineDatasourceRelationService extends IBaseService<OnlineData @@ -82,4 +86,17 @@ public interface OnlineDatasourceRelationService extends IBaseService<OnlineData
*/
List<OnlineDatasourceRelation> getOnlineDatasourceRelationListWithRelation(
OnlineDatasourceRelation filter, String orderBy);
/***
* 功能: 迁移 OnlinePageController#addOnlineDatasourceRelation(OnlFormHead, OnlineDatasource, String, OnlineDblink)
* 方法创建数据表关联关系 先查出改元数据表的所有附表关系
*
* @author ${chenchuchuan}
* @param onlFormHead 主表信息
* @param onlineDatasource 数据库链接配置
* @param errorMessage 错误描述
* @param onlineDblink 数据库实际链接
* @return ResponseResult<Long>
*/
ResponseResult<Long> addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink);
}

8
common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java

@ -139,4 +139,12 @@ public interface OnlineFormService extends IBaseService<OnlineForm, Long> { @@ -139,4 +139,12 @@ public interface OnlineFormService extends IBaseService<OnlineForm, Long> {
* @return
*/
ObjectValue getObjectValue(Map<String, Object> masterData, Map<String, Object> savedata, String tableName);
/**
* 根据表单类型查询表单列表
*
* @param formType 表单类型
* @return 表单列表
*/
List<OnlineForm> queryListByFormType(List<Integer> formType);
}

7
common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java

@ -135,4 +135,11 @@ public interface OnlinePageService extends IBaseService<OnlinePage, Long> { @@ -135,4 +135,11 @@ public interface OnlinePageService extends IBaseService<OnlinePage, Long> {
* @return 在线表单页面列表
*/
List<OnlinePage> getInListWithNonTenant(List<Long> pageIds, String orderBy);
/**
* 查询启用的在线表单页面列表
*
* @return 在线表单页面列表
*/
List<OnlinePage> queryByEnableList();
}

29
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java

@ -31,13 +31,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -31,13 +31,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import com.google.common.base.CaseFormat;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* 字段数据数据操作服务类
@ -106,6 +110,29 @@ public class OnlineColumnServiceImpl extends BaseService<OnlineColumn, Long> imp @@ -106,6 +110,29 @@ public class OnlineColumnServiceImpl extends BaseService<OnlineColumn, Long> imp
return onlineColumnList;
}
@Override
public long saveBySqlTable(SqlTableColumn sqlTableColumn, Long tableId, String columnComment) {
OnlineColumn onlineColumn = new OnlineColumn();
BeanUtil.copyProperties(sqlTableColumn, onlineColumn, false);
onlineColumn.setColumnId(idGenerator.nextLongId());
onlineColumn.setTableId(tableId);
// 传了描述,并且不等于原先的描述,并且原先的为空或者为"null",就赋值
if (StringUtil.isNotBlank(columnComment) && !StringUtil.equals(columnComment, onlineColumn.getColumnComment())
&& (StringUtil.isBlank(onlineColumn.getColumnComment()) || StringUtil.equals("null", onlineColumn.getColumnComment()))) {
onlineColumn.setColumnComment(columnComment);
}
if (StringUtil.isBlank(onlineColumn.getColumnComment())) {
onlineColumn.setColumnComment(onlineColumn.getColumnName());
}
this.setDefault(sqlTableColumn, onlineColumn);
if (super.save(onlineColumn)) {
sqlTableColumn.setColumnName(onlineColumn.getColumnName().toLowerCase());
return onlineColumn.getColumnId();
}
return 0;
}
/**
* 更新数据对象
*

218
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java

@ -4,21 +4,31 @@ import apelet.common.core.annotation.MyDataSourceResolver; @@ -4,21 +4,31 @@ import apelet.common.core.annotation.MyDataSourceResolver;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.MyRelationParam;
import apelet.common.core.object.ResponseResult;
import apelet.common.core.object.TokenData;
import apelet.common.core.util.DefaultDataSourceResolver;
import apelet.common.core.util.MyModelUtil;
import apelet.common.dbutil.object.SqlTable;
import apelet.common.dbutil.object.SqlTableColumn;
import apelet.common.generator.model.OnlFormField;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormFieldService;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.online.dao.OnlineDatasourceRelationMapper;
import apelet.common.online.dao.OnlineDatasourceTableMapper;
import apelet.common.online.dto.OnlineDatasourceRelationDto;
import apelet.common.online.model.*;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineRedisKeyUtil;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
@ -30,6 +40,7 @@ import org.springframework.stereotype.Service; @@ -30,6 +40,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 数据源关联数据操作服务类
@ -61,6 +72,14 @@ public class OnlineDatasourceRelationServiceImpl @@ -61,6 +72,14 @@ public class OnlineDatasourceRelationServiceImpl
private RedissonClient redissonClient;
@Autowired
private CommonRedisUtil commonRedisUtil;
@Autowired
private OnlineDblinkService onlineDblinkService;
@Autowired
private IOnlFormHeadService iOnlFormHeadService;
@Autowired
private OnlineDatasourceRelationService onlineDatasourceRelationService;
@Autowired
private IOnlFormFieldService onlFormFieldService;
/**
* 返回当前Service的主表Mapper对象
@ -289,4 +308,203 @@ public class OnlineDatasourceRelationServiceImpl @@ -289,4 +308,203 @@ public class OnlineDatasourceRelationServiceImpl
}
return CallResult.ok();
}
@Override
public ResponseResult<Long> addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink) {
//历史数据 --做比较新数据写入,已存在的不用管,不存在的删除
OnlineDatasourceRelation onlineDatasourceRelations = new OnlineDatasourceRelation();
onlineDatasourceRelations.setDatasourceId(onlineDatasource.getDatasourceId());
List<OnlineDatasourceRelation> onlineDatasourceRelationList =
onlineDatasourceRelationService.getOnlineDatasourceRelationListWithRelation(onlineDatasourceRelations, null);
Map<String, OnlineDatasourceRelation> oldmap01 =
onlineDatasourceRelationList.stream().collect(Collectors.toMap(obj -> obj.getMasterColumnId() + "_" + obj.getRelationType() + "_" + obj.getSlaveTableId(), obj -> obj,
(key1, key2) -> key1
));
//查出表单元数据的所有附表关系
//主表id
List<OnlineTable> onlineTableList = onlineTableService.list(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, onlFormHead.getTableName().toLowerCase()));
//创建一对多关系:1
List<OnlFormHead> oneToMoreObjsList = iOnlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().eq(OnlFormHead::getLine, 1).eq(OnlFormHead::getSort, onlFormHead.getTableName()));
if (!oneToMoreObjsList.isEmpty()) {
List<Long> tableIdList = onlineTableList.stream().map(OnlineTable::getTableId).collect(Collectors.toList());
// //主表列id对象
OnlineColumn onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper<OnlineColumn>()
.eq(OnlineColumn::getColumnName, "id")
.in(OnlineColumn::getTableId, tableIdList));
for (OnlFormHead formHead : oneToMoreObjsList) {
List<OnlineTable> onlineTableListfu = onlineTableService.list(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, formHead.getTableName().toLowerCase()));
//附表关联的主表字段+附表id
if ((onlineTableListfu == null || onlineTableListfu.size() < 1) || !oldmap01.containsKey(onlineColumn.getColumnId() + "_1_" + onlineTableListfu.get(0).getTableId())) {
OnlineDatasourceRelationDto onlineDatasourceRelationDto = new OnlineDatasourceRelationDto();
onlineDatasourceRelationDto.setDatasourceId(onlineDatasource.getDatasourceId());
//关联名称
onlineDatasourceRelationDto.setRelationName(onlFormHead.getTableName() + "_" + formHead.getTableName());
//关联标识
onlineDatasourceRelationDto.setVariableName(onlFormHead.getTableName() + "_" + formHead.getTableName());
//关联类型
onlineDatasourceRelationDto.setRelationType(1);
//主键字段id
onlineDatasourceRelationDto.setMasterColumnId(onlineColumn.getColumnId());
//从表名称
onlineDatasourceRelationDto.setSlaveTableName(formHead.getTableName());
//从表字段名称
onlineDatasourceRelationDto.setSlaveColumnName(formHead.getSortFiled());
//是否级联删除
onlineDatasourceRelationDto.setCascadeDelete(true);
//是否左连接查询
onlineDatasourceRelationDto.setLeftJoin(false);
OnlineDatasourceRelation onlineDatasourceRelation =
MyModelUtil.copyTo(onlineDatasourceRelationDto, OnlineDatasourceRelation.class);
String appCode = TokenData.takeFromRequest().getAppCode();
if (!StrUtil.equals(onlineDatasource.getAppCode(), appCode)) {
errorMessage = "数据验证失败,当前应用并不包含该数据源Id!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// OnlineDblink onlineDblink = onlineDblinkService.getById(onlineDatasource.getDblinkId());
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, onlineDatasourceRelationDto.getSlaveTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
SqlTableColumn slaveColumn = null;
for (SqlTableColumn column : slaveTable.getColumnList()) {
if (column.getColumnName().equals(onlineDatasourceRelationDto.getSlaveColumnName())) {
slaveColumn = column;
break;
}
}
if (slaveColumn == null) {
errorMessage = "数据验证失败,指定的数据表字段 [" + onlineDatasourceRelationDto.getSlaveColumnName() + "] 不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 验证关联Id的数据合法性
CallResult callResult =
onlineDatasourceRelationService.verifyRelatedData(onlineDatasourceRelation, null);
if (!callResult.isSuccess()) {
errorMessage = callResult.getErrorMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
onlineDatasourceRelation = onlineDatasourceRelationService.saveNew(onlineDatasourceRelation, slaveTable, slaveColumn);
} else {
//更新表字段
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, formHead.getTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
List<SqlTableColumn> sqlTableColumnList = slaveTable.getColumnList();
Set<Long> tableids = new HashSet<>();
tableids.add(onlineTableListfu.get(0).getTableId());
List<OnlineColumn> listColumn = onlineColumnService.getOnlineColumnListByTableIds(tableids);
List<String> columnNames = listColumn.stream().map(OnlineColumn::getColumnName).collect(Collectors.toList());
sqlTableColumnList = sqlTableColumnList.stream().filter(x -> !columnNames.contains(x.getColumnName().toLowerCase())).collect(Collectors.toList());
onlineColumnService.saveNewList(sqlTableColumnList, onlineTableListfu.get(0).getTableId());
}
}
}
//创建一对一关系:0
List<OnlFormField> onlFormFieldList = onlFormFieldService.list(new LambdaQueryWrapper<OnlFormField>()
.eq(OnlFormField::getHeadId, onlFormHead.getId()).isNotNull(OnlFormField::getRefPropert));
//List<OnlFormHead> oneToMoreObjsList = iOnlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().eq(OnlFormHead::getLine, 1).eq(OnlFormHead::getSort, onlFormHead.getTableName()));
if (!onlFormFieldList.isEmpty()) {
Set<String> updatedTable = new HashSet<>();
//附表
for (OnlFormField onlFormField : onlFormFieldList) {
//附表
List<OnlFormHead> onlFormHeadList = iOnlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().eq(OnlFormHead::getId, onlFormField.getRefPropert()));
if (onlFormHeadList.isEmpty()) {
continue;
}
OnlFormHead formHead = onlFormHeadList.get(0);
//根据表名去找在线附表
List<OnlineTable> fuonlineTableList = onlineTableService.list(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, onlFormHeadList.get(0).getTableName()));
//主表id对象
OnlineColumn onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper<OnlineColumn>()
.eq(OnlineColumn::getColumnName, onlFormField.getFieldName())
.in(OnlineColumn::getTableId, onlineTableList.get(0).getTableId()));
if (onlineColumn == null) {
onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper<OnlineColumn>()
.eq(OnlineColumn::getColumnName, onlFormField.getFieldName().toLowerCase())
.in(OnlineColumn::getTableId, onlineTableList.get(0).getTableId()));
}
//附表关联的主表字段+附表id
if (fuonlineTableList == null || fuonlineTableList.isEmpty() || (!oldmap01.containsKey(onlineColumn.getColumnId() + "_0_" + fuonlineTableList.get(0).getTableId()))) {
OnlineDatasourceRelationDto onlineDatasourceRelationDto = new OnlineDatasourceRelationDto();
onlineDatasourceRelationDto.setDatasourceId(onlineDatasource.getDatasourceId());
//关联名称
onlineDatasourceRelationDto.setRelationName(onlFormHead.getTableName() + "_" + onlFormField.getFieldName() + "_" + formHead.getTableName());
//关联标识
onlineDatasourceRelationDto.setVariableName(onlFormHead.getTableName() + "_" + onlFormField.getFieldName() + "_" + formHead.getTableName());
//关联类型
onlineDatasourceRelationDto.setRelationType(0);
//主键字段id
onlineDatasourceRelationDto.setMasterColumnId(onlineColumn.getColumnId());
//从表名称
onlineDatasourceRelationDto.setSlaveTableName(formHead.getTableName());
//从表字段名称
onlineDatasourceRelationDto.setSlaveColumnName("id");
//是否级联删除
onlineDatasourceRelationDto.setCascadeDelete(true);
//是否左连接查询
onlineDatasourceRelationDto.setLeftJoin(false);
OnlineDatasourceRelation onlineDatasourceRelation =
MyModelUtil.copyTo(onlineDatasourceRelationDto, OnlineDatasourceRelation.class);
String appCode = TokenData.takeFromRequest().getAppCode();
if (!StrUtil.equals(onlineDatasource.getAppCode(), appCode)) {
errorMessage = "数据验证失败,当前应用并不包含该数据源Id!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// OnlineDblink onlineDblink = onlineDblinkService.getById(onlineDatasource.getDblinkId());
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, onlineDatasourceRelationDto.getSlaveTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
SqlTableColumn slaveColumn = null;
for (SqlTableColumn column : slaveTable.getColumnList()) {
if (column.getColumnName().equals(onlineDatasourceRelationDto.getSlaveColumnName())) {
slaveColumn = column;
break;
}
}
if (slaveColumn == null) {
errorMessage = "数据验证失败,指定的数据表字段 [" + onlineDatasourceRelationDto.getSlaveColumnName() + "] 不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 验证关联Id的数据合法性
CallResult callResult =
onlineDatasourceRelationService.verifyRelatedData(onlineDatasourceRelation, null);
if (!callResult.isSuccess()) {
errorMessage = callResult.getErrorMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
onlineDatasourceRelation = onlineDatasourceRelationService.saveNew(onlineDatasourceRelation, slaveTable, slaveColumn);
} else {
if (updatedTable.contains(formHead.getTableName())) {
continue;
}
updatedTable.add(formHead.getTableName());
//更新表字段
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, formHead.getTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
List<SqlTableColumn> sqlTableColumnList = slaveTable.getColumnList();
Set<Long> tableids = new HashSet<>();
tableids.add(fuonlineTableList.get(0).getTableId());
List<OnlineColumn> listColumn = onlineColumnService.getOnlineColumnListByTableIds(tableids);
List<String> columnNames = listColumn.stream().map(OnlineColumn::getColumnName).collect(Collectors.toList());
sqlTableColumnList = sqlTableColumnList.stream().filter(x -> !columnNames.contains(x.getColumnName().toLowerCase())).collect(Collectors.toList());
onlineColumnService.saveNewList(sqlTableColumnList, fuonlineTableList.get(0).getTableId());
}
}
}
return null;
}
}

6
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java

@ -11,7 +11,10 @@ import apelet.common.dbutil.object.SqlTable; @@ -11,7 +11,10 @@ import apelet.common.dbutil.object.SqlTable;
import apelet.common.online.dao.OnlineDatasourceMapper;
import apelet.common.online.dao.OnlineDatasourceTableMapper;
import apelet.common.online.dao.OnlinePageDatasourceMapper;
import apelet.common.online.model.*;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.model.OnlineDatasourceTable;
import apelet.common.online.model.OnlinePageDatasource;
import apelet.common.online.model.OnlineTable;
import apelet.common.online.service.OnlineDatasourceRelationService;
import apelet.common.online.service.OnlineDatasourceService;
import apelet.common.online.service.OnlineTableService;
@ -83,6 +86,7 @@ public class OnlineDatasourceServiceImpl extends BaseService<OnlineDatasource, L @@ -83,6 +86,7 @@ public class OnlineDatasourceServiceImpl extends BaseService<OnlineDatasource, L
TokenData tokenData = TokenData.takeFromRequest();
OnlineTable onlineTable = onlineTableService.saveNewFromSqlTable(sqlTable);
onlineDatasource.setDatasourceId(idGenerator.nextLongId());
onlineDatasource.setDatasourceName(sqlTable.getTableName());
onlineDatasource.setAppCode(tokenData.getAppCode());
onlineDatasource.setMasterTableId(onlineTable.getTableId());
Date now = new Date();

20
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java

@ -26,6 +26,7 @@ import apelet.common.orm.impl.LinkEntityColumn; @@ -26,6 +26,7 @@ import apelet.common.orm.impl.LinkEntityColumn;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
@ -35,7 +36,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -35,7 +36,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
@ -460,6 +460,24 @@ public class OnlineFormServiceImpl extends BaseService<OnlineForm, Long> impleme @@ -460,6 +460,24 @@ public class OnlineFormServiceImpl extends BaseService<OnlineForm, Long> impleme
return objectValue;
}
@Override
public List<OnlineForm> queryListByFormType(List<Integer> formType) {
// 查询已发布的 page
List<OnlinePage> pageList = onlinePageService.queryByEnableList();
if (CollectionUtil.isEmpty(pageList)) {
return Collections.emptyList();
}
Set<Long> pageIdSet = pageList.stream().map(OnlinePage::getPageId).collect(Collectors.toSet());
List<OnlineForm> formList = onlineFormMapper.selectList(new LambdaQueryWrapper<OnlineForm>().in(OnlineForm::getFormType, formType).in(OnlineForm::getPageId, pageIdSet));
if (CollectionUtil.isNotEmpty(formList)) {
formList.forEach(item -> {
item.setWidgetJson(null);
});
}
return formList;
}
/**
* 设置关联表字段转换为对象
*

5
common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java

@ -296,4 +296,9 @@ public class OnlinePageServiceImpl extends BaseService<OnlinePage, Long> impleme @@ -296,4 +296,9 @@ public class OnlinePageServiceImpl extends BaseService<OnlinePage, Long> impleme
}
return onlinePageMapper.selectList(queryWrapper);
}
@Override
public List<OnlinePage> queryByEnableList() {
return onlinePageMapper.selectList(new LambdaQueryWrapper<OnlinePage>().eq(OnlinePage::getPublished, true));
}
}

87
common/common-online/src/main/java/apelet/common/online/util/WidgetFieldTypeMapping.java

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
package apelet.common.online.util;
import java.util.HashMap;
import java.util.Map;
public class WidgetFieldTypeMapping {
private WidgetFieldTypeMapping() {
}
private static final Map<Integer, String> WIDGET_TO_FIELD_TYPE = new HashMap<>();
private static final Map<Integer, Integer> DEFAULT_LENGTH = new HashMap<>();
static {
// 文本输入框 → varchar
WIDGET_TO_FIELD_TYPE.put(1, "0");
DEFAULT_LENGTH.put(1, 255);
// 数字输入框 → int
WIDGET_TO_FIELD_TYPE.put(3, "1");
// 开关组件 → int
WIDGET_TO_FIELD_TYPE.put(5, "1");
// 滑块组件 → decimal
WIDGET_TO_FIELD_TYPE.put(6, "4");
// 单选组件 → varchar
WIDGET_TO_FIELD_TYPE.put(7, "0");
DEFAULT_LENGTH.put(7, 255);
// 复选框 → varchar
WIDGET_TO_FIELD_TYPE.put(8, "0");
DEFAULT_LENGTH.put(8, 255);
// 下拉选择框 → varchar
WIDGET_TO_FIELD_TYPE.put(10, "0");
DEFAULT_LENGTH.put(10, 255);
// 级联选择框 → varchar
WIDGET_TO_FIELD_TYPE.put(12, "0");
DEFAULT_LENGTH.put(12, 255);
// 树形选择 → varchar
WIDGET_TO_FIELD_TYPE.put(13, "0");
DEFAULT_LENGTH.put(13, 255);
// 评分组件 → int
WIDGET_TO_FIELD_TYPE.put(14, "1");
DEFAULT_LENGTH.put(14, 5);
// 日期选择框 → datetime
WIDGET_TO_FIELD_TYPE.put(20, "3");
// 颜色选择 → varchar
WIDGET_TO_FIELD_TYPE.put(30, "0");
DEFAULT_LENGTH.put(30, 50);
// 附件上传 → varchar
WIDGET_TO_FIELD_TYPE.put(31, "0");
DEFAULT_LENGTH.put(31, 255);
// 图片上传 → varchar
WIDGET_TO_FIELD_TYPE.put(653, "0");
DEFAULT_LENGTH.put(653, 255);
// 富文本编辑 → varchar(2000)
WIDGET_TO_FIELD_TYPE.put(32, "0");
DEFAULT_LENGTH.put(32, 2000);
// MD编辑器 → varchar(2000)
WIDGET_TO_FIELD_TYPE.put(760, "0");
DEFAULT_LENGTH.put(760, 2000);
// 用户选择 → bigint
WIDGET_TO_FIELD_TYPE.put(400, "2");
// 部门选择 → bigint
WIDGET_TO_FIELD_TYPE.put(401, "2");
// 关联选择 → bigint
WIDGET_TO_FIELD_TYPE.put(402, "2");
// 时间选择器 → datetime
WIDGET_TO_FIELD_TYPE.put(652, "3");
// 文件上传 → varchar
WIDGET_TO_FIELD_TYPE.put(750, "0");
DEFAULT_LENGTH.put(750, 255);
}
public static String getFieldTypeId(Integer widgetType) {
String type = WIDGET_TO_FIELD_TYPE.get(widgetType);
if (type == null) {
throw new IllegalArgumentException("不支持的控件类型: " + widgetType);
}
return type;
}
public static Integer getDefaultLength(Integer widgetType) {
return DEFAULT_LENGTH.get(widgetType);
}
public static boolean isDataWidget(Integer widgetType) {
return WIDGET_TO_FIELD_TYPE.containsKey(widgetType);
}
}

3
common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java

@ -8,6 +8,7 @@ import apelet.common.online.controller.OnlinePageController; @@ -8,6 +8,7 @@ import apelet.common.online.controller.OnlinePageController;
import apelet.common.online.dto.OnlineDatasourceDto;
import apelet.common.online.dto.OnlinePageDatasourceDto;
import apelet.common.online.dto.OnlinePageDto;
import apelet.common.online.model.OnlinePage;
import apelet.common.online.vo.OnlineDatasourceVo;
import apelet.common.online.vo.OnlinePageDatasourceVo;
import apelet.common.online.vo.OnlinePageVo;
@ -43,7 +44,7 @@ public class TenantOnlinePageController extends OnlinePageController { @@ -43,7 +44,7 @@ public class TenantOnlinePageController extends OnlinePageController {
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
@Override
public ResponseResult<Long> add(@MyRequestBody OnlinePageDto onlinePageDto) {
public ResponseResult<OnlinePage> add(@MyRequestBody OnlinePageDto onlinePageDto) {
return super.add(onlinePageDto);
}

1
common/pom.xml

@ -13,7 +13,6 @@ @@ -13,7 +13,6 @@
<modules>
<module>common-flow</module>
<module>common-flow-online</module>
<module>common-online</module>
<module>common-tenant</module>

Loading…
Cancel
Save