Commit 88fd7b685a34f79500d3717af0a42f86d4b70006

Authored by YunaiV
1 parent 607b2e33

feat: 完善瀚高可选接入与信创兼容说明

README.md
... ... @@ -47,7 +47,7 @@
47 47 * 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本
48 48 * 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
49 49 * 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
50   -* 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等
  50 +* 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB,以及达梦、人大金仓、瀚高等国产数据库,详见 [信创兼容与数据库支持](#-信创兼容与数据库支持)
51 51 * 消息队列可使用 Event、Redis、RabbitMQ、Kafka、RocketMQ 等
52 52 * 权限认证使用 Spring Security & Token & Redis,支持多终端、多种用户的认证系统,支持 SSO 单点登录
53 53 * 支持加载动态权限菜单,按钮级别权限控制,Redis 缓存提升性能
... ... @@ -59,6 +59,22 @@
59 59 * 集成阿里云、腾讯云等短信渠道,集成 MinIO、阿里云、腾讯云、七牛云等云存储服务
60 60 * 集成报表设计器、大屏设计器,通过拖拽即可生成酷炫的报表与大屏
61 61  
  62 +## 🐲 信创兼容与数据库支持
  63 +
  64 +支持国产操作系统、数据库和中间件,满足信创环境的部署需要:
  65 +
  66 +| 类别 | 支持 | 说明 |
  67 +|----------|-----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
  68 +| 国产操作系统 | 银河麒麟、统信 UOS 等 Linux 系统 | 参考 [《Linux 部署》](https://doc.iocoder.cn/deployment-linux/),选择与 CPU 架构匹配的 JDK |
  69 +| 关系型数据库 | MySQL、MariaDB、Oracle、PostgreSQL、SQL Server | 默认使用 MySQL |
  70 +| 国产关系型数据库 | 达梦 DM、人大金仓 KingbaseES、openGauss、瀚高 HighGo、OceanBase、TiDB | 参考 [《国产信创数据库》](https://doc.iocoder.cn/xinchuang-db/) |
  71 +| 国产缓存 | 东方通 TongRDS、宝兰德 BES CacheDB | 替换 Redis,参考 [《国产信创数据库》](https://doc.iocoder.cn/xinchuang-db/)。BES CacheDB 提供接入说明,待介质验证 |
  72 +| 国产应用服务器 | 东方通 TongWeb、宝兰德 BES AppServer | 替换 Tomcat,参考 [《东方通服务器部署》](https://doc.iocoder.cn/deployment-tong/)、[《宝兰德服务器部署》](https://doc.iocoder.cn/deployment-bes/)。BES AppServer 提供接入说明,待介质验证 |
  73 +| 国产 Web 服务器 | 东方通 TongHttpServer | 替换 Nginx,提供静态资源、反向代理,参考 [《东方通服务器部署》](https://doc.iocoder.cn/deployment-tong/) |
  74 +
  75 +* 数据库脚本:[`sql`](sql/) 目录提供 MySQL、Oracle、PostgreSQL、SQL Server、达梦、人大金仓、openGauss、瀚高、OceanBase 的项目初始化脚本(含 Quartz),优先使用对应目录;MariaDB、TiDB 直接使用 MySQL 脚本
  76 +* 其它数据库、后续表结构变更,可参考 [`sql/tools`](sql/tools/README.md) 的转换工具及 [《表结构变更(版本升级)》](https://doc.iocoder.cn/sql-update/#_3-数据库转换),转换后仍需按实际数据库调整、测试
  77 +
62 78 ## 🐳 项目关系
63 79  
64 80 ![架构演进](/.image/common/yudao-roadmap.png)
... ...
sql/tools/README.md
... ... @@ -109,9 +109,17 @@ docker tag <image>:<tag> highgo:local
109 109 docker compose up -d highgo
110 110 ```
111 111  
112   -> 注意:不同瀚高镜像的数据目录可能不同,如果容器无法启动,请按镜像实际 `PGDATA` 修改 `docker-compose.yaml` 中的 `highgo` 数据卷挂载目录。
  112 +> 注意:`highgo` 服务属于 `highgo` profile,执行 `docker compose up -d` 时不会启动,需要像上面一样指定服务名。不同瀚高镜像的数据目录可能不同,如果容器无法启动,请按镜像实际 `PGDATA` 修改 `docker-compose.yaml` 中的 `highgo` 数据卷挂载目录。
113 113  
114   -③ 启动完成后,需要手动导入 Quartz 和项目 SQL。瀚高兼容 PostgreSQL,具体客户端命令以当前镜像为准,可使用 `psql` 或瀚高镜像内置的兼容客户端执行 `/tmp/quartz.sql`、`/tmp/schema.sql`。
  114 +③ 初始化数据库:使用 `postgres:14.2` 镜像中的 `psql` 客户端,创建 `ruoyi-vue-pro` 数据库,并依次导入 `sql/highgo/quartz.sql`、`sql/highgo/ruoyi-vue-pro.sql`:
  115 +
  116 +```Bash
  117 +HIGHGO_PASSWORD='数据库密码' docker compose run --rm highgo-init
  118 +```
  119 +
  120 +> 注意:默认使用瀚高的 `highgo` 账号连接 `highgo` 数据库执行建库,可以通过 `HIGHGO_USER`、`HIGHGO_MAINTENANCE_DB` 环境变量修改;密码以镜像或安装时的实际设置为准,脚本不会修改数据库的认证配置。初始化只允许创建全新的数据库:数据库已存在(包括空库)或重复执行时,脚本拒绝导入并返回非零状态;导入失败时保留数据库便于排查。该入口只适用于 PostgreSQL 客户端支持的认证方式(例如 md5、scram-sha-256),数据库开启 SM3 等专有认证时,请使用瀚高客户端手动执行上述两个 SQL 文件。
  121 +
  122 +④ 项目可以使用 PostgreSQL JDBC 驱动(`jdbc:postgresql://127.0.0.1:5866/ruoyi-vue-pro`),也可以使用瀚高 JDBC 驱动(`jdbc:highgo://127.0.0.1:5866/ruoyi-vue-pro`),两者都按 PostgreSQL 处理序列主键、Quartz 等差异,`url` 需要与 `driver-class-name` 配对。驱动需要引入到 `yudao-server/pom.xml` 的 `<dependencies>` 中:`yudao-spring-boot-starter-mybatis` 中的 `org.postgresql:postgresql` 是 `optional` 依赖,只在 `yudao-dependencies` 中声明版本也不会引入 jar 包。详见 [《国产信创数据库》](https://doc.iocoder.cn/xinchuang-db/#_4-highgo-瀚高数据库) 文档。
115 123  
116 124 ### 1.9 OceanBase
117 125  
... ...
sql/tools/docker-compose.yaml
... ... @@ -142,6 +142,7 @@ services:
142 142 # 使用瀚高官方提供的 Docker 镜像,加载后打成本地标签:
143 143 # docker tag <image>:<tag> highgo:local
144 144 image: highgo:local
  145 + profiles: ["highgo"]
145 146 restart: unless-stopped
146 147 ports:
147 148 - "5866:5866"
... ... @@ -150,6 +151,26 @@ services:
150 151 - ../highgo/quartz.sql:/tmp/quartz.sql:ro
151 152 - ../highgo/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
152 153  
  154 + # 瀚高初始化:使用 PostgreSQL 客户端创建 ruoyi-vue-pro 数据库并导入 sql/highgo 脚本,只允许创建全新数据库
  155 + # 只支持 PostgreSQL 客户端可用的认证方式(例如 md5、scram-sha-256),SM3 等专有认证请使用瀚高客户端手动导入
  156 + # 执行:HIGHGO_PASSWORD='数据库密码' docker compose run --rm highgo-init
  157 + highgo-init:
  158 + image: postgres:14.2
  159 + profiles: ["highgo-init"]
  160 + restart: "no"
  161 + environment:
  162 + HIGHGO_HOST: ${HIGHGO_HOST:-highgo}
  163 + HIGHGO_PORT: ${HIGHGO_PORT:-5866}
  164 + HIGHGO_USER: ${HIGHGO_USER:-highgo}
  165 + HIGHGO_PASSWORD: ${HIGHGO_PASSWORD:-}
  166 + HIGHGO_MAINTENANCE_DB: ${HIGHGO_MAINTENANCE_DB:-highgo}
  167 + HIGHGO_DATABASE: ruoyi-vue-pro
  168 + entrypoint: ["bash", "/tmp/create_schema.sh"]
  169 + volumes:
  170 + - ../highgo/quartz.sql:/tmp/quartz.sql:ro
  171 + - ../highgo/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
  172 + - ./highgo/create_schema.sh:/tmp/create_schema.sh:ro
  173 +
153 174 oceanbase:
154 175 image: oceanbase/oceanbase-ce:4.4.2-lts
155 176 restart: unless-stopped
... ...
sql/tools/highgo/create_schema.sh 0 → 100644
  1 +#!/bin/bash
  2 +# 初始化瀚高测试库:使用 PostgreSQL 客户端 psql 创建全新的数据库并导入 sql/highgo 脚本,数据库已存在(包括空库)时拒绝导入;任一步骤失败都以非零状态退出
  3 +# 只支持 PostgreSQL 客户端可用的认证方式(例如 md5、scram-sha-256);数据库开启 SM3 等瀚高专有认证时,请使用瀚高客户端手动导入
  4 +set -euo pipefail
  5 +
  6 +host="${HIGHGO_HOST:-highgo}"
  7 +port="${HIGHGO_PORT:-5866}"
  8 +user="${HIGHGO_USER:-highgo}"
  9 +maintenance_db="${HIGHGO_MAINTENANCE_DB:-highgo}"
  10 +database="${HIGHGO_DATABASE:-ruoyi-vue-pro}"
  11 +export PGPASSWORD="${HIGHGO_PASSWORD:?HIGHGO_PASSWORD 未设置,请设置为瀚高数据库账号的实际密码}"
  12 +export PGCONNECT_TIMEOUT=5
  13 +export PGOPTIONS="-c client_min_messages=warning"
  14 +
  15 +if [[ ! "${database}" =~ ^[A-Za-z0-9_-]+$ ]]; then
  16 + echo "数据库名 ${database} 不合法,只允许字母、数字、下划线和中划线" >&2
  17 + exit 1
  18 +fi
  19 +
  20 +client=(psql -h "${host}" -p "${port}" -U "${user}" -X -q -v ON_ERROR_STOP=1)
  21 +
  22 +# 1. 等待瀚高可连接
  23 +for i in $(seq 1 60); do
  24 + if pg_isready -h "${host}" -p "${port}" -U "${user}" -d "${maintenance_db}" -q; then
  25 + break
  26 + fi
  27 + if [ "${i}" -eq 60 ]; then
  28 + echo "等待瀚高 ${host}:${port} 超时,请执行 docker compose logs highgo 检查启动日志" >&2
  29 + exit 1
  30 + fi
  31 + sleep 5
  32 +done
  33 +
  34 +# 2. 校验账号、密码及认证方式
  35 +if ! "${client[@]}" -d "${maintenance_db}" -c 'SELECT 1' >/dev/null; then
  36 + echo "无法使用账号 ${user} 登录 ${maintenance_db} 数据库:请检查 HIGHGO_USER、HIGHGO_PASSWORD、HIGHGO_MAINTENANCE_DB;如果数据库使用 SM3 等专有认证,请使用瀚高客户端手动导入" >&2
  37 + exit 1
  38 +fi
  39 +
  40 +# 3. 创建全新数据库:不判断是否存在,数据库已存在时直接失败,避免项目 SQL 中的 DROP TABLE 重建已有的同名表
  41 +if ! "${client[@]}" -d "${maintenance_db}" -c "CREATE DATABASE \"${database}\";"; then
  42 + echo "创建数据库 ${database} 失败:只允许初始化全新的数据库,数据库已存在(包括空库)时拒绝导入;如需重新初始化,请删除瀚高数据卷后重建" >&2
  43 + exit 1
  44 +fi
  45 +
  46 +# 4. 按 quartz、ruoyi-vue-pro 的顺序导入脚本(ON_ERROR_STOP 遇到错误立即退出,失败时保留数据库,便于排查)
  47 +"${client[@]}" -d "${database}" -f /tmp/quartz.sql
  48 +"${client[@]}" -d "${database}" -f /tmp/schema.sql
  49 +
  50 +# 注意:瀚高的 information_schema.tables 对同名表可能返回多行,这里直接查 pg_class(每张表一行),只统计普通表、分区表
  51 +table_count=$("${client[@]}" -d "${database}" -At -c "SELECT COUNT(*) FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = current_schema() AND c.relkind IN ('r', 'p');")
  52 +echo "瀚高初始化完成:数据库 ${database} 共 ${table_count} 张表"
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/JdbcUtils.java
... ... @@ -38,11 +38,14 @@ public class JdbcUtils {
38 38 /**
39 39 * 获得 URL 对应的 DB 类型
40 40 *
  41 + * 特殊:瀚高 HighGo 原生驱动(jdbc:highgo:)兼容 PostgreSQL,按 PostgreSQL 处理,复用其序列主键、Quartz 等适配
  42 + *
41 43 * @param url URL
42 44 * @return DB 类型
43 45 */
44 46 public static DbType getDbType(String url) {
45   - return com.baomidou.mybatisplus.extension.toolkit.JdbcUtils.getDbType(url);
  47 + DbType dbType = com.baomidou.mybatisplus.extension.toolkit.JdbcUtils.getDbType(url);
  48 + return dbType == DbType.HIGH_GO ? DbType.POSTGRE_SQL : dbType;
46 49 }
47 50  
48 51 /**
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessorTest.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.config;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
  4 +import com.baomidou.mybatisplus.annotation.DbType;
  5 +import com.baomidou.mybatisplus.extension.incrementer.PostgreKeyGenerator;
  6 +import org.junit.jupiter.api.AfterEach;
  7 +import org.junit.jupiter.api.BeforeEach;
  8 +import org.junit.jupiter.api.Test;
  9 +import org.springframework.core.env.MapPropertySource;
  10 +import org.springframework.core.env.StandardEnvironment;
  11 +
  12 +import java.util.HashMap;
  13 +import java.util.Map;
  14 +
  15 +import static org.junit.jupiter.api.Assertions.assertEquals;
  16 +import static org.junit.jupiter.api.Assertions.assertInstanceOf;
  17 +import static org.junit.jupiter.api.Assertions.assertNull;
  18 +
  19 +/**
  20 + * {@link IdTypeEnvironmentPostProcessor} 的单元测试
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +public class IdTypeEnvironmentPostProcessorTest {
  25 +
  26 + private static final String ID_TYPE_KEY = "mybatis-plus.global-config.db-config.id-type";
  27 + private static final String QUARTZ_DELEGATE_KEY = "spring.quartz.properties.org.quartz.jobStore.driverDelegateClass";
  28 + private static final String POSTGRESQL_DELEGATE = "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate";
  29 +
  30 + private final IdTypeEnvironmentPostProcessor processor = new IdTypeEnvironmentPostProcessor();
  31 +
  32 + @BeforeEach
  33 + @AfterEach
  34 + public void clearQuartzDelegate() {
  35 + // Quartz Delegate 写入的是 JVM 系统属性,需要清理,避免影响其它用例
  36 + System.clearProperty(QUARTZ_DELEGATE_KEY);
  37 + }
  38 +
  39 + @Test
  40 + public void testPostProcessEnvironment_highGoNativeUrl() {
  41 + // 准备参数
  42 + StandardEnvironment environment = buildEnvironment("jdbc:highgo://127.0.0.1:5866/ruoyi-vue-pro", null);
  43 +
  44 + // 调用
  45 + processor.postProcessEnvironment(environment, null);
  46 +
  47 + // 断言:与 PostgreSQL 一致,使用序列主键及 PostgreSQLDelegate
  48 + assertEquals("INPUT", environment.getProperty(ID_TYPE_KEY));
  49 + assertEquals(POSTGRESQL_DELEGATE, environment.getProperty(QUARTZ_DELEGATE_KEY));
  50 + assertInstanceOf(PostgreKeyGenerator.class, new YudaoMybatisAutoConfiguration().keyGenerator(environment));
  51 + }
  52 +
  53 + @Test
  54 + public void testPostProcessEnvironment_postgresqlUrl() {
  55 + // 准备参数
  56 + StandardEnvironment environment = buildEnvironment("jdbc:postgresql://127.0.0.1:5866/ruoyi-vue-pro", null);
  57 +
  58 + // 调用
  59 + processor.postProcessEnvironment(environment, null);
  60 +
  61 + // 断言
  62 + assertEquals("INPUT", environment.getProperty(ID_TYPE_KEY));
  63 + assertEquals(POSTGRESQL_DELEGATE, environment.getProperty(QUARTZ_DELEGATE_KEY));
  64 + assertInstanceOf(PostgreKeyGenerator.class, new YudaoMybatisAutoConfiguration().keyGenerator(environment));
  65 + }
  66 +
  67 + @Test
  68 + public void testPostProcessEnvironment_mysqlUrl() {
  69 + // 准备参数
  70 + StandardEnvironment environment = buildEnvironment("jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro", null);
  71 +
  72 + // 调用
  73 + processor.postProcessEnvironment(environment, null);
  74 +
  75 + // 断言:MySQL 保持自增主键,且不设置 Quartz Delegate
  76 + assertEquals("AUTO", environment.getProperty(ID_TYPE_KEY));
  77 + assertNull(environment.getProperty(QUARTZ_DELEGATE_KEY));
  78 + }
  79 +
  80 + @Test
  81 + public void testPostProcessEnvironment_highGoNativeUrlWithExplicitConfig() {
  82 + // 准备参数:用户显式配置主键策略及 Quartz Delegate
  83 + Map<String, Object> explicit = new HashMap<>();
  84 + explicit.put(ID_TYPE_KEY, "ASSIGN_ID");
  85 + explicit.put(QUARTZ_DELEGATE_KEY, "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
  86 + StandardEnvironment environment = buildEnvironment("jdbc:highgo://127.0.0.1:5866/ruoyi-vue-pro", explicit);
  87 +
  88 + // 调用
  89 + processor.postProcessEnvironment(environment, null);
  90 +
  91 + // 断言:不覆盖用户配置
  92 + assertEquals("ASSIGN_ID", environment.getProperty(ID_TYPE_KEY));
  93 + assertEquals("org.quartz.impl.jdbcjobstore.StdJDBCDelegate", environment.getProperty(QUARTZ_DELEGATE_KEY));
  94 + assertNull(System.getProperty(QUARTZ_DELEGATE_KEY));
  95 + }
  96 +
  97 + @Test
  98 + public void testGetDbType_onlyNormalizeHighGo() {
  99 + assertEquals(DbType.POSTGRE_SQL, JdbcUtils.getDbType("jdbc:highgo://127.0.0.1:5866/ruoyi-vue-pro"));
  100 + assertEquals(DbType.POSTGRE_SQL, JdbcUtils.getDbType("jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro"));
  101 + assertEquals(DbType.MYSQL, JdbcUtils.getDbType("jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro"));
  102 + assertEquals(DbType.KINGBASE_ES, JdbcUtils.getDbType("jdbc:kingbase8://127.0.0.1:54321/test"));
  103 + assertEquals(DbType.DM, JdbcUtils.getDbType("jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO"));
  104 + }
  105 +
  106 + private static StandardEnvironment buildEnvironment(String url, Map<String, Object> extra) {
  107 + Map<String, Object> properties = new HashMap<>();
  108 + properties.put("spring.datasource.dynamic.primary", "master");
  109 + properties.put("spring.datasource.dynamic.datasource.master.url", url);
  110 + properties.put(ID_TYPE_KEY, "NONE");
  111 + if (extra != null) {
  112 + properties.putAll(extra);
  113 + }
  114 + StandardEnvironment environment = new StandardEnvironment();
  115 + environment.getPropertySources().addLast(new MapPropertySource("test", properties));
  116 + return environment;
  117 + }
  118 +
  119 +}
... ...