Compare commits

...

9 Commits

Author SHA1 Message Date
chenchuchuan 3104f1b026 refactor(common-generator): 优化在线表单头服务实现 1 week ago
chenchuchuan 6fc400ab77 feat(online): 表单字段保存时,修改字段备注取值逻辑 2 weeks ago
chenchuchuan e9c3b36512 feat(online): 添加数据表字段保存功能 2 weeks ago
chenchuchuan 7dcb3bb5c9 refactor(online): 使用常量替换硬编码的元数据源字符串 2 weeks ago
chenchuchuan f405113552 fix(controller): 修正在线页面控制器返回类型 2 weeks ago
sunquan 15dd345744 fix:补在线表单控件和数据库字段类型 2 weeks ago
sunquan 1d42dfc702 fix:去掉nacos配置 app-config、t-admin-dev 2 weeks ago
sunquan c665cde835 fix:去掉nacos配置 2 weeks ago
sunquan 610a4d10ed feature-online-form-direct-creation:收到请求后自动创建数据库物理表 + 元数据记录 + 数据源配置 2 weeks 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. 20
      application-tenant/tenant-admin/src/main/resources/logback-spring.xml
  4. 355
      application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml
  5. 11
      common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java
  6. 39
      common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java
  7. 423
      common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java
  8. 89
      common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java
  9. 14
      common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java
  10. 10
      common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java
  11. 17
      common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java
  12. 29
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java
  13. 218
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java
  14. 6
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java
  15. 87
      common/common-online/src/main/java/apelet/common/online/util/WidgetFieldTypeMapping.java
  16. 3
      common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java

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 # 引入共享配置

20
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="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="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="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="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="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="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="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="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>
<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

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;
}
}

423
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;
@ -47,8 +53,9 @@ import org.springframework.web.bind.annotation.*; @@ -47,8 +53,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 +90,17 @@ public class OnlineFormController { @@ -83,12 +90,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 +338,13 @@ public class OnlineFormController { @@ -326,6 +338,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 +704,404 @@ public class OnlineFormController { @@ -685,4 +704,404 @@ 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);
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");
this.saveField(widget.getInteger("widgetType"), bindData.getString("columnComment"), widget.getString("showName"), onlFormHead, fieldName);
}
// 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");
// 默认是字符串类型
this.saveField(750, showName, null, entryHead, newSubColumnName);
}
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.toLowerCase());
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;
}
}
//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());
}
}
changed = true;
}
}
return changed ? widgetJson : null;
}
private void saveField(Integer widgetType, String columnComment, String showName, OnlFormHead onlFormHead, String fieldName) {
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);
}
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;
}
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);
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);
}
}
}
}

89
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);
}
/**
*创建数据表关联关系 先查出改元数据表的所有附表关系
* 创建数据表关联关系 先查出改元数据表的所有附表关系
* 一对多
*
*/
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink);
addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink);
return ResponseResult.success(onlinePage.getPageId());
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);
}

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();

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);
}

Loading…
Cancel
Save