From 62e31ea360e5e03ce1452854793e4b6dd587250f Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Wed, 14 Jan 2026 15:52:59 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=AB=E7=85=A7?= =?UTF-8?q?=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index e11897ea..89af20d0 100644 --- a/pom.xml +++ b/pom.xml @@ -222,11 +222,11 @@ 中铜 ZStack 私服 http://172.16.46.63:30708/repository/test/ - - - - - + + ZT-snap + 中铜 ZStack 私服 + http://172.16.46.63:30708/repository/test-snap/ + From 0ce56cad8b1875ed1ac7344aff933f8058956af6 Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Wed, 14 Jan 2026 17:03:55 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 14 ++++++++++++++ zt-dependencies/pom.xml | 10 +++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 89af20d0..5251fcb8 100644 --- a/pom.xml +++ b/pom.xml @@ -209,6 +209,20 @@ always warn + + false + always + + + + ZT-snap + 中铜 ZStack 私服 + http://172.16.46.63:30708/repository/test-snap/ + + false + always + warn + true always diff --git a/zt-dependencies/pom.xml b/zt-dependencies/pom.xml index 03e2e90f..8212d924 100644 --- a/zt-dependencies/pom.xml +++ b/zt-dependencies/pom.xml @@ -10,11 +10,11 @@ 中铜 ZStack 私服 http://172.16.46.63:30708/repository/test/ - - - - - + + ZT-snap + 中铜 ZStack 私服 + http://172.16.46.63:30708/repository/test-snap/ + com.zt.plat zt-dependencies From 63708dfb36543e89fba02a63a89f5ca9096ca17f Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Wed, 14 Jan 2026 17:09:18 +0800 Subject: [PATCH 3/8] =?UTF-8?q?bmp=20=E5=B7=B2=E6=8C=AA=E5=88=B0=20ztcloud?= =?UTF-8?q?-dist=20=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5251fcb8..3f36d517 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ zt-module-system zt-module-infra - zt-module-bpm + zt-module-report From 287d24fc7f17f1899508af9cc83ad2c2e480b7fe Mon Sep 17 00:00:00 2001 From: wuzongyong <13203449218@163.com> Date: Wed, 14 Jan 2026 18:11:02 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat(gateway):=20=E6=B7=BB=E5=8A=A0API?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=87=AD=E8=AF=81=E5=8A=A0=E5=AF=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在ApiClientCredentialDO实体类中新增enableEncryption字段 - 在ApiClientCredentialRespVO响应对象中添加加密启用状态字段 - 在ApiClientCredentialSaveReqVO请求对象中添加加密启用状态字段 - 在GatewaySecurityFilter中实现加密启用状态检查逻辑 - 添加数据库表结构变更脚本支持加密字段 --- ...s_api_credential_enable_encryption_20260114.sql | 7 +++++++ .../vo/credential/ApiClientCredentialRespVO.java | 3 +++ .../credential/ApiClientCredentialSaveReqVO.java | 4 ++++ .../dataobject/gateway/ApiClientCredentialDO.java | 2 ++ .../gateway/security/GatewaySecurityFilter.java | 14 ++++++++++++++ 5 files changed, 30 insertions(+) create mode 100644 sql/dm/databus_api_credential_enable_encryption_20260114.sql diff --git a/sql/dm/databus_api_credential_enable_encryption_20260114.sql b/sql/dm/databus_api_credential_enable_encryption_20260114.sql new file mode 100644 index 00000000..c7e0da21 --- /dev/null +++ b/sql/dm/databus_api_credential_enable_encryption_20260114.sql @@ -0,0 +1,7 @@ +-- 为 API 客户端凭证表添加"是否启用加密"字段 +-- 2026-01-14 + +ALTER TABLE databus_api_client_credential + ADD enable_encryption BIT DEFAULT '1' NOT NULL; + +COMMENT ON COLUMN databus_api_client_credential.enable_encryption IS '是否启用加密传输'; diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialRespVO.java index 33e2dbe7..a4cc7ec5 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialRespVO.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialRespVO.java @@ -42,6 +42,9 @@ public class ApiClientCredentialRespVO { @Schema(description = "匿名访问固定用户昵称", example = "张三") private String anonymousUserNickname; + @Schema(description = "是否启用加密", example = "true") + private Boolean enableEncryption; + @Schema(description = "创建时间") private LocalDateTime createTime; diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialSaveReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialSaveReqVO.java index 11043ac5..796b1704 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialSaveReqVO.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/credential/ApiClientCredentialSaveReqVO.java @@ -45,4 +45,8 @@ public class ApiClientCredentialSaveReqVO { @Schema(description = "匿名访问固定用户 ID", example = "1024") private Long anonymousUserId; + @Schema(description = "是否启用加密", example = "true") + @NotNull(message = "启用加密标识不能为空") + private Boolean enableEncryption; + } diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiClientCredentialDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiClientCredentialDO.java index 0bf5134f..7b44ecce 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiClientCredentialDO.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiClientCredentialDO.java @@ -38,4 +38,6 @@ public class ApiClientCredentialDO extends BaseDO { private Long anonymousUserId; + private Boolean enableEncryption; + } diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java index f9e5754d..b37dfb76 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java @@ -238,6 +238,11 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { private byte[] decryptRequestBody(byte[] originalBody, ApiClientCredentialDO credential, ApiGatewayProperties.Security security) { + // 检查是否启用加密,如果未启用则直接返回原文 + if (credential != null && Boolean.FALSE.equals(credential.getEnableEncryption())) { + return originalBody != null ? originalBody : new byte[0]; + } + if (originalBody == null || originalBody.length == 0) { return new byte[0]; } @@ -390,6 +395,11 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { private void encryptResponse(ContentCachingResponseWrapper responseWrapper, ApiClientCredentialDO credential, ApiGatewayProperties.Security security) throws IOException { + // 检查是否启用加密,如果未启用则直接返回,不加密响应 + if (credential != null && Boolean.FALSE.equals(credential.getEnableEncryption())) { + return; + } + if (!security.isEncryptResponse()) { return; } @@ -524,6 +534,10 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { if (security == null || credential == null) { return false; } + // 检查是否启用加密,如果未启用则不加密错误响应 + if (Boolean.FALSE.equals(credential.getEnableEncryption())) { + return false; + } if (!security.isEncryptResponse()) { return false; } From 26ab7dac241444128f3b0085b4b217fad144fccb Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Thu, 15 Jan 2026 10:07:17 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=A7=81=E6=9C=8D?= =?UTF-8?q?=E5=9C=B0=E5=9D=80,=E6=8A=8A=20seata-dm=20=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E4=BB=8E=20dsc=E6=8C=AA=E8=BF=87=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 16 +- zt-framework/pom.xml | 2 +- .../zt-spring-boot-starter-seata-dm/pom.xml | 32 ++ .../seata/rm/datasource/DataCompareUtils.java | 326 ++++++++++++++++++ 4 files changed, 360 insertions(+), 16 deletions(-) create mode 100644 zt-framework/zt-spring-boot-starter-seata-dm/pom.xml create mode 100644 zt-framework/zt-spring-boot-starter-seata-dm/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java diff --git a/pom.xml b/pom.xml index 3f36d517..6743a5ca 100644 --- a/pom.xml +++ b/pom.xml @@ -204,25 +204,11 @@ ZT 中铜 ZStack 私服 - http://172.16.46.63:30708/repository/test/ + http://172.16.46.63:30708/repository/zt-cloud/ always warn - - false - always - - - - ZT-snap - 中铜 ZStack 私服 - http://172.16.46.63:30708/repository/test-snap/ - - false - always - warn - true always diff --git a/zt-framework/pom.xml b/zt-framework/pom.xml index 241a211e..d34ccec7 100644 --- a/zt-framework/pom.xml +++ b/zt-framework/pom.xml @@ -25,7 +25,7 @@ zt-spring-boot-starter-job zt-spring-boot-starter-mq zt-spring-boot-starter-rpc - + zt-spring-boot-starter-seata-dm zt-spring-boot-starter-excel zt-spring-boot-starter-test diff --git a/zt-framework/zt-spring-boot-starter-seata-dm/pom.xml b/zt-framework/zt-spring-boot-starter-seata-dm/pom.xml new file mode 100644 index 00000000..49d7b1e2 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-seata-dm/pom.xml @@ -0,0 +1,32 @@ + + + + zt-framework + com.zt.plat + ${revision} + + 4.0.0 + jar + + zt-spring-boot-starter-seata-dm + + ${project.artifactId} + + Seata 达梦数据库补丁模块 + 解决 DmdbTimestamp 时区格式不一致导致的 dirty undo log 回滚失败问题 + 补丁来源: https://github.com/apache/incubator-seata/pull/7538 + Seata 2.6.0 发布后可移除此模块 + + + + + + org.apache.seata + seata-spring-boot-starter + provided + + + + diff --git a/zt-framework/zt-spring-boot-starter-seata-dm/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java b/zt-framework/zt-spring-boot-starter-seata-dm/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java new file mode 100644 index 00000000..24c15d5a --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-seata-dm/src/main/java/org/apache/seata/rm/datasource/DataCompareUtils.java @@ -0,0 +1,326 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.seata.rm.datasource; + +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.core.model.Result; +import org.apache.seata.rm.datasource.sql.struct.Field; +import org.apache.seata.rm.datasource.sql.struct.Row; +import org.apache.seata.rm.datasource.sql.struct.TableRecords; +import org.apache.seata.rm.datasource.undo.AbstractUndoLogManager; +import org.apache.seata.rm.datasource.undo.parser.FastjsonUndoLogParser; +import org.apache.seata.rm.datasource.undo.parser.JacksonUndoLogParser; +import org.apache.seata.sqlparser.struct.TableMeta; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * DataCompareUtils - 包含达梦数据库 DmdbTimestamp 时区问题的补丁 + *

+ * 此类覆盖 Seata 原有的 DataCompareUtils,添加了对达梦数据库 DmdbTimestamp 类型的特殊处理。 + * 通过将 DmdbTimestamp 转换为 UTC Instant 进行比较,解决时区格式不一致导致的 dirty undo log 问题。 + *

+ * 问题背景: + * - 达梦数据库的 DmdbTimestamp 类型在序列化/反序列化后时区格式不一致 + * - 例如:beforeImage 为 "2025-12-25 09:38:54.077811 +08:00" + * afterImage 为 "2025-12-25 09:38:54.077811" + * - 导致 Seata AT 模式回滚时 dirty undo log 检查失败 + *

+ * 解决方案: + * - 当检测到 DmdbTimestamp 类型时,将两个值都转换为 UTC Instant 进行比较 + * - 这样可以忽略时区格式差异,只比较实际的时间点 + *

+ * 补丁来源: https://github.com/apache/incubator-seata/pull/7538 + * 相关 Issue: https://github.com/apache/incubator-seata/issues/7453 + * 该修复已合并到 Seata 2.x 分支,将在 Seata 2.6.0 正式发布,届时可删除此模块。 + * + * @author Seata Community (PR #7538) + */ +public class DataCompareUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(DataCompareUtils.class); + + /** + * 标识补丁类是否已加载 + */ + private static final boolean PATCHED; + + static { + PATCHED = true; + LOGGER.info("[zt-spring-boot-starter-seata-dm] DataCompareUtils 补丁类已加载,用于解决达梦数据库 DmdbTimestamp 时区问题"); + } + + private DataCompareUtils() {} + + /** + * Is field equals result. + * + * @param f0 the f 0 + * @param f1 the f 1 + * @return the result + */ + public static Result isFieldEquals(Field f0, Field f1) { + if (f0 == null) { + return Result.build(f1 == null); + } else { + if (f1 == null) { + return Result.build(false); + } else { + if (StringUtils.equalsIgnoreCase(f0.getName(), f1.getName()) && f0.getType() == f1.getType()) { + if (f0.getValue() == null) { + return Result.build(f1.getValue() == null); + } else { + if (f1.getValue() == null) { + return Result.buildWithParams( + false, "Field not equals, name {}, new value is null", f0.getName()); + } else { + String currentSerializer = AbstractUndoLogManager.getCurrentSerializer(); + if (StringUtils.equals(currentSerializer, FastjsonUndoLogParser.NAME)) { + convertType(f0, f1); + } + // 达梦数据库 DmdbTimestamp 时区补丁 (PR #7538) + if (StringUtils.equals(currentSerializer, JacksonUndoLogParser.NAME)) { + Object v0 = f0.getValue(); + Object v1 = f1.getValue(); + if (isDmdbTimestamp(v0) && isDmdbTimestamp(v1)) { + Instant i0 = toInstant(v0); + Instant i1 = toInstant(v1); + boolean equals = Objects.equals(i0, i1); + LOGGER.info("[zt-seata-dm-patch] DmdbTimestamp 字段比较: field={}, equals={}", f0.getName(), equals); + return equals + ? Result.ok() + : Result.buildWithParams( + false, + "Field not equals (DmdbTimestamp), name {}, old value {}, new value {}", + f0.getName(), + v0, + v1); + } + } + boolean result = Objects.deepEquals(f0.getValue(), f1.getValue()); + if (result) { + return Result.ok(); + } else { + return Result.buildWithParams( + false, + "Field not equals, name {}, old value {}, new value {}", + f0.getName(), + f0.getValue(), + f1.getValue()); + } + } + } + } else { + return Result.buildWithParams( + false, + "Field not equals, old name {} type {}, new name {} type {}", + f0.getName(), + f0.getType(), + f1.getName(), + f1.getType()); + } + } + } + } + + private static void convertType(Field f0, Field f1) { + int f0Type = f0.getType(); + int f1Type = f1.getType(); + if (f0Type == Types.DATE && f0.getValue().getClass().equals(String.class)) { + String[] strings = f0.getValue().toString().split(" "); + f0.setValue(Date.valueOf(strings[0])); + } + if (f1Type == Types.DATE && f1.getValue().getClass().equals(String.class)) { + String[] strings = f1.getValue().toString().split(" "); + f1.setValue(Date.valueOf(strings[0])); + } + if (f0Type == Types.TIME && f0.getValue().getClass().equals(String.class)) { + f0.setValue(Time.valueOf(f0.getValue().toString())); + } + if (f1Type == Types.TIME && f1.getValue().getClass().equals(String.class)) { + f1.setValue(Time.valueOf(f1.getValue().toString())); + } + if (f0Type == Types.TIMESTAMP && f0.getValue().getClass().equals(String.class)) { + if (f1.getValue().getClass().equals(LocalDateTime.class)) { + f0.setValue(LocalDateTime.parse(f0.getValue().toString())); + } else { + f0.setValue(Timestamp.valueOf(f0.getValue().toString())); + } + } + if (f1Type == Types.TIMESTAMP && f1.getValue().getClass().equals(String.class)) { + f1.setValue(Timestamp.valueOf(f1.getValue().toString())); + } + if (f0Type == Types.DECIMAL && f0.getValue().getClass().equals(Integer.class)) { + f0.setValue(new BigDecimal(f0.getValue().toString())); + } + if (f1Type == Types.DECIMAL && f1.getValue().getClass().equals(Integer.class)) { + f1.setValue(new BigDecimal(f1.getValue().toString())); + } + if (f0Type == Types.BIGINT && f0.getValue().getClass().equals(Integer.class)) { + f0.setValue(Long.parseLong(f0.getValue().toString())); + } + if (f1Type == Types.BIGINT && f1.getValue().getClass().equals(Integer.class)) { + f1.setValue(Long.parseLong(f1.getValue().toString())); + } + } + + /** + * Is records equals result. + * + * @param beforeImage the before image + * @param afterImage the after image + * @return the result + */ + public static Result isRecordsEquals(TableRecords beforeImage, TableRecords afterImage) { + if (beforeImage == null) { + return Result.build(afterImage == null, null); + } else { + if (afterImage == null) { + return Result.build(false, null); + } + if (beforeImage.getTableName().equalsIgnoreCase(afterImage.getTableName()) + && CollectionUtils.isSizeEquals(beforeImage.getRows(), afterImage.getRows())) { + // when image is EmptyTableRecords, getTableMeta will throw an exception + if (CollectionUtils.isEmpty(beforeImage.getRows())) { + return Result.ok(); + } + return compareRows(beforeImage.getTableMeta(), beforeImage.getRows(), afterImage.getRows()); + } else { + return Result.build(false, null); + } + } + } + + /** + * Is rows equals result. + * + * @param tableMetaData the table meta data + * @param oldRows the old rows + * @param newRows the new rows + * @return the result + */ + public static Result isRowsEquals(TableMeta tableMetaData, List oldRows, List newRows) { + if (!CollectionUtils.isSizeEquals(oldRows, newRows)) { + return Result.build(false, null); + } + return compareRows(tableMetaData, oldRows, newRows); + } + + private static Result compareRows(TableMeta tableMetaData, List oldRows, List newRows) { + // old row to map + Map> oldRowsMap = rowListToMap(oldRows, tableMetaData.getPrimaryKeyOnlyName()); + // new row to map + Map> newRowsMap = rowListToMap(newRows, tableMetaData.getPrimaryKeyOnlyName()); + // compare data + for (Map.Entry> oldEntry : oldRowsMap.entrySet()) { + String key = oldEntry.getKey(); + Map oldRow = oldEntry.getValue(); + Map newRow = newRowsMap.get(key); + if (newRow == null) { + return Result.buildWithParams(false, "compare row failed, rowKey {}, reason [newRow is null]", key); + } + for (Map.Entry oldRowEntry : oldRow.entrySet()) { + String fieldName = oldRowEntry.getKey(); + Field oldField = oldRowEntry.getValue(); + Field newField = newRow.get(fieldName); + if (newField == null) { + return Result.buildWithParams( + false, + "compare row failed, rowKey {}, fieldName {}, reason [newField is null]", + key, + fieldName); + } + Result oldEqualsNewFieldResult = isFieldEquals(oldField, newField); + if (!oldEqualsNewFieldResult.getResult()) { + return oldEqualsNewFieldResult; + } + } + } + return Result.ok(); + } + + /** + * Row list to map map. + * + * @param rowList the row list + * @param primaryKeyList the primary key list + * @return the map + */ + public static Map> rowListToMap(List rowList, List primaryKeyList) { + // {value of primaryKey, value of all columns} + Map> rowMap = new HashMap<>(); + for (Row row : rowList) { + // ensure the order of column + List rowFieldList = row.getFields().stream() + .sorted(Comparator.comparing(Field::getName)) + .collect(Collectors.toList()); + // {uppercase fieldName : field} + Map colsMap = new HashMap<>(); + StringBuilder rowKey = new StringBuilder(); + boolean firstUnderline = false; + for (int j = 0; j < rowFieldList.size(); j++) { + Field field = rowFieldList.get(j); + if (primaryKeyList.stream().anyMatch(e -> field.getName().equals(e))) { + if (firstUnderline && j > 0) { + rowKey.append("_"); + } + rowKey.append(String.valueOf(field.getValue())); + firstUnderline = true; + } + colsMap.put(field.getName().trim().toUpperCase(), field); + } + rowMap.put(rowKey.toString(), colsMap); + } + return rowMap; + } + + /** + * 判断是否为达梦数据库的 DmdbTimestamp 类型 + */ + private static boolean isDmdbTimestamp(Object obj) { + return obj != null + && "dm.jdbc.driver.DmdbTimestamp".equals(obj.getClass().getName()); + } + + /** + * 将 DmdbTimestamp 转换为 Instant + */ + private static Instant toInstant(Object dmdbTimestamp) { + try { + Method toInstantMethod = dmdbTimestamp.getClass().getMethod("toInstant"); + return (Instant) toInstantMethod.invoke(dmdbTimestamp); + } catch (Exception e) { + throw new RuntimeException("Failed to convert DmdbTimestamp to Instant", e); + } + } +} From a44e7e835e553d7b5d718e3b05149eb8820021ef Mon Sep 17 00:00:00 2001 From: lenovo <549193112@qq.com> Date: Thu, 15 Jan 2026 14:45:43 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8DBUG710=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/dm/ruoyi-vue-pro-dm8.sql | 4 +++- sql/dm/添加文件下载次数统计字段.sql | 5 +++++ .../com/zt/plat/module/infra/api/file/dto/FileRespDTO.java | 3 +++ .../module/infra/controller/admin/file/FileController.java | 4 ++++ .../infra/controller/admin/file/vo/file/FileRespVO.java | 3 +++ .../zt/plat/module/infra/dal/dataobject/file/FileDO.java | 5 +++++ .../zt/plat/module/infra/dal/mysql/file/FileMapper.java | 5 +++++ .../com/zt/plat/module/infra/service/file/FileService.java | 7 +++++++ .../zt/plat/module/infra/service/file/FileServiceImpl.java | 5 +++++ 9 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 sql/dm/添加文件下载次数统计字段.sql diff --git a/sql/dm/ruoyi-vue-pro-dm8.sql b/sql/dm/ruoyi-vue-pro-dm8.sql index 9dc55d0a..1a0eba7e 100644 --- a/sql/dm/ruoyi-vue-pro-dm8.sql +++ b/sql/dm/ruoyi-vue-pro-dm8.sql @@ -339,7 +339,8 @@ CREATE TABLE infra_file ( create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, updater varchar(64) DEFAULT '' NULL, update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, - deleted bit DEFAULT '0' NOT NULL + deleted bit DEFAULT '0' NOT NULL, + DOWNLOAD_COUNT INT DEFAULT 0 NOT NULL ); COMMENT ON COLUMN infra_file.id IS '文件编号'; @@ -356,6 +357,7 @@ COMMENT ON COLUMN infra_file.create_time IS '创建时间'; COMMENT ON COLUMN infra_file.updater IS '更新者'; COMMENT ON COLUMN infra_file.update_time IS '更新时间'; COMMENT ON COLUMN infra_file.deleted IS '是否删除'; +COMMENT ON COLUMN INFRA_FILE.DOWNLOAD_COUNT IS '下载次数'; COMMENT ON TABLE infra_file IS '文件表'; CREATE INDEX idx_infra_file_hash ON infra_file(hash); diff --git a/sql/dm/添加文件下载次数统计字段.sql b/sql/dm/添加文件下载次数统计字段.sql new file mode 100644 index 00000000..dac2a7b7 --- /dev/null +++ b/sql/dm/添加文件下载次数统计字段.sql @@ -0,0 +1,5 @@ +-- 添加文件下载次数统计字段 +ALTER TABLE JYGK_TEST.INFRA_FILE + ADD DOWNLOAD_COUNT INT DEFAULT 0 NOT NULL; + +COMMENT ON COLUMN JYGK_TEST.INFRA_FILE.DOWNLOAD_COUNT IS '下载次数'; \ No newline at end of file diff --git a/zt-module-infra/zt-module-infra-api/src/main/java/com/zt/plat/module/infra/api/file/dto/FileRespDTO.java b/zt-module-infra/zt-module-infra-api/src/main/java/com/zt/plat/module/infra/api/file/dto/FileRespDTO.java index 01093d15..58af4c9a 100644 --- a/zt-module-infra/zt-module-infra-api/src/main/java/com/zt/plat/module/infra/api/file/dto/FileRespDTO.java +++ b/zt-module-infra/zt-module-infra-api/src/main/java/com/zt/plat/module/infra/api/file/dto/FileRespDTO.java @@ -34,4 +34,7 @@ public class FileRespDTO { @Schema(description = "文件内容", requiredMode = Schema.RequiredMode.REQUIRED) private byte[] content; + @Schema(description = "文件下载次数") + private Integer downloadCount; + } \ No newline at end of file diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java index 101a2654..d4b971a9 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java @@ -133,6 +133,10 @@ public class FileController { response.setStatus(HttpStatus.NOT_FOUND.value()); return; } + + // 统计下载次数 + fileService.incDownloadCount(configId,path); + writeAttachment(response, path, content); } diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/vo/file/FileRespVO.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/vo/file/FileRespVO.java index 161cf629..6d4d12f4 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/vo/file/FileRespVO.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/vo/file/FileRespVO.java @@ -99,4 +99,7 @@ public class FileRespVO { @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; + @Schema(description = "下载次数") + private Integer downloadCount; + } diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/dataobject/file/FileDO.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/dataobject/file/FileDO.java index 131ea253..c82c0e3c 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/dataobject/file/FileDO.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/dataobject/file/FileDO.java @@ -65,6 +65,11 @@ public class FileDO extends BaseDO { */ private String aesIv; + /** + * 文件下载次数统计 + */ + private Integer downloadCount; + /** * 是否加密 *

diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/mysql/file/FileMapper.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/mysql/file/FileMapper.java index df330c49..9857cdc2 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/mysql/file/FileMapper.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/dal/mysql/file/FileMapper.java @@ -6,6 +6,8 @@ import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; import com.zt.plat.module.infra.controller.admin.file.vo.file.FilePageReqVO; import com.zt.plat.module.infra.dal.dataobject.file.FileDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; /** * 文件操作 Mapper @@ -32,4 +34,7 @@ public interface FileMapper extends BaseMapperX { return selectFirstOne(FileDO::getHash, hash); } + + @Update("UPDATE INFRA_FILE SET DOWNLOAD_COUNT = DOWNLOAD_COUNT + 1 WHERE CONFIG_ID = #{configId} AND PATH = #{path}") + int incDownloadCount(@Param("configId") Long configId, @Param("path") String path); } diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileService.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileService.java index 96e6b3d4..6624810c 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileService.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileService.java @@ -112,4 +112,11 @@ public interface FileService { FileDO getActiveFileById(Long fileId); boolean verifyCode(Long fileId, Long userId, String code) throws Exception; + + /** + * 更新文件下载次数 + * @param configId + * @param path + */ + void incDownloadCount(Long configId, String path); } diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileServiceImpl.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileServiceImpl.java index 2c62ec69..14f42c77 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileServiceImpl.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/service/file/FileServiceImpl.java @@ -334,4 +334,9 @@ public class FileServiceImpl implements FileService { } } + @Override + public void incDownloadCount(Long configId, String path) { + fileMapper.incDownloadCount(configId, path); + } + } From 8a7c74bfb57249302bae9ba61c6c0b2a0a990803 Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Thu, 15 Jan 2026 15:54:18 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9docker=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-infra/zt-module-infra-server-app/Dockerfile | 2 +- zt-module-report/zt-module-report-server-app/Dockerfile | 2 +- zt-module-system/zt-module-system-server-app/Dockerfile | 2 +- zt-module-template/zt-module-template-server-app/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zt-module-infra/zt-module-infra-server-app/Dockerfile b/zt-module-infra/zt-module-infra-server-app/Dockerfile index 76f0282c..cdcdd75f 100644 --- a/zt-module-infra/zt-module-infra-server-app/Dockerfile +++ b/zt-module-infra/zt-module-infra-server-app/Dockerfile @@ -6,7 +6,7 @@ FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre RUN mkdir -p /zt-module-infra-server-app WORKDIR /zt-module-infra-server-app ## 将后端项目的 Jar 文件,复制到镜像中 -COPY ../zt-module-infra-server/target/zt-module-infra-server-app.jar app.jar +COPY ./target/zt-module-infra-server-app.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 diff --git a/zt-module-report/zt-module-report-server-app/Dockerfile b/zt-module-report/zt-module-report-server-app/Dockerfile index 6e5aa793..32367c0b 100644 --- a/zt-module-report/zt-module-report-server-app/Dockerfile +++ b/zt-module-report/zt-module-report-server-app/Dockerfile @@ -6,7 +6,7 @@ FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre RUN mkdir -p /zt-module-report-server-app WORKDIR /zt-module-report-server-app ## 将后端项目的 Jar 文件,复制到镜像中 -COPY ../zt-module-report-server/target/zt-module-report-server-app.jar app.jar +COPY ./target/zt-module-report-server-app.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 diff --git a/zt-module-system/zt-module-system-server-app/Dockerfile b/zt-module-system/zt-module-system-server-app/Dockerfile index 611da8b3..fed0ffb7 100644 --- a/zt-module-system/zt-module-system-server-app/Dockerfile +++ b/zt-module-system/zt-module-system-server-app/Dockerfile @@ -6,7 +6,7 @@ FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre RUN mkdir -p /zt-module-system-server-app WORKDIR /zt-module-system-server-app ## 将后端项目的 Jar 文件,复制到镜像中 -COPY ../zt-module-system-server/target/zt-module-system-server-app.jar app.jar +COPY ./target/zt-module-system-server-app.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 diff --git a/zt-module-template/zt-module-template-server-app/Dockerfile b/zt-module-template/zt-module-template-server-app/Dockerfile index 0630e44e..38331059 100644 --- a/zt-module-template/zt-module-template-server-app/Dockerfile +++ b/zt-module-template/zt-module-template-server-app/Dockerfile @@ -6,7 +6,7 @@ FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre RUN mkdir -p /zt-module-template-server-app WORKDIR /zt-module-template-server-app ## 将后端项目的 Jar 文件,复制到镜像中 -COPY ../zt-module-template-server/target/zt-module-template-server-app.jar app.jar +COPY ./target/zt-module-template-server-app.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 From d1e2e25b96295b18ee32fce6bc0974395e0e0a60 Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Thu, 15 Jan 2026 16:05:02 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20zt-module-databus-serv?= =?UTF-8?q?er-app=20=E7=9A=84=20dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zt-module-databus-server-app/Dockerfile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 zt-module-databus/zt-module-databus-server-app/Dockerfile diff --git a/zt-module-databus/zt-module-databus-server-app/Dockerfile b/zt-module-databus/zt-module-databus-server-app/Dockerfile new file mode 100644 index 00000000..aff90a83 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server-app/Dockerfile @@ -0,0 +1,19 @@ +## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 + +FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre + +## 创建目录,并使用它作为工作目录 +RUN mkdir -p /zt-module-databus-server-app +WORKDIR /zt-module-databus-server-app +## 将后端项目的 Jar 文件,复制到镜像中 +COPY ./target/zt-module-databus-server-app.jar app.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m" + +## 暴露后端项目的 48080 端口 +EXPOSE 48082 + +## 启动后端项目 +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar