2.4. blade执行脚本集成多发

2.4.1. blade执行脚本集成多发简介

在blade执行测试用例过程中多个信创库执行sql脚本存在以下问题:

  • 1.操作繁琐重复:执行测试用例前,需针对 Oracle、dm、ocean_base_oracle 等不同数据库手动单独执行 SQL 脚本同步数据,存在大量重复劳动。

  • 2.缺乏数据校验机制:没有统一的数据校验机制,无法快速排查不同数据库间的数据差异,影响测试效率和数据一致性保障。。

2.4.2. 优化后的整体流程图:

../_images/arch_ukagent_blade_sql_scripts_forward_2025-11-03_19-22-35.png

2.4.3. 提前在blade配置http脚本

  1. 项目设置-项目连接设置(开通权限),新增http_config类型

  2. 测试执行->测试环境(引用项目连接设置中的http_config),给测试环境添加执行机(执行机,配置执行机授权码,授权码来自项目设置)

  3. 测试脚本(http)引用项目连接的设置

  4. 测试用例中引用测试脚本(http)

  5. 调试测试用例,按照步骤,发起http(依赖执行机)

  6. http api 参考下方接口内容

部分截图如下:

设置项目连接,新增http_config类型

../_images/blade_config_project_connection_2025-11-11_15-57-46.png

设置测试环境,引用项目连接设置中的http_config, 设置http接口,参考下方接口内容,添加执行机

../_images/blade_config_test_environment_2025-11-11_16-04-36.png

测试http脚本引用项目连接的设置

../_images/blade_config_reference_project_connection_2025-11-11_16-06-56.png

2.4.4. 服务部署

  1. 下载并解压uk_agent发布包

  • 访问Hundsun Artifactory制品库指定地址,下载包含uk_agent压缩包,若存在访问问题,需联系制品库维护人员。具体见下方截图:

    ../_images/%E5%88%B6%E5%93%81%E5%BA%93%E4%B8%8B%E8%BD%BDuk_agent_2025-11-27_15-09-00.png
  • 上传uk_agent到服务器(linux环境),将压缩包解压至uk_agent指定工作目录(示例路径:/data/lightdb/xxxxxx/uk_agent),解压后需包含以下核心目录/文件:

    • config:配置目录,含数据源、多发参数等配置文件和待执行的sql脚本,具体参考下面说明

    • start.sh/stop.sh:服务启停脚本

    • uk_agent-25.3.1.jar:服务执行jar包,这里是25.3.1版本

  1. 配置目标库数据源 - 进入uk_agent配置目录:uk_agent/config,编辑数据源配置文件 application.properties,配置所有目标库数据源,示例如下:

# 第一个数据源配置(mysql的一个具体数据库)
agent.datasource.sources[0].name=mysql # 数据库类型,不区分大小写,
agent.datasource.sources[0].alias=mysql_xxxxxx # 具体数据库名称,对应下方请求中参数targetDbs内容
agent.datasource.sources[0].url=jdbc:mysql://10.20.30.199:2306/xxxxxx  # 数据库url
agent.datasource.sources[0].username=lightdb     # 数据库用户名称
agent.datasource.sources[0].password=${password} # 数据库用户密码
agent.datasource.sources[0].driver-class-name=com.mysql.cj.jdbc.Driver # 数据库所需的驱动类,这里是mysql的驱动
agent.datasource.sources[0].initial-size=5  # 连接池初始大小
agent.datasource.sources[0].max-active=20  # 连接池最大活跃数
agent.datasource.sources[0].min-idle=5  # 连接池最小空闲数
agent.datasource.sources[0].max-wait=60000  # 最大等待时间(毫秒)
agent.datasource.sources[0].validation-query=select 1  # 连接校验SQL

# 第二个数据源配置(lightdb_mysql的一个具体数据库)
agent.datasource.sources[1].name=LIGHTDB_MYSQL
agent.datasource.sources[1].alias=lightdb_mysql_yyyyyy
agent.datasource.sources[1].url=jdbc:postgresql://10.20.30.193:5440/yyyyyy?options=-c%20search_path=public,mysql,lt_catalog
agent.datasource.sources[1].username=lightdb
agent.datasource.sources[1].password=${password}
agent.datasource.sources[1].driver-class-name=org.postgresql.Driver
agent.datasource.sources[1].initial-size=5
agent.datasource.sources[1].max-active=20
agent.datasource.sources[1].min-idle=5
agent.datasource.sources[1].max-wait=10000
agent.datasource.sources[1].validation-query=select 1
  1. 配置多发关联参数

  • 为确保服务能正常关联比对服务,需在 config 目录下配置多发参数文件:

#1.  ``config/unisql.conf`` 文件,关键配置:
unisql.lib.full-path=uk_agent/config/unisql.linux.x86_64.so  # 统一SQL动态库路径,需与uk_agent部署环境匹配
unisql.skip=1  # 全局透传开关,按基础配置设置

#2. ``config/jrescloud.properties``文件,关键配置(确保数据可发送到比对服务进行比对):
multi.run.what=2  # 执行多发,按基础配置设置
multi.sendCompareService=1  # 开启数据发送至比对服务
multi.compareServiceUrl=http://10.20.30.199:2043/em  # 比对服务地址
multi.send.change.data.http.url=http://10.20.30.199:2043/em/compare/upload/change/data  # 数据上传URL,与比对服务地址匹配
  1. 需在 config/scripts 目录下,配置sql脚本(基于步骤2中具体数据库名称alias分层存储)

  • 基于具体数据库名称的sql脚本分层目录如下:

config
└── scripts
    ├── lightdb_mysql_yyyyyy
    │   └── xx
    │       └── yy
    │           └── test.sql
    └── mysql_xxxxxx
        └── xx
            └── yy
                └── test.sql
  • 其中一个具体数据库例如mysql_xxxxxx的sql脚本test.sql内容示例:

-- 创建表
CREATE TABLE user_info (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL
);

-- 插入数据
INSERT INTO user_info (name) VALUES ('张三');
INSERT INTO user_info (name) VALUES ('李四');
INSERT INTO user_info (name) VALUES ('王五');

-- 更新数据
UPDATE user_info
SET name = '李小四'
WHERE id = 2;

--查询数据
SELECT id, name
FROM user_info
ORDER BY id ASC
LIMIT 2;

-- 删除数据
DELETE FROM user_info
WHERE id = 3;

-- 删除表
DROP TABLE user_info;
  • oracle系(oracle,dm,gaussdb_oracle,ocean_base_oracle)支持plsql,sql脚本示例如下:

-- 这是文件级单行注释(会被跳过)
prompt 开始执行PL/SQL块(带注释);

-- 场景1:匿名PL/SQL块,含单行注释
begin
    -- 单行注释:声明变量
    declare
v_name varchar2(50); -- 用户名变量(行尾注释)
        v_age number;
begin
        v_name := '张三--测试字符串'; -- 字符串内的--不是注释
        v_age := 30;
        dbms_output.put_line('姓名:' || v_name || ', 年龄:' || v_age);
end; -- 内层end
end;
/

-- 场景2:存储过程(含注释)
create or replace procedure test_procedure(p_id number) is
    v_desc varchar2(100);
begin
select 'ID=' || p_id || '--描述' into v_desc from dual; -- 字符串内的--
dbms_output.put_line('描述:' || v_desc);
exception
    when no_data_found then
        dbms_output.put_line('无数据(注释:异常处理)');
end test_procedure;
/

-- 场景3:普通SQL(带行尾注释)
insert into t_user (name, age) values ('李四', 25); -- 插入测试数据
commit;

2.4.5. 接口说明

2.4.5.1. 简介

http-api,接收测试用例数据同步请求,根据参数动态执行源库及指定目标信创库的脚本,sql执行变化发送到比对服务进行生成比对报告,并返回sql脚本执行结果

2.4.5.2. 接口请求地址

http://ip:port/api/exec/sql

2.4.5.3. 请求方式

POST

2.4.5.4. Content-Type

application/json;charset=UTF-8

2.4.5.5. 接口请求参数

参数名

类型

是否必须

说明

取值范围

示例

scriptName

String

脚本相对目录和名称,如 “xxx/xxx/init.sql”(对应服务端脚本路径的子目录及文件名)

sourceDb

String

源数据库类型,不区分大小写

取值范围[oracle,mysql]

targetDbs

Array<String>

集合中每个元素是数据库的某个具体数据库或者schema的名称,对应数据源配置项agent.datasource.sources[i].alias, 也标识sql脚本目录中不同的数据库

示例[“dm_fund60x”,”ora_test”,”gaussdb_abc”]

2.4.5.6. 接口响应参数

参数名

类型

是否必须

说明

取值范围

示例

status

String

整体执行状态

success:所有库执行成功,partial_success:部分库执行成功,failed:所有库执行失败

traceId

String

标识一次操作,sql脚本在源库和目标库执行

details

Array<DetailInfo>

各数据库执行详情数组,每个元素对应一个库的执行结果

DetailInfo对象结构如下:

参数名

类型

是否必须

说明

示例

name

String

数据库名称,对应的是入参中targetDbs这个数组中的具体数据库或者schema的名字

status

String

该库执行状态:success(成功)、failed(失败)

error

String

可选字段,仅当 status=failed 时返回,描述错误原因(如 “表不存在”“权限不足” 等)

sqlCount

String

执行的sql数量

executeDuration

String

执行sql总耗时,单位毫秒(ms)

failedSqlDetails

DailedSqlDetails

可选对象,执行sql失败,打印行号,sql,失败原因

DailedSqlDetails对象结构如下:

参数名

类型

是否必须

说明

示例

lineNumber

String

sql行号

sql

String

失败SQL内容

error

String

失败原因

executeTime

String

执行耗时(毫秒)

2.4.5.7. 请求和响应示例

请求示例1(成功)

{
  "scriptName": "xx/yy/test.sql",
  "sourceDb": "oracle",
  "targetDbs": [
    "lightdb_mysql_xxxxxx",
    "mysql_xxxxxx"
  ]
}

响应示例1(成功)

{
  "traceId": "uk_agent_b7569552c45945d2b5f2ec2d9607c8b1",
  "status": "success",
  "details": [
        {
          "name": "lightdb_mysql_liangdong30629",
          "status": "success",
          "error": null,
          "sqlCount": 8,
          "executeDuration": 5274,
          "failedSqlDetails": null
        },
        {
          "name": "mysql_liangdong30629",
          "status": "success",
          "error": null,
          "sqlCount": 8,
          "executeDuration": 5289,
          "failedSqlDetails": null
        }
  ]
}

请求示例2(失败)

{
  "scriptName": "xx/yy/test2.sql",
  "sourceDb": "mysql",
  "targetDbs": [
        "lightdb_mysql_liangdong30629",
        "mysql_liangdong30629"
  ]
}

响应示例2(失败原因是脚本不存在)

{
  "traceId": "uk_agent_ca7515c1a676443f938f43d436bfa4bb",
  "status": "failed",
  "details": [
        {
          "name": "lightdb_mysql_liangdong30629",
          "status": "failed",
          "error": "脚本文件不存在:C:\\Users\\T14s\\idea_workspace\\unisql_trunk_develop\\config\\scripts\\lightdb_mysql_liangdong30629\\xx/yy/test2.sql",
          "sqlCount": 0,
          "executeDuration": 0,
          "failedSqlDetails": null
        },
        {
          "name": "mysql_liangdong30629",
          "status": "failed",
          "error": "脚本文件不存在:C:\\Users\\T14s\\idea_workspace\\unisql_trunk_develop\\config\\scripts\\mysql_liangdong30629\\xx/yy/test2.sql",
          "sqlCount": 0,
          "executeDuration": 0,
          "failedSqlDetails": null
        }
  ]
}

请求示例3(失败)

{
  "scriptName": "xx/yy/test.sql",
  "sourceDb": "mysql",
  "targetDbs": [
        "lightdb_mysql_liangdong30629",
        "mysql_liangdong30629"
  ]
}

响应示例3(失败,部分SQL执行失败,可查看失败的sql和失败原因)

{
  "traceId": "uk_agent_a24d43bf64c443c2bdd10660a81edafb",
  "status": "partial_success",
  "details": [
        {
          "name": "lightdb_mysql_liangdong30629",
          "status": "failed",
          "error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
          "sqlCount": 8,
          "executeDuration": 5620,
          "failedSqlDetails": [
                {
                  "lineNumber": 22,
                  "sql": "SELECT id, name\nFROM user_info2\nORDER BY id ASC\nLIMIT 2;",
                  "error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_a24d43bf64c443c2bdd10660a81edafb] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 15:15:27\",\"bizSqlOperationType\":\"SELECT\",\"bizSqlSequence\":6,\"bizSqlTableName\":\"user_info2\",\"compareKind\":\"RAW_BIZ_QUERY\",\"compareReportGenerated\":false,\"errorReason\":\"多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL执行失败,traceId [uk_agent_a24d43bf64c443c2bdd10660a81edafb],业务SQL[SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;],异常信息[com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \\\"user_info2\\\" does not exist\\n  位置:22\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\\r\\n\\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\\r\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\r\\n]\",\"functionId\":\"\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQueryRowCount\":0,\"multiQuerySql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"state\":\"\",\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_a24d43bf64c443c2bdd10660a81edafb\",\"transformedBizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info2\" does not exist\n  位置:22\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
                  "executeTime": 620
                }
          ]
        },
        {
          "name": "mysql_liangdong30629",
          "status": "success",
          "error": null,
          "sqlCount": 8,
          "executeDuration": 5612,
          "failedSqlDetails": null
        }
  ]
}

请求示例4(失败)

{
  "scriptName": "xx/yy/test.sql",
  "sourceDb": "mysql",
  "targetDbs": [
        "lightdb_mysql_liangdong30629",
        "mysql_liangdong30629"
  ]
}

响应示例4(失败,两个数据库均有sql执行失败,可查看失败的sql和失败原因)

{
  "traceId": "uk_agent_3b2caececdf343f99c97f593861e8067",
  "status": "failed",
  "details": [
        {
          "name": "lightdb_mysql_liangdong30629",
          "status": "failed",
          "error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
          "sqlCount": 8,
          "executeDuration": 5576,
          "failedSqlDetails": [
                {
                  "lineNumber": 22,
                  "sql": "SELECT id, name\nFROM user_info2\nORDER BY id ASC\nLIMIT 2;",
                  "error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_3b2caececdf343f99c97f593861e8067] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:06:45\",\"bizSqlOperationType\":\"SELECT\",\"bizSqlSequence\":6,\"bizSqlTableName\":\"user_info2\",\"compareKind\":\"RAW_BIZ_QUERY\",\"compareReportGenerated\":false,\"errorReason\":\"多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL执行失败,traceId [uk_agent_3b2caececdf343f99c97f593861e8067],业务SQL[SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;],异常信息[com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \\\"user_info2\\\" does not exist\\n  位置:22\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\\r\\n\\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\\r\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\r\\n]\",\"functionId\":\"\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQueryRowCount\":0,\"multiQuerySql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"state\":\"BIZ_SQL_EXECUTE_FAIL\",\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_3b2caececdf343f99c97f593861e8067\",\"transformedBizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info2\" does not exist\n  位置:22\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
                  "executeTime": 561
                }
          ]
        },
        {
          "name": "mysql_liangdong30629",
          "status": "failed",
          "error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
          "sqlCount": 8,
          "executeDuration": 5565,
          "failedSqlDetails": [
                {
                  "lineNumber": 31,
                  "sql": "DELETE FROM user_info3\nWHERE id = 3;",
                  "error": "多发源端[MYSQL]到目标[MYSQL]业务SQL串行执行失败,traceId [uk_agent_3b2caececdf343f99c97f593861e8067] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:06:46\",\"bizSqlOperationType\":\"DELETE\",\"bizSqlSequence\":7,\"bizSqlTableName\":\"user_info3\",\"compareKind\":\"AFTER_QUERY\",\"compareReportGenerated\":false,\"functionId\":\"\",\"messageType\":\"http\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQuerySql\":\"SELECT * FROM user_info3 WHERE id=3 ORDER BY id\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":true,\"syncConnectionInfo\":{\"password\":\"lightdb123456\",\"url\":\"jdbc:mysql://10.20.30.199:2306/liangdong30629?sourceDialect=mysql&targetDialect=mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_3b2caececdf343f99c97f593861e8067\",\"transformedBizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [java.sql.SQLSyntaxErrorException: Table 'liangdong30629.user_info3' doesn't exist\r\n\tat com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121)\r\n\tat com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)\r\n\tat com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:770)\r\n\tat com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:653)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:855)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
                  "executeTime": 570
                }
          ]
        }
  ]
}

响应示例5(失败,两个数据库均有sql执行失败,其中一个具体数据库有多个sql执行失败,可查看失败的sql和失败原因)

{
  "traceId": "uk_agent_729ea0c3a9334e1b9da0edb0fabac355",
  "status": "failed",
  "details": [
        {
          "name": "lightdb_mysql_liangdong30629",
          "status": "failed",
          "error": "部分SQL执行失败:总SQL数=8,成功数=6,失败数=2",
          "sqlCount": 8,
          "executeDuration": 5693,
          "failedSqlDetails": [
                {
                  "lineNumber": 22,
                  "sql": "SELECT id, name\nFROM user_info2\nORDER BY id ASC\nLIMIT 2;",
                  "error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:36:33\",\"bizSqlOperationType\":\"SELECT\",\"bizSqlSequence\":6,\"bizSqlTableName\":\"user_info2\",\"compareKind\":\"RAW_BIZ_QUERY\",\"compareReportGenerated\":false,\"errorReason\":\"多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355],业务SQL[SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;],异常信息[com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \\\"user_info2\\\" does not exist\\n  位置:22\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\\r\\n\\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\\r\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\r\\n]\",\"functionId\":\"\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQueryRowCount\":0,\"multiQuerySql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"state\":\"COMPARE_FAIL\",\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_729ea0c3a9334e1b9da0edb0fabac355\",\"transformedBizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info2\" does not exist\n  位置:22\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
                  "executeTime": 624
                },
                {
                  "lineNumber": 31,
                  "sql": "DELETE FROM user_info1\nWHERE id = 3;",
                  "error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"bizSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:36:34\",\"bizSqlOperationType\":\"DELETE\",\"bizSqlSequence\":7,\"bizSqlTableName\":\"user_info1\",\"compareKind\":\"AFTER_QUERY\",\"compareReportGenerated\":false,\"functionId\":\"\",\"messageType\":\"http\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQuerySql\":\"SELECT * FROM user_info1 WHERE id=3 ORDER BY id\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_729ea0c3a9334e1b9da0edb0fabac355\",\"transformedBizSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info1\" does not exist\n  位置:13\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:855)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
                  "executeTime": 625
                }
          ]
        },
        {
          "name": "mysql_liangdong30629",
          "status": "failed",
          "error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
          "sqlCount": 8,
          "executeDuration": 5917,
          "failedSqlDetails": [
                {
                  "lineNumber": 31,
                  "sql": "DELETE FROM user_info3\nWHERE id = 3;",
                  "error": "多发源端[MYSQL]到目标[MYSQL]业务SQL串行执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:36:34\",\"bizSqlOperationType\":\"DELETE\",\"bizSqlSequence\":7,\"bizSqlTableName\":\"user_info3\",\"compareKind\":\"AFTER_QUERY\",\"compareReportGenerated\":false,\"functionId\":\"\",\"messageType\":\"http\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQuerySql\":\"SELECT * FROM user_info3 WHERE id=3 ORDER BY id\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":true,\"syncConnectionInfo\":{\"password\":\"lightdb123456\",\"url\":\"jdbc:mysql://10.20.30.199:2306/liangdong30629?sourceDialect=mysql&targetDialect=mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_729ea0c3a9334e1b9da0edb0fabac355\",\"transformedBizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [java.sql.SQLSyntaxErrorException: Table 'liangdong30629.user_info3' doesn't exist\r\n\tat com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121)\r\n\tat com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)\r\n\tat com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:770)\r\n\tat com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:653)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:855)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
                  "executeTime": 861
                }
          ]
        }
  ]
}

2.4.6. 支持在比对服务页面查看具体报错信息

../_images/uk_agent_failed_report_in_comparison_page_2025-11-10_16-44-21.png

2.4.7. 常见问题

2.4.7.1. 解析sql脚本支持哪些sql

  1. 普通SQL(分号/斜杠结尾,非PL/SQL)

  • 支持类型:DML(增删改查)、DDL(建表/索引/视图等)、DCL(授权/回收),需满足“分号结尾”或“独立斜杠结尾”。

  • 注释兼容:自动过滤单行注释、多行注释、不误判字符串内的单行注释,见下方sql示例:

-- 分号结尾的普通SQL(支持)
SELECT * FROM user_info WHERE name = '张三--测试'; -- 行尾注释
INSERT INTO dept (deptno, dname) VALUES (10, '研发部');

-- 独立斜杠结尾的普通SQL(支持,代码会移除结尾/)
CREATE TABLE test (id INT);
/
  1. Oracle系PL/SQL块(自动移除结尾独立/)

  • 支持的PL/SQL类型(通过正则识别): 匿名块(begin/declare … end;) ,存储过程/函数(create or replace procedure/function …) ,包/包体(create or replace package …)

  • 关键特性:仅识别独立一行的/为结束符,自动移除该/,保留PL/SQL主体(适配JDBC执行)。

  • 注释/嵌套兼容:支持PL/SQL内的单行/多行注释、嵌套begin-end块,注释中含end关键字不影响结束判断。 注意: plsql内嵌套多行注释,jdbc执行报错

-- 处理前(带独立/结尾)
begin
        declare
                v_name varchar2(50) := '张三';
        begin
                /* 注释含end:end不算结束 */
                dbms_output.put_line(v_name);
        end; -- 内层end
end;
/

-- 处理后(自动移除/,保留主体)
begin
        declare
                v_name varchar2(50) := '张三';
        begin
                /* 注释含end:end不算结束 */
                dbms_output.put_line(v_name);
        end; -- 内层end
end;
  1. Prompt提示语句(仅Oracle系支持)

  • 识别以prompt开头的语句(忽略大小写),注意: JDBC执行报错。

PROMPT 开始执行初始化SQL...
prompt 执行建表语句

2.4.7.2. 解析sql脚本不能处理哪些sql

  1. 非Oracle系数据库的PL/SQL/特有语法

  • 限制原因:限定仅支持Oracle系数据库,其他数据库的PL/SQL或特有语法会被跳过或报错。

  • MySQL的存储过程(用DELIMITER $$切换结束符)

  • PostgreSQL的DO $$ … $$匿名块

  1. 未以“独立/”结尾的PL/SQL块

  • 限制原因:判定PL/SQL结束的唯一条件是“独立一行的/”,若PL/SQL块仅以end;结尾(无/),会被当作“未结束SQL”存入,执行时可能因语法不完整报错。

-- 无/结尾的PL/SQL块(代码会视为未结束,存入后执行可能报错)
begin
        dbms_output.put_line('无/结尾');
end;
  1. 非独立/结尾的PL/SQL块

  • 限制原因:仅匹配严格独立一行,前后无空格/字符,若/前后有空格或其他字符,不会被识别为PL/SQL结束符。

begin
        dbms_output.put_line('非独立/');
end;
/  -- 末尾有空格
  1. PL/SQL块内包含独立/(非结束符)

  • 限制原因:若PL/SQL主体内有独立一行的/(如动态SQL、注释外的/),会被误判为PL/SQL结束符,导致后续内容被截断。

begin
        -- 主体内的独立/(会被误判为结束符,后续内容被丢弃)
        execute immediate 'select 1/2 from dual';
        /
        dbms_output.put_line('被截断的内容'); -- 不会被执行
end;
/
  1. 带绑定变量的SQL/PL/SQL

  • 限制原因:未处理Oracle绑定变量(如`:v1`),JDBC执行时会因“未设置绑定变量值”报错。

SELECT * FROM user_info WHERE id = :v_id; -- 绑定变量:v_id未处理
  1. 其他数据库特有语法/结束符

  • 不支持MySQL的DELIMITER、PostgreSQL的$$等特有结束符。

  • 不支持非Oracle系的存储过程/函数语法。

2.4.7.3. 服务端执行过程中的限制

以下限制均基于实际执行流程(从请求校验→脚本解析→任务执行→结果处理):

  1. 源库类型固定限制:仅支持 MySQL 和 Oracle 两种源库类型(不区分大小写),其他数据库类型(如 PostgreSQL、DB2、SQL Server 等)直接判定为不支持,同步流程终止。

  2. 目标库配置强依赖限制:目标库必须在数据源配置文件application.properties定义别名(alias),未配置别名或别名重复(重复时保留原有配置)的目标库无法执行同步,会直接记录配置缺失错误。

  3. sql脚本路径不可自定义限制:SQL 脚本存放路径固定为「用户工作目录/config/scripts/目标库别名/脚本名」。

  4. sql脚本有效性强制校验限制:目标库对应的脚本文件必须存在且为有效文件(非目录),若脚本文件不存在、为空,或解析后无有效 SQL(全是注释/空行),会直接终止该目标库同步,抛出“脚本不存在”或“无有效 SQL”异常。

  5. 任务超时时间固定限制:并行执行,是所有任务的 “整体等待时间”(不管并行 / 串行,最多等30 分钟)。

  6. 并发同步无冲突控制限制:多目标库同步任务并行执行,无并发控制机制(如分布式锁、乐观锁、目标库级别的串行控制),同一目标库若被多次触发同步,可能导致数据一致性冲突。

  7. SQL 执行方式安全与性能限制:每条 SQL 都会单独创建 JDBC 连接和 Statement。

  8. 事务支持缺失限制:SQL 执行默认自动提交,无事务管理机制(无手动提交、回滚逻辑),一条 SQL 执行失败不会影响其他 SQL 执行。

  9. 无自动重试机制限制:SQL 执行失败、任务超时、线程中断等场景下,无自动重试逻辑,失败后仅记录错误详情,需手动重新触发同步流程,不支持失败重试策略(如重试次数、重试间隔配置)。

  10. 动态数据源不支持限制:仅支持数据源配置文件application.properties中预定义的目标库,无法动态添加新的目标库(如通过接口传入临时数据源配置)进行同步。

  11. 批量 SQL 执行不支持限制:不支持 JDBC 批量执行,每条 SQL 单独执行。

  12. sql语句中包含–注释,可能会引起jdbc执行报错:示例如下:

    SELECT * FROM --users
    WHERE id = 1;
    --注释了原本的表名users,导致FROM子句后无表名,SQL 语法不完整。
    
    
    SELECT * FROM users WHERE id = 1--
    AND name = 'test';
    --注释了第一行末尾,第二行的AND name = 'test'成为独立的无效语句(AND不能单独存在)。
    

2.4.8. 增加blade执行sql用例的标志位判断逻辑

2.4.8.1. 概述

站在服务uk_agent的角度

  1. 服务定位: 服务uk_agent作为blade与多环境后端服务的代理中间件。

  2. 核心功能: 统一登录、参数替换、清算标志位校验、结果聚合、可在比对服务查看比对报告。

  3. 核心价值: 简化多环境对接、减少重复开发、保障清算流程一致性。

2.4.8.2. 时序图

  1. 登录请求时序图

    ../_images/blade%E9%9B%86%E6%88%90%E5%A4%9A%E5%8F%91_%E6%B8%85%E7%AE%97%E6%A0%87%E5%BF%97%E4%BD%8D%E5%88%A4%E6%96%AD_%E7%99%BB%E5%BD%95%E8%AF%B7%E6%B1%82%E6%97%B6%E5%BA%8F%E5%9B%BE.png
  2. 业务请求时序图

    ../_images/blade%E9%9B%86%E6%88%90%E5%A4%9A%E5%8F%91_%E6%B8%85%E7%AE%97%E6%A0%87%E5%BF%97%E4%BD%8D%E5%88%A4%E6%96%AD_%E4%B8%9A%E5%8A%A1%E8%AF%B7%E6%B1%82%E6%97%B6%E5%BA%8F%E5%9B%BE.png
  3. 时序图进一步解释:

    • 登录请求(/tafund/submitLogin)在业务请求之前

    • 业务请求的请求参数operator_code和timeStamp来自登录请求返回结果中operator_code和timeStamp

2.4.8.3. 核心协议与使用指南

  • 自定义请求头规范(Ta-Hosts、Expected、Ta-Timeout字段格式、必填性、示例)

参数名

是否必须

说明

示例

Ta-Hosts

多环境后端服务地址,用逗号分隔,格式为`ip:port`

10.20.154.49:8080,10.20.154.50:8080

Expected

多个清算标志位表达式,多条件用逗号分隔,格式为`路径=值`(路径为业务数据的层级索引)

data_0_rows_0_scheDealStatus=3,data_0_returnCode=0

Ta-Timeout

多后端请求的总超时时间,单位ms(默认30000ms)

-- 请求头示例
Ta-Hosts: 10.20.154.49:8080
Expected: data_0_rows_0_ErrorNo=0000
Ta-Timeout: 30000

-- Expected对应的响应体示例如下:
    "data": [
      {
        "total": -1,
        "totalCount": -1,
        "rows": [
          {
            "ErrorInfo": "刷新完成",
            "ErrorNo": "0000"
          }
        ],
        "returnCode": "0",
        "errorNo": "0",
        "errorInfo": ""
      }
    ]
    -- 解释一下,data_0_rows_0_ErrorNo 表示上面响应体中data集合第一个元素data_0的属性rows的集合的第一个元素data_0_rows_0的属性ErrorNo的值为0000
  • 登录请求使用(请求路径、请求体参数、curl示例、成功/失败响应解读)

    -- 多后端环境,示例,oracle环境10.20.162.155:8080, dm环境 10.20.198.56:8080
    -- uk_agent服务 10.188.129.216:9091
    -- 登录请求uri=/tafund/submitLogin
    -- 登录请求体参数operatorCode和password
    -- curl示例如下
    
    curl -i -X POST   http://10.188.129.216:9091/tafund/submitLogin
    -H "Ta-Hosts: 10.20.162.155:8080,10.20.198.56:8080"
    -H "Content-Type: application/x-www-form-urlencoded"
    -d "operatorCode=378F8B35904CF45EE61FBAB59AE66DCA&password=D5A47C2B6C3F61A2BB0C3515399A133D"
    
    -- 成功响应信息如下:,
    -- 只有请求头Ta-Hosts中第一个元素的主机信息的响应结果,这里与单环境的登录的请求结果保持一致。
    -- 同时也可以让blade暂存响应结果中operator_code和timeStamp,用于后续其他业务请求中请求参数替换
    
       {
        "authorization": [],
        "operator_code": "1DD1ADA68063874A33596F6C4B5E2242381E9A4A2FE12C5141F0FC598A9C7C456BEBF766B88E0AE82E2D2361BA5FC376CB6424CAC2C35134",
        "operator_name": "系统管理员",
        "org_code": "",
        "org_name": "",
        "return_code": "0",
        "return_info": "",
        "error_no": "",
        "error_info": "",
        "nick_name": "",
        "user_token": "",
        "user_type": "1",
        "istemporary": "0",
        "wan_ip": "",
        "lan_ip": "",
        "mac": "",
        "inst_id": "0",
        "certificate_type": "",
        "customer_id": "0",
        "realInst_id": "0",
        "lastLoginDateTime": "2025-12-22 16:37:18",
        "systemLatestVersion": "2",
        "currUserBranchCode": "总部机构",
        "sysInitDate": "2025年12月22日",
        "appCode": "",
        "menuCode": "",
        "path": "",
        "timeStamp": "1766392633852",
        "systemLatestVersions": [
          "TA6.0-FUND.V202506.08.000",
          "TA6.0-PUB.V202506.10.000"
        ],
        "menuKindList": [
          {
            "kindCode": "console-fund-ta-vue",
            "kindColor": "#2E81FF",
            "menuIcon": "icon-icon_menu_fund",
            "kindName": "自建"
          }
        ],
        "businTypes": [
          {
            "businType": "*",
            "businCode": "all",
            "businName": "全部",
            "kindCode": "console-bizframe-vue",
            "baseUrl": "API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "*",
            "businCode": "all",
            "businName": "公共",
            "kindCode": "console-biz-common-vue",
            "baseUrl": "COMMON_API_HOME",
            "isDefault": "0",
            "remark": " "
          },
          {
            "businType": "0",
            "businCode": "standard",
            "businName": "理财TA",
            "kindCode": "console-fina-ta-vue",
            "baseUrl": "FINATA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "1",
            "businCode": "ant",
            "businName": "蚂蚁TA",
            "kindCode": "console-ant-ta-vue",
            "baseUrl": "ANT_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "2",
            "businCode": "net",
            "businName": "腾讯TA",
            "kindCode": "console-net-ta-vue",
            "baseUrl": "NET_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "3",
            "businCode": "trust",
            "businName": "信托TA",
            "kindCode": "console-trust-ta-vue",
            "baseUrl": "TRUST_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "4",
            "businCode": "etf",
            "businName": "ETFTA",
            "kindCode": "console-etf-ta-vue",
            "baseUrl": "ETF_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "5",
            "businCode": "fund",
            "businName": "基金自TA",
            "kindCode": "console-fund-ta-vue",
            "baseUrl": "FUND_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "6",
            "businCode": "sub",
            "businName": "分TA",
            "kindCode": "console-sub-ta-vue",
            "baseUrl": "SUB_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "8",
            "businCode": "firs",
            "businName": "中债报备",
            "kindCode": "console-firs-ta-vue",
            "baseUrl": "FIRS_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "9",
            "businCode": "account",
            "businName": "账户中心",
            "kindCode": "console-account-ta-vue",
            "baseUrl": "ACCOUNT_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "A",
            "businCode": "aop",
            "businName": "AOP",
            "kindCode": "console-aop-vue",
            "baseUrl": "AOP_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "M",
            "businCode": "monitor",
            "businName": "监控",
            "kindCode": " ",
            "baseUrl": "MONITOR_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          },
          {
            "businType": "I",
            "businCode": "web",
            "businName": "互联网TA",
            "kindCode": "console-web-ta-vue",
            "baseUrl": "WEB_TA_API_HOME",
            "isDefault": "1",
            "remark": " "
          }
        ],
        "licCustomerName": "恒生电子股份有限公司"
    }
    
  • 业务请求使用(请求路径、参数替换规则、清算标志位校验示例、curl示例)

## 1.请求头示例
### 请求头Expected参数示例,Expected: data_0_rows_0_ErrorNo=0000,注意看响应体中data集合第一个元素的rows属性对应的集合的第一个元素的属性ErrorNo值为0000
### 业务请求uri示例/tafund/fundCacheRefreshQuery/fundCacheRefreshQueryQuery
### 后端服务10.20.154.49:8080
### uk_agent服务10.188.129.216:9091

curl -i -X POST \
  http://10.188.129.216:9091/tafund/fundCacheRefreshQuery/fundCacheRefreshQueryQuery \
  -H "Ta-Hosts: 10.20.154.49:8080" \
  -H "Expected: data_0_rows_0_ErrorNo=0000" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "operator_code=1DD1ADA68063874A33596F6C4B5E22420D921280C61A7D65707A6499FAD2228D13A41055CC4CED48393B207AAB1521E5FF63F2F4D0CE2842&refreshType=0,1,2,3,5&timeStamp=1763983025267"


## 2. 成功响应部分
### HTTP响应头
```
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 243
Date: Fri, 12 Dec 2025 06:29:40 GMT
```

### 格式化响应体(JSON)
{
    "code": 0,
    "msg": "success",
    "traceId": "Trace-Id=uk_agent_29cc0dd5-d39b-4438-b784-9398f2157aa3",
    "results": [
        {
            "data": [
                {
                    "total": -1,
                    "totalCount": -1,
                    "rows": [
                        {
                            "ErrorInfo": "刷新完成",
                            "ErrorNo": "0000"
                        }
                    ],
                    "returnCode": "0",
                    "errorNo": "0",
                    "errorInfo": ""
                }
            ]
        }
    ],
    "ipHosts": [
        "10.20.154.49:8080"
    ],
    "failedHosts": []
}
-- 注意看响应体组成部分,顶层: code、msg、traceId、results、ipHosts、failedHosts,见下面响应格式详解
-- 响应中results集合中每个元素依次表示不同后端返回的响应,其中data集合第一个元素data_0的属性rows对应的集合中第一个元素data_0_rows_0的属性ErrorNo的值是0000与清算标志位预期值Expected: data_0_rows_0_ErrorNo=0000一致

2.4.8.4. 响应格式详解

  • 响应字段说明(code、msg、traceId、results、ipHosts、failedHosts)

字段名

类型

必填项

说明

code

int

响应状态码:0 表示整体请求处理成功,非0表示整体失败(如参数错误、系统异常)

traceId

String

链路id,可以在比对服务查看本次多环境操作过程中sql引起的数据变化比对,前提多个后端服务接入统一sql和多发

msg

String

响应信息描述:success 表示成功,失败时为具体错误信息

results

list

多环境后端服务的响应结果集合,集合中每个元素对应每个后端服务返回的原始结果,与ipHosts集合中元素对应

ipHosts

list

本次uk_agent转发的全部的后端环境,举例 ["10.20.162.155:8080","10.20.198.56:8080"]

failedHosts

list

处理失败的主机列表:空数组表示所有主机处理成功,非空则包含失败的主机地址,比如不符合预期结果,预期值来自自定义请求头Expected内容

  • 部分失败的响应体示例如下:

-- 失败响应示例1
{
  "code": 1,
  "msg": "部分或全部主机响应不符合预期",
  "traceId": "Trace-Id=uk_agent_29cc0dd5-d39b-4438-b784-9398f2157aa3",
  "results": [
    {
      "data": [
        {
          "total": -1,
          "totalCount": -1,
          "rows": [
            {
              "ErrorInfo": "刷新完成",
              "ErrorNo": "0000"
            }
          ],
          "returnCode": "0",
          "errorNo": "0",
          "errorInfo": ""
        }
      ]
    },
    {
      "data": [
        {
          "returnCode": "-1",
          "errorNo": "9999",
          "total": 0,
          "errorInfo": "节点【刷新JVM缓存-公共库】返回错误信息:节点【刷新JVM缓存-交易库】返回错误信息:片区1:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确,片区2:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确];errCode:[9999],errMsg:[节点【刷新JVM缓存-账户库】返回错误信息:片区1:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确,片区2:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确",
          "rows": [],
          "totalCount": 0
        }
      ]
    }
  ],
  "ipHosts": [
    "10.20.162.155:8080",
    "10.20.198.56:8080"
  ],
  "failedHosts": [
    "10.20.198.56:8080"
  ]
}
-- 这里失败的主机是10.20.198.56:8080,返回的失败响应见results集合中第二个


-- 登录失败响应示例2

  {
  "code": 1,
  "msg": "部分或全部主机登录失败",
  "traceId": "Trace-Id=uk_agent_ede62f70-850d-4353-af9e-5150cab11399",
  "results": [
    {
      "authorization": [],
      "operator_code": "1DD1ADA68063874A33596F6C4B5E22420D921280C61A7D65FC6FDBD6ECF4DFF579650058DC0B5ED994B478D15B463E93D7CC0AE8BC2471D3",
      "operator_name": "系统管理员",
      "org_code": "",
      "org_name": "",
      "return_code": "0",
      "return_info": "",
      "error_no": "",
      "error_info": "",
      "nick_name": "",
      "user_token": "",
      "user_type": "1",
      "istemporary": "0",
      "wan_ip": "",
      "lan_ip": "",
      "mac": "",
      "inst_id": "0",
      "certificate_type": "",
      "customer_id": "0",
      "realInst_id": "0",
      "lastLoginDateTime": "2025-12-22 16:12:18",
      "systemLatestVersion": "2",
      "currUserBranchCode": "总部机构",
      "sysInitDate": "2025年12月22日",
      "appCode": "",
      "menuCode": "",
      "path": "",
      "timeStamp": "1766391134269",
      "systemLatestVersions": [
        "TA6.0-FUND.V202506.08.000",
        "TA6.0-PUB.V202506.10.000"
      ],
      "menuKindList": [
        {
          "kindCode": "console-fund-ta-vue",
          "kindColor": "#2E81FF",
          "menuIcon": "icon-icon_menu_fund",
          "kindName": "自建"
        }
      ],
      "businTypes": [
        {
          "businType": "*",
          "businCode": "all",
          "businName": "全部",
          "kindCode": "console-bizframe-vue",
          "baseUrl": "API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "*",
          "businCode": "all",
          "businName": "公共",
          "kindCode": "console-biz-common-vue",
          "baseUrl": "COMMON_API_HOME",
          "isDefault": "0",
          "remark": " "
        },
        {
          "businType": "0",
          "businCode": "standard",
          "businName": "理财TA",
          "kindCode": "console-fina-ta-vue",
          "baseUrl": "FINATA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "1",
          "businCode": "ant",
          "businName": "蚂蚁TA",
          "kindCode": "console-ant-ta-vue",
          "baseUrl": "ANT_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "2",
          "businCode": "net",
          "businName": "腾讯TA",
          "kindCode": "console-net-ta-vue",
          "baseUrl": "NET_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "3",
          "businCode": "trust",
          "businName": "信托TA",
          "kindCode": "console-trust-ta-vue",
          "baseUrl": "TRUST_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "4",
          "businCode": "etf",
          "businName": "ETFTA",
          "kindCode": "console-etf-ta-vue",
          "baseUrl": "ETF_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "5",
          "businCode": "fund",
          "businName": "基金自TA",
          "kindCode": "console-fund-ta-vue",
          "baseUrl": "FUND_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "6",
          "businCode": "sub",
          "businName": "分TA",
          "kindCode": "console-sub-ta-vue",
          "baseUrl": "SUB_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "8",
          "businCode": "firs",
          "businName": "中债报备",
          "kindCode": "console-firs-ta-vue",
          "baseUrl": "FIRS_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "9",
          "businCode": "account",
          "businName": "账户中心",
          "kindCode": "console-account-ta-vue",
          "baseUrl": "ACCOUNT_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "A",
          "businCode": "aop",
          "businName": "AOP",
          "kindCode": "console-aop-vue",
          "baseUrl": "AOP_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "M",
          "businCode": "monitor",
          "businName": "监控",
          "kindCode": " ",
          "baseUrl": "MONITOR_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        },
        {
          "businType": "I",
          "businCode": "web",
          "businName": "互联网TA",
          "kindCode": "console-web-ta-vue",
          "baseUrl": "WEB_TA_API_HOME",
          "isDefault": "1",
          "remark": " "
        }
      ],
      "licCustomerName": "恒生电子股份有限公司"
    },
    {
      "msg": "登录失败",
      "reason": "I/O error on POST request for \"http://10.20.198.56:8081/tafund/submitLogin\": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect"
    }
  ],
  "ipHosts": [
    "10.20.162.155:8080",
    "10.20.198.56:8081"
  ],
  "failedHosts": [
    "10.20.198.56:8081"
  ]
}
-- 登录主机10.20.198.56:8081失败

2.4.8.5. 附录

  • 响应码对照表(所有可能的code值及含义)

响应码

信息

原因

解决方案

0

success

登录成功、业务请求成功(有标志位判断也是后端环境都符合预期(参考自定义请求头Expected))

1

未登录或登录缓存已过期,请先发送登录请求

业务请求时登录令牌缓存不存在/过期

blade侧立即重新发起登录请求

1

Ta-Hosts 格式不合法(需为ip:port,ip:port格式)

Ta-Hosts 头非ip:port格式

在uk_agent侧封装请求参数检核工具类,发起请求前自动检核Ta-Hosts、Expected、请求体、必选参数的合法性,提前拦截错误

1

处理请求异常:内部服务错误

uk_agent自身线程池满或转发请求超时

可重启uk_agent服务临时恢复

1

部分或全部主机响应不符合预期

后端服务返回的响应与预期不符

排查后端服务出错原因

1

请求处理超时

uk_agent在一定时间内没有从后端服务获取响应结果

请求头Ta-Timeout增加uk_agent读取时间

1

请求处理异常:xxx

uk_agent聚合多个后端服务返回的响应结果出现异常

根据请求处理异常:xxx 定位异常原因和解决方案

1

登录请求缺少operatorCode或password参数

登录请求(/tafund/submitLogin)缺少必要参数:operatorCode或password

登录请求的请求参数增加必要参数operatorCode和password

1

业务请求缺少operator_code参数

业务请求缺少operator_code参数

业务请求的请求参数增加必要参数operator_code,提示:参数operator_code来自登录请求返回结果

1

处理请求异常: xxx

uk_agent在多环境请求处理流程异常

根据处理请求异常: xxx 定位异常原因和解决方案

2.4.9. 支持TA SQL语法标志位判断(校验多库SQL执行结果一致性)

2.4.9.1. 概述

复用原有标准接口 http://ip:port/api/exec/sql 无任何路径变更,在原有请求参数基础上 新增2个可选核心参数,实现对指定目标库执行校验SQL、并将SQL执行结果与预期值进行精准比对的能力。

核心价值:解决不同信创库执行相同SQL后,结果数据不一致/不符合预期 的验证诉求,自动化校验SQL执行结果,替代人工查询比对,提升多库兼容性测试效率,保障TA业务SQL执行结果的一致性。

核心能力说明

  1. 复用原有接口 http://ip:port/api/exec/sql :无需新增接口地址,保持调用方式、请求方式、Content-Type完全不变;

  2. 新增参数为 非必传项 :不传入则执行原有「脚本执行」逻辑,传入则执行「SQL结果校验」逻辑,两种模式互不影响;

  3. 一一对应校验规则:传入的校验SQL数组与预期结果数组,按 数组索引顺序严格一一匹配 校验;

  4. 多库并行校验:对 targetDbs 中配置的所有目标库,并行执行所有校验SQL并完成结果比对;

  5. 精准错误反馈:校验不通过时,明确返回「校验项索引、校验SQL、预期结果、实际结果」,快速定位不一致问题;

  6. 兼容原有返回结构:响应体的整体格式、字段含义、状态枚举值完全沿用原有规范,无适配成本。

2.4.9.2. 接口说明

接口请求地址(复用已有的接口)

http://ip:port/api/exec/sql

请求方式

POST

Content-Type

application/json;charset=UTF-8

原有参数保持不变(必传)

参数名

类型

是否必须

说明

sourceDb

String

源数据库类型,不区分大小写,取值范围[oracle,mysql]

targetDbs

Array<String>

集合中每个元素是数据库的具体别名,对应数据源配置项 agent.datasource.sources[i].alias

新增扩展参数(可选,二选一执行逻辑)

说明:原参数 scriptName 为「脚本执行模式」核心参数,新增的 checkSql/expectedResult 为「SQL结果校验模式」核心参数,两种模式 互斥

  1. scriptName → 执行原有「多库执行指定SQL脚本」逻辑;

  2. checkSql + expectedResult → 执行新增「多库执行校验SQL+结果比对」逻辑;

  3. 均不传/均传 → 接口返回参数错误,提示「请选择一种执行模式:脚本执行/结果校验」。

新增参数名

类型

是否必须

说明

checkSql

Array<String>

待执行的校验SQL数组,仅支持单行查询类SQL(SELECT) ,数组中每个元素为一条完整的校验SQL语句

expectedResult

Array<Object>

校验SQL对应的预期结果数组,数组长度必须与checkSql一致、索引一一对应;值类型支持字符串、数字,无结果时可传空字符串 “”

新增参数约束规则

  1. checkSqlexpectedResult 必须 成对出现,有其一必有其二,缺一不可;

  2. 两个数组的 长度必须完全相同,否则直接判定参数非法;

  3. checkSql 内仅支持 SELECT开头的单行查询SQL,不支持DML(增删改)、DDL(建表)、PL/SQL块、存储过程等;

  4. 每条校验SQL要求 返回且仅返回1行多列结果 (聚合查询最佳,如max/min/count/单行字段查询),若返回多行多列,取第一行作为实际结果参与比对;

  5. expectedResult 的值类型会自动适配实际执行结果类型做 等值匹配,例如预期传数字 20100531,实际返回数值型结果也可精准匹配;

  6. 大小写兼容:数据库返回的字符串结果,比对时 不区分大小写

2.4.9.3. 时序图

../_images/blade%E9%9B%86%E6%88%90%E5%A4%9A%E5%8F%91_ta_%E6%A0%A1%E9%AA%8C%E5%A4%9A%E5%BA%93SQL%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C%E4%B8%80%E8%87%B4%E6%80%A7_%E6%97%B6%E5%BA%8F%E5%9B%BE.png

2.4.9.4. 请求和响应示例

-- 示例1:部分SQL执行结果不符合预期(校验不通过,返回failed)
-- 请求参数

{
    "checkSql": [
        "select init_date from tbsysarg;",
        "select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')"
    ],
    "expectedResult": [
        20100531,
        "4"
    ],
    "sourceDb": "oracle",
    "targetDbs": [
        "oracle_fund60pub",
        "dm_fund60pub"
    ]
}

-- 响应参数

{
  "traceId": "uk_agent_72370992605345b0bed8cf8ec31a5daa",
  "status": "failed",
  "details": [
    {
      "name": "oracle_fund60pub",
      "status": "failed",
      "error": "目标库oracle_fund60pub检查项校验不通过:\n【检查项索引1】\n- 校验SQL:select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')\n- 预期结果:4\n- 实际结果:3",
      "sqlCount": 2,
      "executeDuration": 73,
      "failedSqlDetails": null
    },
    {
      "name": "dm_fund60pub",
      "status": "failed",
      "error": "目标库dm_fund60pub检查项校验不通过:\n【检查项索引1】\n- 校验SQL:select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')\n- 预期结果:4\n- 实际结果:3",
      "sqlCount": 2,
      "executeDuration": 24,
      "failedSqlDetails": null
    }
  ]
}

-- 示例2:所有SQL执行结果符合预期(校验通过,返回success)

-- 请求参数

{
    "checkSql": [
        "select init_date from tbsysarg;",
        "select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')"
    ],
    "expectedResult": [
        20100531,
        "3"
    ],
    "sourceDb": "oracle",
    "targetDbs": [
        "oracle_fund60pub",
        "dm_fund60pub"
    ]
}

-- 接口响应参数

{
  "traceId": "uk_agent_473e72a694774859b8f01869ab9b79b9",
  "status": "success",
  "details": [
    {
      "name": "oracle_fund60pub",
      "status": "success",
      "error": null,
      "sqlCount": 2,
      "executeDuration": 63,
      "failedSqlDetails": null
    },
    {
      "name": "dm_fund60pub",
      "status": "success",
      "error": null,
      "sqlCount": 2,
      "executeDuration": 24,
      "failedSqlDetails": null
    }
  ]
}

响应字段补充说明(完全兼容原有规范)

本次增强 无任何响应体字段变更,所有返回字段含义、状态枚举值与原有接口完全一致,仅对 error 字段的内容做针对性补充,补充说明如下:

  1. 顶层核心状态 status 枚举(不变)

    • success :所有目标库的 全部校验SQL执行成功+结果比对一致

    • failed :所有目标库校验失败,或存在SQL执行异常(如SQL语法错误、表不存在、权限不足)。

  2. 明细项 details 内字段补充说明

    • sqlCount :当前目标库执行的 校验SQL总数 (即 checkSql 数组的长度);

    • executeDuration :当前目标库执行 所有校验SQL的总耗时(毫秒)

    • error

      • 校验通过 → null

      • 校验不通过 → 格式化文本,清晰展示「目标库名称、校验项索引、校验SQL、预期结果、实际结果」;

      • SQL执行异常 → 原有报错逻辑,如「表不存在、SQL语法错误」等原生错误信息;

    • failedSqlDetails

      • SQL结果校验模式下固定为null,该字段仅在「脚本执行模式」下返回具体失败SQL的行号/内容,两种模式互不干扰。

2.4.9.5. 注意事项

  1. 本能力仅支持 查询类SQL(SELECT),不支持执行任何修改类SQL,保障数据库数据安全;

  2. 校验SQL建议使用 聚合函数查询(max/min/count/avg)、单行查询 ,避免返回多行多列导致结果截取不准确;

  3. 预期结果的类型无需严格匹配,接口会自动做类型转换后比对(如数字 20100531 与字符串 “20100531” 判定为一致);

  4. 所有目标库的数据源配置,仍沿用原有 application.properties 中的配置,无需新增任何数据源配置;

  5. 执行结果的比对日志,可通过 traceId比对服务页面 查看完整链路详情,与原有脚本执行链路日志一致;

  6. 性能说明:校验SQL为轻量级查询,多库并行执行,单条SQL执行耗时与直接在数据库客户端执行耗时基本一致。