fix(FEAT-DS-001): 修正MySQL健康检查会话时区
This commit is contained in:
@@ -18,7 +18,7 @@ V1 采用 AKTools 外部服务直连,不新增 Python 服务和 Docker;暂
|
|||||||
|
|
||||||
## 修复缺陷
|
## 修复缺陷
|
||||||
|
|
||||||
修复了短线情绪面板缺失导致的前端测试导入失败,并补齐页面未消费短线结论响应的问题;同时修复页面接线遗漏造成的 `shortTermConclusion` 未定义告警。未发现 TDX 回归。
|
修复了短线情绪面板缺失导致的前端测试导入失败,并补齐页面未消费短线结论响应的问题;同时修复页面接线遗漏造成的 `shortTermConclusion` 未定义告警。补充修复 MySQL 健康检查因 `mysql2` 的 `timezone` 选项不会设置服务端会话时区而误判不可用的问题:健康检查现在在同一连接上显式执行 `SET SESSION time_zone = '+08:00'`,完成元数据校验后始终释放连接。未发现 TDX 回归。
|
||||||
|
|
||||||
## 未完成与延期项
|
## 未完成与延期项
|
||||||
|
|
||||||
@@ -30,4 +30,4 @@ V1 采用 AKTools 外部服务直连,不新增 Python 服务和 Docker;暂
|
|||||||
|
|
||||||
## 验证证据
|
## 验证证据
|
||||||
|
|
||||||
Schema 基线与校验通过(`v0.0.10 captured_empty`);开发前 GitNexus 风险 LOW,页面改动前对 `useMarketOverview` 影响分析为 LOW;后端 15 个测试文件 79 个测试通过,前端 8 个测试文件 23 个测试通过,Web typecheck 通过,浏览器桌面/390×844 视觉检查通过且 `scrollWidth=innerWidth`。安全边界包括回环地址白名单、固定接口、超时、响应体上限、JSON 校验和错误脱敏;三个上游请求并行,单请求超时 10 秒,当前性能证据为本机定向测试,不代表生产容量。
|
Schema 基线与校验通过(`v0.0.10 captured_empty`);开发前 GitNexus 风险 LOW,页面改动前对 `useMarketOverview` 影响分析为 LOW;后端 15 个测试文件 80 个测试通过,前端 8 个测试文件 23 个测试通过,Web typecheck 通过,`pnpm verify` 全量门禁通过且 Playwright 4 个测试通过。健康接口真实验证返回 HTTP 200,MySQL 为 5.7.42 / `a_share_analysis_dev` / `+08:00`。安全边界包括回环地址白名单、固定接口、超时、响应体上限、JSON 校验和错误脱敏;三个上游请求并行,单请求超时 10 秒,MySQL 健康查询使用连接级 finally 释放,当前性能证据为本机定向测试,不代表生产容量。
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ Status: up-to-date
|
|||||||
|
|
||||||
## 开发后影响
|
## 开发后影响
|
||||||
|
|
||||||
新增 AKTools Adapter、短线 Service/Controller、OpenAPI、Web API、Composable 和页面组件形成独立短线执行流;既有 TDX 概览执行流未被改写。`loadApiConfig` 上游影响为 LOW,直接消费者仅配置测试;`useMarketOverview` 页面影响为 LOW。数据库、Redis、RocketMQ、Scheduler 和 Worker 没有新增业务执行流。
|
新增 AKTools Adapter、短线 Service/Controller、OpenAPI、Web API、Composable 和页面组件形成独立短线执行流;既有 TDX 概览执行流未被改写。`loadApiConfig` 上游影响为 LOW,直接消费者仅配置测试;`useMarketOverview` 页面影响为 LOW。数据库、Redis、RocketMQ、Scheduler 和 Worker 没有新增业务执行流。本轮 MySQL 健康检查修复的暂存区检测识别 `UseFactory → MysqlHealthClient` 与 `OnModuleDestroy → End` 两条连接生命周期流,工具风险 MEDIUM;逐项复核后确认仅是连接池获取/释放行为变化。
|
||||||
|
|
||||||
## 变更检测
|
## 变更检测
|
||||||
|
|
||||||
格式化提交后已重新索引,`gitnexus_detect_changes({ scope: "all" })` 仅在提交前短暂识别格式化触及的既有短线符号;最终追溯提交 `358004f` 后索引与提交一致,工作区无本功能差异,仅保留用户未纳入本功能的未跟踪 CSV 文件。
|
格式化提交后已重新索引,`gitnexus_detect_changes({ scope: "all" })` 仅在提交前短暂识别格式化触及的既有短线符号;本轮暂存区检测结果已记录在 `detected-changes.md`,提交后将再次刷新索引并核对连接生命周期变更,工作区仅保留用户未纳入本功能的未跟踪 CSV 文件。
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# GitNexus 前后对比
|
# GitNexus 前后对比
|
||||||
|
|
||||||
开发前基线已记录在 `before.md`。开发后 GitNexus 重新索引确认新增 AKTools Adapter、短线 Service、Controller、Web API、配置、页面和文档均落在计划范围;现有 TDX 市场概览执行流未被改写。`useMarketOverview` 的直接消费者为页面和测试,影响风险 LOW;未发现数据库、Redis、RocketMQ、Scheduler 或 Worker 新执行流。
|
开发前基线已记录在 `before.md`。开发后 GitNexus 重新索引确认新增 AKTools Adapter、短线 Service、Controller、Web API、配置、页面和文档均落在计划范围;现有 TDX 市场概览执行流未被改写。`useMarketOverview` 的直接消费者为页面和测试,影响风险 LOW;未发现数据库、Redis、RocketMQ、Scheduler 或 Worker 新执行流。本轮健康检查修复前后对比增加连接级会话设置和 finally 释放,暂存区工具风险 MEDIUM 仅来自连接生命周期执行流,未形成新的跨模块业务依赖。
|
||||||
|
|||||||
@@ -6,4 +6,6 @@
|
|||||||
- 首次提交后:`gitnexus_detect_changes({ scope: "all" })` 返回无变更,索引与提交 `6e8a051` 一致;格式化提交 `1398804` 前的 staged 检测仅触及同一短线执行流,已由全量测试复验。
|
- 首次提交后:`gitnexus_detect_changes({ scope: "all" })` 返回无变更,索引与提交 `6e8a051` 一致;格式化提交 `1398804` 前的 staged 检测仅触及同一短线执行流,已由全量测试复验。
|
||||||
- 当前:索引与提交 `1398804` 一致,工作区本功能无未提交差异。
|
- 当前:索引与提交 `1398804` 一致,工作区本功能无未提交差异。
|
||||||
|
|
||||||
复核结论:TDX 核心链路保持独立;没有新增数据库、Redis、RocketMQ、Scheduler 或 Worker 业务执行流。工具报告中的高风险结果已记录并完成逐项 `context/impact` 复核。
|
- 本轮健康检查修复暂存区检测:6 个文件、28 个符号、2 条执行流,工具风险为 MEDIUM;实际代码影响集中在 `createMysqlHealthClient`、`MysqlHealthClient` 及 `OnModuleDestroy → End` 连接生命周期,文档章节被工具按标题识别为符号。直接 upstream impact 均为 LOW,未发现业务模块、Web、消息、数据库 Schema 或数据源执行流影响。
|
||||||
|
|
||||||
|
复核结论:TDX 核心链路保持独立;没有新增数据库、Redis、RocketMQ、Scheduler 或 Worker 业务执行流。工具报告中的 MEDIUM 结果已记录并完成逐项 `context/impact` 复核,原因是连接关闭生命周期被纳入执行流,而非跨模块业务耦合。
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 当前状态
|
## 当前状态
|
||||||
|
|
||||||
已完成代码验证:后端 Vitest 15 个测试文件、79 个测试通过,包含 AKTools Client/Mapper、短线 Service、HTTP E2E 和 OpenAPI;前端 Vitest 8 个测试文件、23 个测试通过,包含短线面板、Composable 和 API;Web typecheck 通过。AKTools 本机涨停池、炸板池和昨日涨停池冒烟均返回 HTTP 200;`stock_zh_a_spot_em` 返回 HTTP 500,未纳入 V1。
|
已完成代码验证:后端 Vitest 15 个测试文件、80 个测试通过,包含 MySQL 健康检查连接设置/释放回归、AKTools Client/Mapper、短线 Service、HTTP E2E 和 OpenAPI;前端 Vitest 8 个测试文件、23 个测试通过,包含短线面板、Composable 和 API;Web typecheck 通过。`pnpm verify` 全量门禁通过,Playwright 4 个测试通过。AKTools 本机涨停池、炸板池和昨日涨停池冒烟均返回 HTTP 200;`stock_zh_a_spot_em` 返回 HTTP 500,未纳入 V1。
|
||||||
|
|
||||||
## 安全验证
|
## 安全验证
|
||||||
|
|
||||||
@@ -10,4 +10,4 @@
|
|||||||
|
|
||||||
## 性能验证
|
## 性能验证
|
||||||
|
|
||||||
Service 测试验证三个请求使用 `Promise.allSettled` 并行、单个失败返回 `partial`、全部失败返回 `unavailable`。单请求超时配置为 10 秒;当前仅有本机测试证据,不将其视为生产容量结论。
|
Service 测试验证三个请求使用 `Promise.allSettled` 并行、单个失败返回 `partial`、全部失败返回 `unavailable`;MySQL 健康检查验证会话时区设置、元数据查询和异常路径释放连接。单请求超时配置为 10 秒;当前仅有本机测试证据,不将其视为生产容量结论。
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
## 发现
|
## 发现
|
||||||
|
|
||||||
已完成实现 Review:未发现阻断项。Client 只允许三个固定 AKTools 路径,Service 用 `Promise.allSettled` 做模块级降级,DTO 不透传中文原始字段;Web 只通过 Axios API 文件访问 Nest。发现页面接线遗漏 `shortTermConclusion` 未解构,已在测试失败后修复。
|
已完成实现 Review:未发现阻断项。Client 只允许三个固定 AKTools 路径,Service 用 `Promise.allSettled` 做模块级降级,DTO 不透传中文原始字段;Web 只通过 Axios API 文件访问 Nest。发现页面接线遗漏 `shortTermConclusion` 未解构,已在测试失败后修复。后续健康检查复核发现 `mysql2` 的 `timezone` 选项不会改变服务端会话变量,已在同一连接上显式设置 `+08:00` 并用 `finally` 释放;成功顺序和查询失败释放均有回归测试。
|
||||||
|
|
||||||
## 测试缺口
|
## 测试缺口
|
||||||
|
|
||||||
上游 5xx、超时、部分接口失败、字段非法、响应大小、不可用状态和窄屏布局已有定向覆盖;交易日期延迟口径和 AKShare 字段漂移仍需后续真实交易日联调。
|
上游 5xx、超时、部分接口失败、字段非法、响应大小、不可用状态、MySQL 会话时区/释放和窄屏布局已有定向覆盖;交易日期延迟口径和 AKShare 字段漂移仍需后续真实交易日联调。
|
||||||
|
|
||||||
## 安全与残余风险
|
## 安全与残余风险
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
## 性能与资源影响
|
## 性能与资源影响
|
||||||
|
|
||||||
三个请求并行,10 秒超时,2 MiB 响应上限,不引入缓存和消息链路;测试证据仅证明行为和边界,不构成生产容量证明。
|
三个请求并行,10 秒超时,2 MiB 响应上限;MySQL 健康检查复用单个连接完成会话设置和元数据查询,并在成功或异常路径释放连接;不引入缓存和消息链路,测试证据仅证明行为和边界,不构成生产容量证明。
|
||||||
|
|
||||||
## GitNexus 与同步修改
|
## GitNexus 与同步修改
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
## 修复与复审
|
## 修复与复审
|
||||||
|
|
||||||
发现问题后记录修复提交和复审证据。
|
发现问题后记录修复提交和复审证据;本轮健康检查修复通过 80 项后端测试、全量门禁和真实 `/api/v1/health` HTTP 200 验证。
|
||||||
|
|
||||||
## 最终结论
|
## 最终结论
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
## 不符合项与处置
|
## 不符合项与处置
|
||||||
|
|
||||||
逐项检查通过:功能档案、中文注释、OpenAPI 生成契约、Axios API 边界、固定数据源 Adapter、模块级降级、环境模板和未提交凭据约束均符合规范。实现期间发现的页面接线遗漏已修复并由前端全量测试复验。
|
逐项检查通过:功能档案、中文注释、OpenAPI 生成契约、Axios API 边界、固定数据源 Adapter、模块级降级、环境模板和未提交凭据约束均符合规范。实现期间发现的页面接线遗漏已修复并由前端全量测试复验;MySQL 健康检查的服务端时区误判已通过显式会话设置、连接释放 finally 和回归测试修复。
|
||||||
|
|
||||||
## 安全、性能与架构检查
|
## 安全、性能与架构检查
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
## GitNexus 与开发总结检查
|
## GitNexus 与开发总结检查
|
||||||
|
|
||||||
开发前后 GitNexus 报告、影响计划、变更检测、开发总结、测试结果、视觉 QA、Review 和回退说明均已补齐;`governance:finish` 及最终提交前再次执行变更检测。
|
开发前后 GitNexus 报告、影响计划、变更检测、开发总结、测试结果、视觉 QA、Review 和回退说明均已补齐;本轮修复的 GitNexus 影响分析为 LOW,仅影响健康客户端和 API 工厂直接调用;`governance:finish` 及最终提交前再次执行变更检测。
|
||||||
|
|
||||||
## 最终结论
|
## 最终结论
|
||||||
|
|
||||||
|
|||||||
@@ -24,23 +24,32 @@ describe('基础设施只读健康客户端', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('MySQL 使用固定元数据查询并返回实际 5.7.x 版本', async () => {
|
it('MySQL 使用固定元数据查询并返回实际 5.7.x 版本', async () => {
|
||||||
const queries: string[] = [];
|
const operations: string[] = [];
|
||||||
const client = new MysqlHealthClient(
|
const client = new MysqlHealthClient(
|
||||||
{
|
{
|
||||||
query: async (sql: string) => {
|
getConnection: async () => {
|
||||||
queries.push(sql);
|
operations.push('GET_CONNECTION');
|
||||||
return [
|
return {
|
||||||
[
|
query: async (sql: string) => {
|
||||||
{
|
operations.push(sql);
|
||||||
version: '5.7.41-log',
|
if (sql === "SET SESSION time_zone = '+08:00'") return [[], []];
|
||||||
databaseName: 'a_share_analysis_dev',
|
return [
|
||||||
charset: 'utf8mb4',
|
[
|
||||||
collation: 'utf8mb4_general_ci',
|
{
|
||||||
timezone: '+08:00',
|
version: '5.7.41-log',
|
||||||
},
|
databaseName: 'a_share_analysis_dev',
|
||||||
],
|
charset: 'utf8mb4',
|
||||||
[],
|
collation: 'utf8mb4_general_ci',
|
||||||
];
|
timezone: '+08:00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
release: () => {
|
||||||
|
operations.push('RELEASE');
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
end: async () => undefined,
|
end: async () => undefined,
|
||||||
},
|
},
|
||||||
@@ -51,26 +60,60 @@ describe('基础设施只读健康客户端', () => {
|
|||||||
status: 'healthy',
|
status: 'healthy',
|
||||||
detail: 'MySQL 5.7.41 / a_share_analysis_dev / +08:00',
|
detail: 'MySQL 5.7.41 / a_share_analysis_dev / +08:00',
|
||||||
});
|
});
|
||||||
expect(queries).toEqual([MYSQL_HEALTH_QUERY]);
|
expect(operations).toEqual([
|
||||||
|
'GET_CONNECTION',
|
||||||
|
"SET SESSION time_zone = '+08:00'",
|
||||||
|
MYSQL_HEALTH_QUERY,
|
||||||
|
'RELEASE',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MySQL 查询失败时仍释放当前连接', async () => {
|
||||||
|
const operations: string[] = [];
|
||||||
|
const client = new MysqlHealthClient(
|
||||||
|
{
|
||||||
|
getConnection: async () => ({
|
||||||
|
query: async (sql: string) => {
|
||||||
|
operations.push(sql);
|
||||||
|
if (sql === MYSQL_HEALTH_QUERY) throw new Error('query failed');
|
||||||
|
return [[], []];
|
||||||
|
},
|
||||||
|
release: () => {
|
||||||
|
operations.push('RELEASE');
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
end: async () => undefined,
|
||||||
|
},
|
||||||
|
'a_share_analysis_dev',
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(client.check()).rejects.toThrow('query failed');
|
||||||
|
expect(operations).toEqual(["SET SESSION time_zone = '+08:00'", MYSQL_HEALTH_QUERY, 'RELEASE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MySQL 拒绝版本、库名或连接口径漂移', async () => {
|
it('MySQL 拒绝版本、库名或连接口径漂移', async () => {
|
||||||
const createClient = (override: Record<string, string>) =>
|
const createClient = (override: Record<string, string>) =>
|
||||||
new MysqlHealthClient(
|
new MysqlHealthClient(
|
||||||
{
|
{
|
||||||
query: async () => [
|
getConnection: async () => ({
|
||||||
[
|
query: async (sql: string) =>
|
||||||
{
|
sql === MYSQL_HEALTH_QUERY
|
||||||
version: '5.7.42',
|
? [
|
||||||
databaseName: 'a_share_analysis_dev',
|
[
|
||||||
charset: 'utf8mb4',
|
{
|
||||||
collation: 'utf8mb4_general_ci',
|
version: '5.7.42',
|
||||||
timezone: '+08:00',
|
databaseName: 'a_share_analysis_dev',
|
||||||
...override,
|
charset: 'utf8mb4',
|
||||||
},
|
collation: 'utf8mb4_general_ci',
|
||||||
],
|
timezone: '+08:00',
|
||||||
[],
|
...override,
|
||||||
],
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
]
|
||||||
|
: [[], []],
|
||||||
|
release: () => undefined,
|
||||||
|
}),
|
||||||
end: async () => undefined,
|
end: async () => undefined,
|
||||||
},
|
},
|
||||||
'a_share_analysis_dev',
|
'a_share_analysis_dev',
|
||||||
@@ -92,18 +135,24 @@ describe('基础设施只读健康客户端', () => {
|
|||||||
it('MySQL 按当前配置校验生产库名', async () => {
|
it('MySQL 按当前配置校验生产库名', async () => {
|
||||||
const client = new MysqlHealthClient(
|
const client = new MysqlHealthClient(
|
||||||
{
|
{
|
||||||
query: async () => [
|
getConnection: async () => ({
|
||||||
[
|
query: async (sql: string) =>
|
||||||
{
|
sql === MYSQL_HEALTH_QUERY
|
||||||
version: '5.7.42',
|
? [
|
||||||
databaseName: 'a_share_analysis',
|
[
|
||||||
charset: 'utf8mb4',
|
{
|
||||||
collation: 'utf8mb4_general_ci',
|
version: '5.7.42',
|
||||||
timezone: '+08:00',
|
databaseName: 'a_share_analysis',
|
||||||
},
|
charset: 'utf8mb4',
|
||||||
],
|
collation: 'utf8mb4_general_ci',
|
||||||
[],
|
timezone: '+08:00',
|
||||||
],
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
]
|
||||||
|
: [[], []],
|
||||||
|
release: () => undefined,
|
||||||
|
}),
|
||||||
end: async () => undefined,
|
end: async () => undefined,
|
||||||
},
|
},
|
||||||
'a_share_analysis',
|
'a_share_analysis',
|
||||||
|
|||||||
@@ -12,8 +12,16 @@ export const MYSQL_HEALTH_QUERY = `SELECT
|
|||||||
@@collation_database AS collation,
|
@@collation_database AS collation,
|
||||||
@@session.time_zone AS timezone`;
|
@@session.time_zone AS timezone`;
|
||||||
|
|
||||||
interface MysqlQueryPort {
|
/** mysql2 的 timezone 选项只控制日期转换,因此需显式设置服务端会话时区。 */
|
||||||
|
export const MYSQL_SESSION_TIMEZONE_QUERY = "SET SESSION time_zone = '+08:00'";
|
||||||
|
|
||||||
|
interface MysqlConnectionPort {
|
||||||
query(sql: string): Promise<[unknown, unknown]>;
|
query(sql: string): Promise<[unknown, unknown]>;
|
||||||
|
release(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MysqlPoolPort {
|
||||||
|
getConnection(): Promise<MysqlConnectionPort>;
|
||||||
end(): Promise<void>;
|
end(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,31 +36,37 @@ interface MysqlMetadataRow {
|
|||||||
/** MySQL 健康客户端只校验元数据,不读取表数据或执行写入。 */
|
/** MySQL 健康客户端只校验元数据,不读取表数据或执行写入。 */
|
||||||
export class MysqlHealthClient implements OnModuleDestroy {
|
export class MysqlHealthClient implements OnModuleDestroy {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly client: MysqlQueryPort,
|
private readonly client: MysqlPoolPort,
|
||||||
private readonly expectedDatabase: string,
|
private readonly expectedDatabase: string,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async check(): Promise<DependencyHealthDto> {
|
async check(): Promise<DependencyHealthDto> {
|
||||||
const [rows] = await this.client.query(MYSQL_HEALTH_QUERY);
|
const connection = await this.client.getConnection();
|
||||||
const row = (rows as unknown as MysqlMetadataRow[])[0];
|
try {
|
||||||
if (!row) throw new Error('MySQL 未返回元数据');
|
await connection.query(MYSQL_SESSION_TIMEZONE_QUERY);
|
||||||
const versionMatch = /^(5\.7\.\d+)(?:[-+].*)?$/.exec(row.version ?? '');
|
const [rows] = await connection.query(MYSQL_HEALTH_QUERY);
|
||||||
if (!versionMatch) {
|
const row = (rows as unknown as MysqlMetadataRow[])[0];
|
||||||
throw new Error('MySQL 5.7.x 版本要求未满足');
|
if (!row) throw new Error('MySQL 未返回元数据');
|
||||||
}
|
const versionMatch = /^(5\.7\.\d+)(?:[-+].*)?$/.exec(row.version ?? '');
|
||||||
if (row.databaseName !== this.expectedDatabase) {
|
if (!versionMatch) {
|
||||||
throw new Error(`MySQL 健康检查必须连接 ${this.expectedDatabase}`);
|
throw new Error('MySQL 5.7.x 版本要求未满足');
|
||||||
}
|
}
|
||||||
if (row.charset !== 'utf8mb4') throw new Error('MySQL 字符集必须为 utf8mb4');
|
if (row.databaseName !== this.expectedDatabase) {
|
||||||
if (row.collation !== 'utf8mb4_general_ci') {
|
throw new Error(`MySQL 健康检查必须连接 ${this.expectedDatabase}`);
|
||||||
throw new Error('MySQL 排序规则必须为 utf8mb4_general_ci');
|
}
|
||||||
}
|
if (row.charset !== 'utf8mb4') throw new Error('MySQL 字符集必须为 utf8mb4');
|
||||||
if (row.timezone !== '+08:00') throw new Error('MySQL 会话时区必须为 +08:00');
|
if (row.collation !== 'utf8mb4_general_ci') {
|
||||||
|
throw new Error('MySQL 排序规则必须为 utf8mb4_general_ci');
|
||||||
|
}
|
||||||
|
if (row.timezone !== '+08:00') throw new Error('MySQL 会话时区必须为 +08:00');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: 'healthy',
|
status: 'healthy',
|
||||||
detail: `MySQL ${versionMatch[1]} / ${row.databaseName} / ${row.timezone}`,
|
detail: `MySQL ${versionMatch[1]} / ${row.databaseName} / ${row.timezone}`,
|
||||||
};
|
};
|
||||||
|
} finally {
|
||||||
|
connection.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user