无尘阁日记

无尘阁日记

更改JWT的TOKEN签发方式的沟通
2026-03-12

我问:

-- =========================================================

-- 对接企业主表

-- 这里明确:对我们来说,一个 partner 就等于一家企业

-- 但为便于与领导、业务、接口文档持续沟通,表名、字段名中仍保留 partner 这一叫法

--

-- 设计原则:

-- 1)tenant_id:存我们系统中的企业唯一编码。你已明确:tenant_id 实际就是企业ID

-- 2)enterprise_code:存企业统一社会信用代码,替代原 enterprise_id

-- 3)company_id:去掉,不再单独存,因为本质上已被 enterprise_code 替代

-- 4)app_key:对外提供的接入标识,建议全局唯一

-- 5)app_secret:

--    - app_secret_hash:用于 login 校验,不存明文

--    - app_secret_ciphertext:用于“确有业务需要再次展示明文”时保存密文;不是明文

--    - app_secret_mask:前端列表展示的脱敏值,如 abcd****wxyz

-- 6)secret_version:当前生效的 secret 版本号

--    - 首次生成时为 1

--    - 之后每重置一次 app_secret,版本号 +1

--    - token 日志里记录签发时使用的是第几版 secret,便于排障和审计

-- 7)关于 secret 轮换(这里按你的要求特别注明):

--    - 所谓“轮换 secret”,不是指单纯对比企业有效时间和 secret 有效时间后,数据库自己直接自动刷新

--    - 正确含义是:当系统发现当前 secret 已过期,或者管理员主动要求更换时,生成一套新的 secret,并将 secret_version + 1

--    - 是否“自动刷新”,属于业务策略,不是表结构本身的内建能力

--    - 你可以在业务层实现两种策略:

--      A. 自动轮换:当 login 或定时任务发现 secret_expire_time 已过,就自动生成新 secret,并更新版本

--      B. 人工轮换:secret 过期后不自动生成,要求前端/管理员点击“生成/重置 secret”

--    - 如果采用自动轮换,必须注意:新明文 secret 如何安全通知对方,否则对方拿不到新 secret,后续仍无法 login

--    - 因此从实际落地看,更推荐“过期后禁止签发 + 人工重置”,除非双方有成熟的密钥同步机制

--    - 企业有效时间与 secret 有效时间可以分别存在:

--      * 企业有效时间:代表这家企业整体是否仍具备对接资格

--      * secret 有效时间:代表当前这把 secret 是否还能用于 login

--      * login 时通常需要两者都有效,才允许签发 token

-- 8)OA连通性:

--    - 本表保留当前最新连通状态快照

--    - 另配 bridge_partner_connectivity_log 记录每次手工测试/自动测试明细日志

-- =========================================================

DROP TABLE IF EXISTS `bridge_partner`;

CREATE TABLE `bridge_partner`

(

`id`                          BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',


`tenant_id`                   BIGINT NOT NULL COMMENT '我们系统中的企业唯一编码。你已明确:tenant_id 实际就是企业ID',

`enterprise_code`             VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码。替代原 enterprise_id 字段',


`partner_code`                VARCHAR(100) NOT NULL COMMENT '对接企业编码。虽然 partner 等于企业,但仍保留,便于接口、日志、配置识别,如 nuoming',

`partner_name`                VARCHAR(255) NOT NULL COMMENT '对接企业名称',

`enterprise_name`             VARCHAR(255) DEFAULT NULL COMMENT '企业名称冗余字段,便于展示和检索',


`partner_effective_time`      DATETIME DEFAULT NULL COMMENT '企业对接生效时间。为空表示不限制起始时间',

`partner_expire_time`         DATETIME DEFAULT NULL COMMENT '企业对接失效时间。超过该时间后,即使 app_key、app_secret 正确,也不允许 login 和业务交互',


`api_host_test`               VARCHAR(500) DEFAULT NULL COMMENT '对方OA测试环境接口域名或IP',

`api_host_production`         VARCHAR(500) DEFAULT NULL COMMENT '对方OA正式环境接口域名或IP',


`app_key`                     VARCHAR(200) NOT NULL COMMENT '对外分配的接入标识。建议由系统生成并全局唯一。login时作为账号标识之一',

`app_secret_hash`             VARCHAR(255) NOT NULL COMMENT 'app_secret 摘要值,用于 login 校验;不存明文。用户传入明文后,服务端做同算法摘要比对',

`app_secret_ciphertext`       VARCHAR(2000) DEFAULT NULL COMMENT 'app_secret 加密密文。仅当业务要求“可再次展示明文”时使用;不是明文。若采用“只展示一次”方案,可为空',

`app_secret_mask`             VARCHAR(100) DEFAULT NULL COMMENT 'app_secret 脱敏展示值,如 abcd****wxyz,用于前端列表回显,避免暴露完整 secret',

`secret_version`              INT NOT NULL DEFAULT 1 COMMENT '当前生效的 secret 版本号。首次生成为1,每重置或轮换一次+1。token签发日志需记录本版本号,便于定位“某token是用哪一版secret签发的”',

`secret_last_generated_time`  DATETIME DEFAULT NULL COMMENT '最近一次生成、重置或轮换 app_secret 的时间',

`secret_last_generated_by`    BIGINT DEFAULT NULL COMMENT '最近一次生成、重置或轮换 app_secret 的操作人',

`secret_effective_time`       DATETIME DEFAULT NULL COMMENT '当前 app_secret 生效时间。通常生成成功后立即生效',

`secret_expire_time`          DATETIME DEFAULT NULL COMMENT '当前 app_secret 失效时间。超过该时间后,当前 secret 不再允许用于 login。是否自动轮换新 secret,由业务策略决定,不是表自动完成',

`secret_rotate_mode`          VARCHAR(20) NOT NULL DEFAULT 'MANUAL' COMMENT 'secret轮换模式:MANUAL-人工轮换;AUTO-自动轮换。仅表示业务策略标识,不代表数据库会自动刷新。若为AUTO,通常由定时任务或login流程检测过期后触发生成新secret',

`secret_rotate_notice`        VARCHAR(500) DEFAULT NULL COMMENT 'secret轮换策略备注。例如“过期后禁止签发,需人工重置”“过期后由定时任务自动轮换并邮件通知对方”',


`login_failed_count`          INT NOT NULL DEFAULT 0 COMMENT '连续登录失败次数。可用于风控,例如连续失败过多后临时锁定',

`last_login_success_time`     DATETIME DEFAULT NULL COMMENT '最近一次 login 成功时间',

`last_login_failed_time`      DATETIME DEFAULT NULL COMMENT '最近一次 login 失败时间',


`ip_allow_list`               VARCHAR(2000) DEFAULT NULL COMMENT '调用方IP白名单,多个用英文逗号分隔;为空表示不限制',

`max_retry_count`             INT DEFAULT 3 COMMENT '业务接口重试最大次数。是接口调用重试策略,不是 login 重试次数',

`retry_interval_seconds`      INT DEFAULT 60 COMMENT '业务接口重试间隔秒数',


`oa_connect_status`           TINYINT NOT NULL DEFAULT 0 COMMENT '当前对方OA连通状态:0-未测试,1-连通成功,2-连通失败',

`oa_connect_env`              VARCHAR(20) DEFAULT NULL COMMENT '最近一次测试的环境:TEST/PRODUCTION',

`oa_connect_time`             DATETIME DEFAULT NULL COMMENT '最近一次点击“连通对方OA”测试的时间',

`oa_connect_result_code`      VARCHAR(64) DEFAULT NULL COMMENT '最近一次连通测试结果码,如 SUCCESS/TIMEOUT/DNS_ERROR/HTTP_401/HTTP_500/SOCKET_ERROR',

`oa_connect_result_msg`       VARCHAR(1000) DEFAULT NULL COMMENT '最近一次连通测试结果说明,例如“DNS解析失败”“连接超时”“鉴权失败”等',

`oa_connect_response_time_ms` INT DEFAULT NULL COMMENT '最近一次连通测试耗时,单位毫秒',

`oa_connect_by`               BIGINT DEFAULT NULL COMMENT '最近一次点击“连通对方OA”的操作人',


`status`                      TINYINT NOT NULL DEFAULT 1 COMMENT '记录状态:0-停用,1-启用。停用后不允许 login,不允许业务接口交互',

`remark`                      VARCHAR(500) DEFAULT NULL COMMENT '备注',


`created_by`                  BIGINT DEFAULT NULL COMMENT '创建人',

`updated_by`                  BIGINT DEFAULT NULL COMMENT '更新人',

`is_deleted`                  TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除:0否,1是。逻辑删除',

`deleted_time`                DATETIME DEFAULT NULL COMMENT '删除时间',

`create_time`                 DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

`update_time`                 DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',


PRIMARY KEY (`id`) USING BTREE,

UNIQUE KEY `uk_tenant_id` (`tenant_id`) USING BTREE,

UNIQUE KEY `uk_partner_code` (`partner_code`) USING BTREE,

UNIQUE KEY `uk_app_key` (`app_key`) USING BTREE,

UNIQUE KEY `uk_enterprise_code` (`enterprise_code`) USING BTREE,


KEY `idx_partner_name` (`partner_name`) USING BTREE,

KEY `idx_status` (`status`) USING BTREE,

KEY `idx_oa_connect_status` (`oa_connect_status`) USING BTREE,

KEY `idx_partner_expire_time` (`partner_expire_time`) USING BTREE,

KEY `idx_secret_expire_time` (`secret_expire_time`) USING BTREE

) ENGINE=InnoDB

DEFAULT CHARSET=utf8mb4

COLLATE=utf8mb4_0900_ai_ci COMMENT='对接企业主表(一个partner就等于一家企业,同时承载企业级认证配置)';



-- =========================================================

-- app_secret 生成/重置/轮换日志表

--

-- 为什么需要这张表:

-- 1)前端点击“生成 app_secret”是一个关键安全动作,建议必须审计

-- 2)要知道是谁点的、何时点的、是首次生成还是重置/轮换、是否成功、失败原因是什么

-- 3)一般不在此表保存明文 secret,避免明文到处扩散

--

-- 关于“前端需要显示这个明文,怎么得到”:

-- 推荐流程:

-- 1)服务端生成随机明文 secret

-- 2)服务端当场返回给前端一次,供用户复制

-- 3)数据库只落 hash / ciphertext / mask / version

-- 4)本日志表只记录动作,不落明文

--

-- 关于“轮换 secret”:

-- 1)ROTATE 本质上也是生成新 secret,只是语义上强调“替换旧版本”

-- 2)若系统发现 secret 已过期,可根据 secret_rotate_mode 决定:

--    - MANUAL:仅记录不可用,要求管理员手动生成/重置

--    - AUTO:由业务程序自动生成新 secret,并写入本表 operation_type = ROTATE

-- 3)是否自动轮换,不是表本身完成,而是业务流程或定时任务完成

-- =========================================================

DROP TABLE IF EXISTS `bridge_partner_secret_log`;

CREATE TABLE `bridge_partner_secret_log`

(

`id`                     BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',

`tenant_id`              BIGINT NOT NULL COMMENT '企业唯一编码',

`enterprise_code`        VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码',

`partner_id`             BIGINT NOT NULL COMMENT '关联 bridge_partner.id',


`operation_type`         VARCHAR(30) NOT NULL COMMENT '操作类型:GENERATE-首次生成;RESET-人工重置secret;ROTATE-轮换secret。轮换可由人工触发,也可由业务程序按策略自动触发',

`trigger_type`           VARCHAR(20) NOT NULL DEFAULT 'MANUAL' COMMENT '触发方式:MANUAL-人工点击;AUTO-系统自动触发。若secret过期后由程序或定时任务自动生成新secret,则记为AUTO',

`old_secret_version`     INT DEFAULT NULL COMMENT '旧 secret 版本号。首次生成时为空;重置或轮换时记录原版本',

`new_secret_version`     INT DEFAULT NULL COMMENT '新 secret 版本号。首次生成通常为1,之后每次+1',

`generate_status`        TINYINT NOT NULL DEFAULT 0 COMMENT '生成结果:0-失败,1-成功',

`generate_result_code`   VARCHAR(64) DEFAULT NULL COMMENT '结果码:SUCCESS/RANDOM_FAIL/ENCRYPT_FAIL/SAVE_FAIL/PERMISSION_DENIED/SECRET_EXPIRED_AUTO_ROTATE 等',

`generate_result_msg`    VARCHAR(1000) DEFAULT NULL COMMENT '结果说明,便于排障',


`secret_mask`            VARCHAR(100) DEFAULT NULL COMMENT '本次生成出来的 secret 脱敏值,仅用于审计确认,不保存明文',

`is_frontend_displayed`  TINYINT NOT NULL DEFAULT 0 COMMENT '本次明文 secret 是否已返回并展示给前端:0-否,1-是。若为系统自动轮换,一般为0,因为通常不会直接展示给前端',

`display_expire_time`    DATETIME DEFAULT NULL COMMENT '若前端采用一次性展示机制,可记录前端展示凭证失效时间;仅审计用途,可为空',


`operator_id`            BIGINT DEFAULT NULL COMMENT '操作人ID。若为AUTO自动触发,可为空或填系统用户ID',

`operator_name`          VARCHAR(100) DEFAULT NULL COMMENT '操作人姓名。若为AUTO自动触发,可写SYSTEM',

`source_ip`              VARCHAR(50) DEFAULT NULL COMMENT '操作来源IP。若为AUTO自动触发,可为空',

`request_id`             VARCHAR(100) DEFAULT NULL COMMENT '请求流水号',

`request_body`           JSON DEFAULT NULL COMMENT '请求报文。注意不要写入明文 secret',

`response_body`          JSON DEFAULT NULL COMMENT '响应报文。注意若含明文 secret,建议落库前脱敏或不存',


`remark`                 VARCHAR(500) DEFAULT NULL COMMENT '备注',

`create_time`            DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',


PRIMARY KEY (`id`) USING BTREE,

KEY `idx_partner_id` (`partner_id`) USING BTREE,

KEY `idx_tenant_id` (`tenant_id`) USING BTREE,

KEY `idx_request_id` (`request_id`) USING BTREE,

KEY `idx_operation_type` (`operation_type`) USING BTREE,

KEY `idx_trigger_type` (`trigger_type`) USING BTREE,

KEY `idx_new_secret_version` (`new_secret_version`) USING BTREE,

KEY `idx_operator_id` (`operator_id`) USING BTREE

) ENGINE=InnoDB

DEFAULT CHARSET=utf8mb4

COLLATE=utf8mb4_0900_ai_ci COMMENT='app_secret生成/重置/轮换审计日志表';



-- =========================================================

-- 对方OA连通性测试日志表

--

-- 用途:

-- 1)记录每次点击“连通对方OA”的完整历史,而不是只保留 bridge_partner 上的当前快照

-- 2)能回答:

--    - 是否测通过

--    - 在什么环境测的

--    - 为什么不通

--    - 请求耗时多少

--    - 是谁测的

--    - 当时用的哪个地址

-- =========================================================

DROP TABLE IF EXISTS `bridge_partner_connectivity_log`;

CREATE TABLE `bridge_partner_connectivity_log`

(

`id`                       BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',

`tenant_id`                BIGINT NOT NULL COMMENT '企业唯一编码',

`enterprise_code`          VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码',

`partner_id`               BIGINT NOT NULL COMMENT '关联 bridge_partner.id',


`connect_env`              VARCHAR(20) NOT NULL COMMENT '测试环境:TEST/PRODUCTION',

`target_host`              VARCHAR(500) DEFAULT NULL COMMENT '本次测试命中的目标地址。通常取当时配置的 api_host_test 或 api_host_production',

`connect_status`           TINYINT NOT NULL DEFAULT 0 COMMENT '连通结果:0-失败,1-成功',

`result_code`              VARCHAR(64) DEFAULT NULL COMMENT '结果码:SUCCESS/TIMEOUT/DNS_ERROR/HTTP_401/HTTP_403/HTTP_404/HTTP_500/SSL_ERROR/SOCKET_ERROR 等',

`result_msg`               VARCHAR(1000) DEFAULT NULL COMMENT '结果说明,记录为何无法连通',

`http_status_code`         INT DEFAULT NULL COMMENT '若走HTTP接口,可记录HTTP状态码',

`response_time_ms`         INT DEFAULT NULL COMMENT '本次连通测试耗时,单位毫秒',


`operator_id`              BIGINT DEFAULT NULL COMMENT '点击测试按钮的操作人ID',

`operator_name`            VARCHAR(100) DEFAULT NULL COMMENT '点击测试按钮的操作人姓名',

`source_ip`                VARCHAR(50) DEFAULT NULL COMMENT '操作来源IP',

`request_id`               VARCHAR(100) DEFAULT NULL COMMENT '请求流水号',

`request_body`             JSON DEFAULT NULL COMMENT '请求报文',

`response_body`            JSON DEFAULT NULL COMMENT '响应报文',


`remark`                   VARCHAR(500) DEFAULT NULL COMMENT '备注',

`create_time`              DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',


PRIMARY KEY (`id`) USING BTREE,

KEY `idx_partner_id` (`partner_id`) USING BTREE,

KEY `idx_tenant_id` (`tenant_id`) USING BTREE,

KEY `idx_request_id` (`request_id`) USING BTREE,

KEY `idx_connect_env` (`connect_env`) USING BTREE,

KEY `idx_connect_status` (`connect_status`) USING BTREE,

KEY `idx_operator_id` (`operator_id`) USING BTREE

) ENGINE=InnoDB

DEFAULT CHARSET=utf8mb4

COLLATE=utf8mb4_0900_ai_ci COMMENT='对方OA连通性测试日志表';



-- =========================================================

-- JWT签发日志表

--

-- 说明:

-- 1)虽然已经明确一个 partner 就是一家企业,但按你的要求,这里仍保留 partner 叫法用于沟通

-- 2)enterprise_code 设为可选填,所以允许为 NULL

-- 3)grant_type 是“授权类型”的意思,源自 OAuth2 概念

--    当前场景推荐固定为 client_credentials

--    含义:不是某个自然人账号登录,而是系统对系统凭据换 token

-- 4)jti = JWT ID,JWT唯一编号

--    用途:

--    - 标识每一个 token

--    - 防重复

--    - 可追踪

--    - 可吊销

-- 5)jti 生成规则建议:

--    - 必须全局唯一

--    - 不建议用简单自增ID

--    - 推荐使用:lower(replace(uuid(), '-', ''))

--    - 例如:2f8c2f9e7b2347d6a1fcb3bb7d8c1234

-- 6)token 是否存明文:

--    - 安全上更推荐只存 token_hash + token_preview

--    - 这里保留 token 字段,便于你当前排查;后续若安全要求提高,可改为不存明文

-- =========================================================

DROP TABLE IF EXISTS `bridge_token_log`;

CREATE TABLE `bridge_token_log`

(

`id`                    BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',

`tenant_id`             BIGINT NOT NULL COMMENT '企业唯一编码',

`enterprise_code`       VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码。按当前要求,这里为选填,可为空',

`partner_id`            BIGINT NOT NULL COMMENT '关联 bridge_partner.id。虽然partner本质上就是企业,但为便于沟通和领导要求,这里字段名保持不变',


`request_id`            VARCHAR(100) DEFAULT NULL COMMENT '请求流水号',

`grant_type`            VARCHAR(50) NOT NULL DEFAULT 'client_credentials' COMMENT '授权类型。当前推荐固定为 client_credentials,表示系统对系统凭据换 token,不是自然人账号登录',

`app_key_snapshot`      VARCHAR(200) DEFAULT NULL COMMENT '签发时使用的 app_key 快照。冗余保存,避免后续主表变化影响审计',

`secret_version`        INT DEFAULT NULL COMMENT '签发该 token 时使用的 secret 版本号。便于排查“这个 token 是用哪一版 secret 签发的”',


`jti`                   VARCHAR(100) NOT NULL COMMENT 'JWT唯一编号。建议使用 lower(replace(uuid(), ''-'', '''')) 生成,要求全局唯一,用于审计、追踪、吊销',

`token`                 VARCHAR(2048) DEFAULT NULL COMMENT '签发的JWT明文token。若后续考虑安全收紧,可改为不再保存明文,只保留 token_hash 与 token_preview',

`token_hash`            VARCHAR(255) DEFAULT NULL COMMENT 'token摘要值。用于校验和追踪,不暴露明文',

`token_preview`         VARCHAR(100) DEFAULT NULL COMMENT 'token脱敏预览值,例如前20位 + ... + 后10位,便于人工排查',

`issued_time`           DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'token签发时间',

`expire_time`           DATETIME NOT NULL COMMENT 'token过期时间',

`token_status`          TINYINT NOT NULL DEFAULT 1 COMMENT 'token状态:0-失效,1-有效,2-已吊销。若只需要简单模型,可只用0和1',


`auth_status`           TINYINT NOT NULL DEFAULT 1 COMMENT '本次login鉴权结果:0-失败,1-成功。即使鉴权失败,也建议记录日志,便于统计攻击和排障',

`auth_result_code`      VARCHAR(64) DEFAULT NULL COMMENT '鉴权结果码:SUCCESS/APP_KEY_NOT_FOUND/SECRET_INVALID/PARTNER_DISABLED/PARTNER_EXPIRED/SECRET_EXPIRED/IP_DENIED 等',

`auth_result_msg`       VARCHAR(1000) DEFAULT NULL COMMENT '鉴权结果说明',


`source_ip`             VARCHAR(50) DEFAULT NULL COMMENT '请求来源IP',

`request_body`          JSON DEFAULT NULL COMMENT '获取token时的请求体。注意不要原样落库明文 app_secret,建议入库前脱敏',

`response_body`         JSON DEFAULT NULL COMMENT '获取token时的响应体。注意若含完整token,需结合安全要求决定是否完整保存',


`remark`                VARCHAR(500) DEFAULT NULL COMMENT '备注',

`create_time`           DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',


PRIMARY KEY (`id`) USING BTREE,

UNIQUE KEY `uk_jti` (`jti`) USING BTREE,

KEY `idx_partner_id` (`partner_id`) USING BTREE,

KEY `idx_tenant_id` (`tenant_id`) USING BTREE,

KEY `idx_request_id` (`request_id`) USING BTREE,

KEY `idx_expire_time` (`expire_time`) USING BTREE,

KEY `idx_token_status` (`token_status`) USING BTREE,

KEY `idx_auth_status` (`auth_status`) USING BTREE,

KEY `idx_secret_version` (`secret_version`) USING BTREE

) ENGINE=InnoDB

DEFAULT CHARSET=utf8mb4

COLLATE=utf8mb4_0900_ai_ci COMMENT='JWT签发与login鉴权日志表';


重新设计,对方改了通信方式。在前面我刚跟你沟通过。你应该还记得,如果不记得,你告诉我不记得,我随后提供给你。请你按照新沟通的方式重新设计这几张表,必要的日志必须记录,便于追踪、审计。另外注释必须尽可能详尽,稍微做些字段冗余是可以的,但不要过度。你是一个顶级模型设计师。

AI答:

-- =========================================================
-- 说明:
-- 基于你前面刚补充的新通信方式,原先“app_key + app_secret 登录换 token”的模型已经不再适合作为核心认证模型,
-- 现在改为:
--
-- 1)通过统一社会信用代码关联租户关系
-- 2)请求方向目标系统申请 token 时,不再直接提交 app_key / app_secret
-- 3)而是提交一段由发起方服务端签发的 JWT
-- 4)JWT 内至少包含:
--    - 发起方租户ID(partner_tenant_id)
--    - 企业统一社会信用代码(enterprise_code)
--    - expire_time(过期时间)
--    - nonce(随机串,防重放)
-- 5)目标系统收到 JWT 后:
--    - 先做基础解析与时效校验
--    - 再调用发起方 verify 接口,验证该 JWT/签名是否确实由对方服务端签发
--    - 验证通过后,目标系统再签发自己的 access_token
--
-- 因此表结构也要随之调整:
-- 1)去掉以 app_key/app_secret 为中心的认证主模型
-- 2)强化 partner 主配置、verify 接口配置、连通性快照
-- 3)强化 SSO/JWT 换 token 主日志
-- 4)强化 verify 调用明细日志
-- 5)access_token 的签发、失效、吊销保留独立日志,便于后续审计、排障、统计
--
-- 设计原则:
-- 1)一个 partner 仍等于一家企业,但表名/字段名继续保留 partner,便于对外沟通
-- 2)tenant_id 仍是我们系统中的企业唯一编码
-- 3)enterprise_code 存统一社会信用代码,用来建立双方企业映射关系
-- 4)适度冗余关键字段,方便日志自解释,避免完全依赖主表追溯
-- 5)必要日志必须可追踪、可审计、可排障,但不过度堆字段
-- =========================================================

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- =========================================================
-- 对接企业主表
--
-- 用途:
-- 1)记录“一个 partner 就是一家企业”的主配置
-- 2)保存与新 SSO/JWT 通信方式有关的核心配置
-- 3)保存当前最新连通性快照
--
-- 这里不再以 app_key / app_secret 作为核心字段
-- 因为新方案的核心是:
-- “发起方服务端签发 JWT -> 目标系统调用发起方 verify 接口核验 -> 目标系统签发 access_token”
-- =========================================================
DROP TABLE IF EXISTS `bridge_partner`;
CREATE TABLE `bridge_partner`
(
    `id`                              BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',

    `tenant_id`                       BIGINT NOT NULL COMMENT '我们系统中的企业唯一编码。你已明确:tenant_id 实际就是企业ID',
    `enterprise_code`                 VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码。双方租户关系的核心匹配字段之一',
    `partner_code`                    VARCHAR(100) NOT NULL COMMENT '对接企业编码。虽然一个partner本质上等于一家企业,但仍保留该叫法,便于和领导、业务、接口文档持续沟通,例如 nuoming',
    `partner_name`                    VARCHAR(255) NOT NULL COMMENT '对接企业名称',
    `enterprise_name`                 VARCHAR(255) DEFAULT NULL COMMENT '企业名称冗余字段,便于查询、展示、日志排查',

    `partner_effective_time`          DATETIME DEFAULT NULL COMMENT '企业对接生效时间。为空表示不限制起始时间',
    `partner_expire_time`             DATETIME DEFAULT NULL COMMENT '企业对接失效时间。超过该时间后,不允许再进行JWT换token、业务调用等动作',

    `api_host_test`                   VARCHAR(500) DEFAULT NULL COMMENT '对方测试环境基础地址,例如 https://test.xxx.com',
    `api_host_production`             VARCHAR(500) DEFAULT NULL COMMENT '对方正式环境基础地址,例如 https://api.xxx.com',

    `verify_path`                     VARCHAR(500) DEFAULT NULL COMMENT '对方用于“验证JWT是否由其服务端签发”的 verify 接口路径。建议只存相对路径,如 /open-api/sso/verify,实际调用时再按环境拼接host',
    `verify_http_method`              VARCHAR(20) NOT NULL DEFAULT 'POST' COMMENT 'verify接口请求方式,通常为POST',
    `verify_timeout_ms`               INT NOT NULL DEFAULT 5000 COMMENT '调用对方verify接口的超时时间,单位毫秒',
    `verify_retry_count`              INT NOT NULL DEFAULT 0 COMMENT 'verify接口失败后的最大重试次数。0表示失败后不重试',
    `verify_retry_interval_ms`        INT NOT NULL DEFAULT 0 COMMENT 'verify接口重试间隔,单位毫秒。只有 verify_retry_count > 0 时才有意义',

    `jwt_alg`                         VARCHAR(30) DEFAULT NULL COMMENT '约定的JWT算法标识,例如 HS256、RS256。该字段主要用于协议记录和基础校验,不代表一定在本地完成完整签名验签',
    `jwt_issuer`                      VARCHAR(200) DEFAULT NULL COMMENT '约定的JWT签发者issuer。若协议里要求校验iss,可在基础校验时使用',
    `jwt_audience`                    VARCHAR(200) DEFAULT NULL COMMENT '约定的JWT受众audience。若协议里要求校验aud,可在基础校验时使用',
    `jwt_max_valid_seconds`           INT NOT NULL DEFAULT 300 COMMENT '发起方JWT允许的最大有效期秒数。用于防止对方签发一个过长生命周期的JWT',
    `allowed_clock_skew_seconds`      INT NOT NULL DEFAULT 300 COMMENT '服务端允许的时钟偏差秒数。用于处理双方服务器时间略有误差的情况',
    `nonce_expire_seconds`            INT NOT NULL DEFAULT 600 COMMENT 'nonce防重放有效窗口,单位秒。超过该窗口的请求即便JWT未彻底过期,也可视业务拒绝',
    `token_expire_seconds`            INT NOT NULL DEFAULT 7200 COMMENT '我方在verify通过后签发给对方的 access_token 默认有效期,单位秒',
    `ip_allow_list`                   VARCHAR(2000) DEFAULT NULL COMMENT '来源IP白名单,多个以英文逗号分隔。为空表示不限制。用于限制谁可以调用JWT换token接口',
    `status`                          TINYINT NOT NULL DEFAULT 1 COMMENT '记录状态:0-停用,1-启用。停用后不允许JWT换token、不允许业务接口交互',

    `oa_connect_status`               TINYINT NOT NULL DEFAULT 0 COMMENT '当前最新连通状态快照:0-未测试,1-成功,2-失败',
    `oa_connect_env`                  VARCHAR(20) DEFAULT NULL COMMENT '最近一次测试环境:TEST/PRODUCTION',
    `oa_connect_test_type`            VARCHAR(30) DEFAULT NULL COMMENT '最近一次测试类型:HOST_PING-基础地址探测;VERIFY_API-verify接口探测;TOKEN_EXCHANGE-完整换token链路测试',
    `oa_connect_time`                 DATETIME DEFAULT NULL COMMENT '最近一次点击“连通对方OA”或系统自动探测的时间',
    `oa_connect_result_code`          VARCHAR(64) DEFAULT NULL COMMENT '最近一次连通测试结果码,如 SUCCESS/TIMEOUT/DNS_ERROR/HTTP_403/HTTP_500/VERIFY_FAIL 等',
    `oa_connect_result_msg`           VARCHAR(1000) DEFAULT NULL COMMENT '最近一次连通测试结果说明',
    `oa_connect_response_time_ms`     INT DEFAULT NULL COMMENT '最近一次连通测试总耗时,单位毫秒',
    `oa_connect_by`                   BIGINT DEFAULT NULL COMMENT '最近一次执行连通测试的操作人。若为系统自动任务,可为空或写系统用户ID',

    `remark`                          VARCHAR(1000) DEFAULT NULL COMMENT '备注,可记录该企业的特殊对接说明',
    `created_by`                      BIGINT DEFAULT NULL COMMENT '创建人',
    `updated_by`                      BIGINT DEFAULT NULL COMMENT '更新人',
    `is_deleted`                      TINYINT NOT NULL DEFAULT 0 COMMENT '是否逻辑删除:0否,1是',
    `deleted_time`                    DATETIME DEFAULT NULL COMMENT '逻辑删除时间',
    `create_time`                     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`                     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

    PRIMARY KEY (`id`) USING BTREE,
    UNIQUE KEY `uk_tenant_id` (`tenant_id`) USING BTREE,
    UNIQUE KEY `uk_partner_code` (`partner_code`) USING BTREE,
    UNIQUE KEY `uk_enterprise_code` (`enterprise_code`) USING BTREE,

    KEY `idx_partner_name` (`partner_name`) USING BTREE,
    KEY `idx_status` (`status`) USING BTREE,
    KEY `idx_partner_expire_time` (`partner_expire_time`) USING BTREE,
    KEY `idx_oa_connect_status` (`oa_connect_status`) USING BTREE
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_0900_ai_ci COMMENT='对接企业主表(新SSO/JWT模式主配置表,一个partner即一家企业)';


-- =========================================================
-- 对方OA / verify接口 连通性测试日志表
--
-- 用途:
-- 1)记录每次点击“连通对方OA”的完整历史
-- 2)记录测试的是基础地址、verify接口,还是完整换token链路
-- 3)记录失败原因、HTTP状态、响应耗时、操作人
--
-- 说明:
-- bridge_partner 上只保留“最新快照”
-- 本表保留“全量历史”
-- =========================================================
DROP TABLE IF EXISTS `bridge_partner_connectivity_log`;
CREATE TABLE `bridge_partner_connectivity_log`
(
    `id`                           BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
    `tenant_id`                    BIGINT NOT NULL COMMENT '企业唯一编码',
    `enterprise_code`              VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码,冗余保存便于按信用代码排查',
    `partner_id`                   BIGINT NOT NULL COMMENT '关联 bridge_partner.id',

    `connect_env`                  VARCHAR(20) NOT NULL COMMENT '测试环境:TEST/PRODUCTION',
    `test_type`                    VARCHAR(30) NOT NULL COMMENT '测试类型:HOST_PING-基础地址探测;VERIFY_API-verify接口探测;TOKEN_EXCHANGE-完整换token测试',
    `target_host`                  VARCHAR(500) DEFAULT NULL COMMENT '本次测试命中的host,例如测试环境或正式环境基础地址',
    `target_path`                  VARCHAR(500) DEFAULT NULL COMMENT '本次测试命中的接口路径。例如 verify_path 或 token exchange path',
    `target_url`                   VARCHAR(1000) DEFAULT NULL COMMENT '本次实际请求的完整URL,便于日志直接排查',
    `http_method`                  VARCHAR(20) DEFAULT NULL COMMENT '请求方式,例如 GET/POST',
    `connect_status`               TINYINT NOT NULL DEFAULT 0 COMMENT '测试结果:0-失败,1-成功',
    `result_code`                  VARCHAR(64) DEFAULT NULL COMMENT '结果码:SUCCESS/TIMEOUT/DNS_ERROR/SSL_ERROR/HTTP_401/HTTP_403/HTTP_404/HTTP_500/VERIFY_FAIL 等',
    `result_msg`                   VARCHAR(1000) DEFAULT NULL COMMENT '结果说明,例如“DNS解析失败”“接口返回403”“verify接口业务校验失败”等',
    `http_status_code`             INT DEFAULT NULL COMMENT 'HTTP状态码。若请求未发出成功,可为空',
    `response_time_ms`             INT DEFAULT NULL COMMENT '本次测试总耗时,单位毫秒',

    `request_id`                   VARCHAR(100) DEFAULT NULL COMMENT '请求流水号,用于串联应用日志',
    `trace_id`                     VARCHAR(100) DEFAULT NULL COMMENT '链路追踪号。若系统接入统一trace体系,可用于串联跨服务日志',
    `request_body`                 JSON DEFAULT NULL COMMENT '请求报文。注意不要塞入敏感明文',
    `response_body`                JSON DEFAULT NULL COMMENT '响应报文。建议保留必要字段即可,避免过大',
    `operator_id`                  BIGINT DEFAULT NULL COMMENT '操作人ID。若为系统自动探测可为空',
    `operator_name`                VARCHAR(100) DEFAULT NULL COMMENT '操作人姓名。若为系统自动探测可写 SYSTEM',
    `source_ip`                    VARCHAR(50) DEFAULT NULL COMMENT '操作来源IP',
    `remark`                       VARCHAR(500) DEFAULT NULL COMMENT '备注',
    `create_time`                  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

    PRIMARY KEY (`id`) USING BTREE,
    KEY `idx_partner_id` (`partner_id`) USING BTREE,
    KEY `idx_tenant_id` (`tenant_id`) USING BTREE,
    KEY `idx_connect_env` (`connect_env`) USING BTREE,
    KEY `idx_test_type` (`test_type`) USING BTREE,
    KEY `idx_connect_status` (`connect_status`) USING BTREE,
    KEY `idx_request_id` (`request_id`) USING BTREE,
    KEY `idx_trace_id` (`trace_id`) USING BTREE
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_0900_ai_ci COMMENT='对方OA与verify接口连通性测试日志表';


-- =========================================================
-- SSO/JWT 换 token 主日志表
--
-- 这是新通信方式下最核心的一张日志表。
--
-- 记录一个完整链路:
-- 1)谁来申请
-- 2)提交了什么JWT关键信息
-- 3)基础校验是否通过
-- 4)nonce 是否命中重放
-- 5)verify接口是否调用成功、业务是否验证通过
-- 6)最终是否签发 access_token
--
-- 设计上把它当做“主流水表”
-- 而 verify 接口每次重试/调用明细,放到子表 bridge_sso_verify_log 里
-- =========================================================
DROP TABLE IF EXISTS `bridge_sso_exchange_log`;
CREATE TABLE `bridge_sso_exchange_log`
(
    `id`                              BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
    `request_id`                      VARCHAR(100) NOT NULL COMMENT '本次JWT换token请求流水号,建议全局唯一。用于串联应用日志、verify日志、token日志',
    `trace_id`                        VARCHAR(100) DEFAULT NULL COMMENT '链路追踪号。若网关或链路系统已生成,可直接落库',

    `tenant_id`                       BIGINT DEFAULT NULL COMMENT '匹配到的我方企业唯一编码。若请求尚未成功映射到我方企业,可为空',
    `enterprise_code`                 VARCHAR(100) DEFAULT NULL COMMENT '请求中携带的企业统一社会信用代码,或根据partner配置映射到的信用代码',
    `partner_id`                      BIGINT DEFAULT NULL COMMENT '关联 bridge_partner.id。若还没匹配到企业,可为空',
    `partner_code`                    VARCHAR(100) DEFAULT NULL COMMENT '对接企业编码冗余,便于直接查看日志',
    `partner_name`                    VARCHAR(255) DEFAULT NULL COMMENT '对接企业名称冗余,便于直接查看日志',

    `exchange_direction`              VARCHAR(20) NOT NULL DEFAULT 'INBOUND' COMMENT '换token方向:INBOUND-对方拿JWT来换我方token;OUTBOUND-我方向对方提交JWT换对方token。当前场景通常是 INBOUND',
    `exchange_scene`                  VARCHAR(50) NOT NULL DEFAULT 'SSO_TOKEN_EXCHANGE' COMMENT '业务场景。当前固定可写 SSO_TOKEN_EXCHANGE,便于未来扩展',
    `exchange_env`                    VARCHAR(20) DEFAULT NULL COMMENT '执行环境:TEST/PRODUCTION',

    `source_ip`                       VARCHAR(50) DEFAULT NULL COMMENT '请求来源IP',
    `http_method`                     VARCHAR(20) DEFAULT NULL COMMENT '本次换token接口请求方式,通常为POST',
    `request_url`                     VARCHAR(1000) DEFAULT NULL COMMENT '本次请求命中的我方接口URL',
    `request_body`                    JSON DEFAULT NULL COMMENT '原始请求体。注意可存JWT脱敏值,不建议长期落完整超长敏感报文',

    `jwt_raw_preview`                 VARCHAR(500) DEFAULT NULL COMMENT '请求JWT脱敏预览值,例如前30位...后20位,便于人工排查',
    `jwt_hash`                        VARCHAR(255) DEFAULT NULL COMMENT '请求JWT摘要值。用于去重、追踪,不直接暴露原文',
    `jwt_alg`                         VARCHAR(30) DEFAULT NULL COMMENT '从JWT头部解析出的算法标识',
    `jwt_issuer`                      VARCHAR(200) DEFAULT NULL COMMENT '从JWT claim中解析出的iss。若无则为空',
    `jwt_audience`                    VARCHAR(200) DEFAULT NULL COMMENT '从JWT claim中解析出的aud。若无则为空',
    `jwt_jti`                         VARCHAR(100) DEFAULT NULL COMMENT '从JWT claim中解析出的jti。若协议未要求可为空',
    `partner_tenant_id`               VARCHAR(100) DEFAULT NULL COMMENT '请求JWT里携带的发起方租户ID。这里保留字符串,兼容对方租户ID不一定是纯数字的情况',
    `request_enterprise_code`         VARCHAR(100) DEFAULT NULL COMMENT '请求JWT里携带的统一社会信用代码原值',
    `request_expire_time`             DATETIME DEFAULT NULL COMMENT '请求JWT里声明的过期时间。注意这是对方JWT本身的过期时间,不是我方access_token过期时间',
    `request_nonce`                   VARCHAR(200) DEFAULT NULL COMMENT '请求JWT里携带的nonce,用于防重放',

    `parse_status`                    TINYINT NOT NULL DEFAULT 0 COMMENT 'JWT解析结果:0-失败,1-成功',
    `parse_result_code`               VARCHAR(64) DEFAULT NULL COMMENT 'JWT解析结果码:SUCCESS/JWT_EMPTY/JWT_FORMAT_INVALID/JWT_DECODE_FAIL/JWT_HEADER_INVALID/JWT_CLAIM_INVALID 等',
    `parse_result_msg`                VARCHAR(1000) DEFAULT NULL COMMENT 'JWT解析结果说明',

    `claim_validate_status`           TINYINT NOT NULL DEFAULT 0 COMMENT 'claim基础校验结果:0-失败,1-成功',
    `claim_validate_code`             VARCHAR(64) DEFAULT NULL COMMENT 'claim校验结果码:SUCCESS/PARTNER_NOT_FOUND/ENTERPRISE_CODE_MISMATCH/EXPIRE_TIME_MISSING/JWT_EXPIRED/CLOCK_SKEW_INVALID/NONCE_MISSING/IP_DENIED/PARTNER_DISABLED/PARTNER_EXPIRED 等',
    `claim_validate_msg`              VARCHAR(1000) DEFAULT NULL COMMENT 'claim校验结果说明',
    `clock_skew_seconds`              INT DEFAULT NULL COMMENT '本次请求中,服务端计算出的时间偏差秒数。用于排查双方机器时间不一致问题',
    `nonce_check_status`              TINYINT NOT NULL DEFAULT 0 COMMENT 'nonce校验结果:0-未校验或失败,1-成功',
    `nonce_check_code`                VARCHAR(64) DEFAULT NULL COMMENT 'nonce校验结果码:SUCCESS/NONCE_MISSING/NONCE_REPLAY/NONCE_EXPIRED/NONCE_INVALID 等',
    `nonce_check_msg`                 VARCHAR(1000) DEFAULT NULL COMMENT 'nonce校验结果说明',

    `verify_status`                   TINYINT NOT NULL DEFAULT 0 COMMENT '调用对方verify接口后的最终验证结果:0-失败,1-成功',
    `verify_result_code`              VARCHAR(64) DEFAULT NULL COMMENT 'verify最终结果码:SUCCESS/VERIFY_API_TIMEOUT/VERIFY_HTTP_FAIL/VERIFY_RESPONSE_INVALID/VERIFY_BUSINESS_REJECT 等',
    `verify_result_msg`               VARCHAR(1000) DEFAULT NULL COMMENT 'verify最终结果说明',
    `verify_attempt_count`            INT NOT NULL DEFAULT 0 COMMENT '本次换token流程中,verify接口共尝试调用了多少次。包括重试次数',
    `verify_success_time`             DATETIME DEFAULT NULL COMMENT 'verify最终成功时间。若未成功则为空',

    `token_issue_status`              TINYINT NOT NULL DEFAULT 0 COMMENT '我方access_token签发结果:0-未签发或失败,1-签发成功',
    `token_issue_code`                VARCHAR(64) DEFAULT NULL COMMENT '签发结果码:SUCCESS/VERIFY_NOT_PASS/TOKEN_GENERATE_FAIL/TOKEN_SAVE_FAIL/PARTNER_DISABLED 等',
    `token_issue_msg`                 VARCHAR(1000) DEFAULT NULL COMMENT '签发结果说明',
    `issued_token_log_id`             BIGINT DEFAULT NULL COMMENT '若签发成功,对应 bridge_access_token_log.id,便于回查',
    `issued_access_token_preview`     VARCHAR(200) DEFAULT NULL COMMENT '签发成功后的token脱敏预览值,例如前20位...后10位,便于排查',
    `issued_expire_time`              DATETIME DEFAULT NULL COMMENT '本次签发的我方access_token过期时间',

    `final_status`                    VARCHAR(30) NOT NULL DEFAULT 'PROCESSING' COMMENT '整条换token链路最终状态:PROCESSING-处理中;SUCCESS-成功;FAILED-失败',
    `final_result_code`               VARCHAR(64) DEFAULT NULL COMMENT '整条链路最终结果码。通常可直接用来做统计报表',
    `final_result_msg`                VARCHAR(1000) DEFAULT NULL COMMENT '整条链路最终结果说明',
    `finished_time`                   DATETIME DEFAULT NULL COMMENT '整条链路完成时间。无论成功还是失败,只要流程结束就应写入',

    `remark`                          VARCHAR(500) DEFAULT NULL COMMENT '备注',
    `create_time`                     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`                     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

    PRIMARY KEY (`id`) USING BTREE,
    UNIQUE KEY `uk_request_id` (`request_id`) USING BTREE,
    KEY `idx_trace_id` (`trace_id`) USING BTREE,
    KEY `idx_partner_id` (`partner_id`) USING BTREE,
    KEY `idx_tenant_id` (`tenant_id`) USING BTREE,
    KEY `idx_enterprise_code` (`enterprise_code`) USING BTREE,
    KEY `idx_request_nonce` (`request_nonce`) USING BTREE,
    KEY `idx_jwt_jti` (`jwt_jti`) USING BTREE,
    KEY `idx_verify_status` (`verify_status`) USING BTREE,
    KEY `idx_token_issue_status` (`token_issue_status`) USING BTREE,
    KEY `idx_final_status` (`final_status`) USING BTREE,
    KEY `idx_create_time` (`create_time`) USING BTREE
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_0900_ai_ci COMMENT='SSO/JWT换token主日志表';


-- =========================================================
-- verify 接口调用明细日志表
--
-- 为什么单独拆出来:
-- 1)一次换token流程里,verify接口可能会重试多次
-- 2)主表 bridge_sso_exchange_log 只保留“最终结果”和“总尝试次数”
-- 3)本表保留每一次 verify 调用明细,便于排查:
--    - 第几次调用
--    - 调的是哪个URL
--    - 发了什么
--    - 返回了什么
--    - 是网络失败,还是业务拒绝
-- =========================================================
DROP TABLE IF EXISTS `bridge_sso_verify_log`;
CREATE TABLE `bridge_sso_verify_log`
(
    `id`                           BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
    `exchange_log_id`              BIGINT NOT NULL COMMENT '关联 bridge_sso_exchange_log.id',
    `request_id`                   VARCHAR(100) NOT NULL COMMENT '换token主请求流水号,冗余保存,便于不连表排查',
    `trace_id`                     VARCHAR(100) DEFAULT NULL COMMENT '链路追踪号',

    `tenant_id`                    BIGINT DEFAULT NULL COMMENT '企业唯一编码。若请求早期就失败且未映射成功,可为空',
    `enterprise_code`              VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码冗余',
    `partner_id`                   BIGINT DEFAULT NULL COMMENT '关联 bridge_partner.id',
    `partner_code`                 VARCHAR(100) DEFAULT NULL COMMENT '对接企业编码冗余',
    `partner_name`                 VARCHAR(255) DEFAULT NULL COMMENT '对接企业名称冗余',

    `verify_env`                   VARCHAR(20) DEFAULT NULL COMMENT '调用环境:TEST/PRODUCTION',
    `attempt_no`                   INT NOT NULL COMMENT '第几次调用,从1开始递增',
    `target_host`                  VARCHAR(500) DEFAULT NULL COMMENT '本次调用命中的host',
    `target_path`                  VARCHAR(500) DEFAULT NULL COMMENT '本次调用命中的verify路径',
    `target_url`                   VARCHAR(1000) DEFAULT NULL COMMENT '本次调用的完整URL',
    `http_method`                  VARCHAR(20) DEFAULT NULL COMMENT '请求方式,通常为POST',

    `request_jwt_preview`          VARCHAR(500) DEFAULT NULL COMMENT '本次提交给verify接口的JWT脱敏预览值',
    `request_nonce`                VARCHAR(200) DEFAULT NULL COMMENT '本次请求关联的nonce',
    `request_body`                 JSON DEFAULT NULL COMMENT '本次调用verify接口的请求报文',
    `response_body`                JSON DEFAULT NULL COMMENT '本次调用verify接口的响应报文',

    `call_status`                  TINYINT NOT NULL DEFAULT 0 COMMENT '本次调用是否成功拿到“可解析响应”:0-失败,1-成功',
    `result_code`                  VARCHAR(64) DEFAULT NULL COMMENT '结果码:SUCCESS/TIMEOUT/CONNECT_FAIL/HTTP_403/HTTP_500/RESPONSE_INVALID/BUSINESS_REJECT 等',
    `result_msg`                   VARCHAR(1000) DEFAULT NULL COMMENT '结果说明',
    `http_status_code`             INT DEFAULT NULL COMMENT 'HTTP状态码',
    `business_verify_status`       TINYINT DEFAULT NULL COMMENT '对方业务层验签结果:0-失败,1-成功。只有拿到业务响应时才有意义',
    `business_verify_code`         VARCHAR(64) DEFAULT NULL COMMENT '对方业务层返回的结果码,例如 SUCCESS/SIGN_INVALID/JWT_EXPIRED/NONCE_INVALID 等',
    `business_verify_msg`          VARCHAR(1000) DEFAULT NULL COMMENT '对方业务层返回的结果说明',
    `response_time_ms`             INT DEFAULT NULL COMMENT '本次verify调用耗时,单位毫秒',

    `create_time`                  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

    PRIMARY KEY (`id`) USING BTREE,
    KEY `idx_exchange_log_id` (`exchange_log_id`) USING BTREE,
    KEY `idx_request_id` (`request_id`) USING BTREE,
    KEY `idx_partner_id` (`partner_id`) USING BTREE,
    KEY `idx_attempt_no` (`attempt_no`) USING BTREE,
    KEY `idx_call_status` (`call_status`) USING BTREE,
    KEY `idx_business_verify_status` (`business_verify_status`) USING BTREE,
    KEY `idx_create_time` (`create_time`) USING BTREE
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_0900_ai_ci COMMENT='verify接口调用明细日志表';


-- =========================================================
-- access_token 签发日志表
--
-- 说明:
-- 1)新模式下,access_token 仍然是需要独立管理和审计的
-- 2)但它的来源不再是“app_key/app_secret login”
-- 3)而是“JWT + verify成功”之后的签发结果
--
-- 本表用于:
-- 1)记录签发了什么token
-- 2)何时签发、何时过期
-- 3)当前是否有效、是否吊销
-- 4)这个token来源于哪次 exchange 请求
--
-- 安全建议:
-- 1)建议长期只保留 token_hash + token_preview
-- 2)若当前阶段为了排障方便保留 token 明文,也建议后续收紧
-- =========================================================
DROP TABLE IF EXISTS `bridge_access_token_log`;
CREATE TABLE `bridge_access_token_log`
(
    `id`                           BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
    `exchange_log_id`              BIGINT DEFAULT NULL COMMENT '关联 bridge_sso_exchange_log.id。表示该token由哪次JWT换token流程签发出来',
    `request_id`                   VARCHAR(100) DEFAULT NULL COMMENT '换token请求流水号冗余,便于不连表排查',
    `trace_id`                     VARCHAR(100) DEFAULT NULL COMMENT '链路追踪号',

    `tenant_id`                    BIGINT NOT NULL COMMENT '企业唯一编码',
    `enterprise_code`              VARCHAR(100) DEFAULT NULL COMMENT '企业统一社会信用代码',
    `partner_id`                   BIGINT NOT NULL COMMENT '关联 bridge_partner.id',
    `partner_code`                 VARCHAR(100) DEFAULT NULL COMMENT '对接企业编码冗余',
    `partner_name`                 VARCHAR(255) DEFAULT NULL COMMENT '对接企业名称冗余',

    `grant_type`                   VARCHAR(50) NOT NULL DEFAULT 'sso_jwt_exchange' COMMENT '授权来源类型。这里不再是传统 client_credentials,而是固定记为 sso_jwt_exchange,表示由SSO/JWT换token流程签发',
    `token_jti`                    VARCHAR(100) NOT NULL COMMENT '我方签发的access_token唯一编号。建议全局唯一,如 lower(replace(uuid(), ''-'', ''''))',
    `token`                        VARCHAR(2048) DEFAULT NULL COMMENT '签发的access_token明文。当前阶段如为排障方便可保留;后续建议关闭明文落库',
    `token_hash`                   VARCHAR(255) DEFAULT NULL COMMENT 'access_token摘要值。用于追踪、比对,不暴露明文',
    `token_preview`                VARCHAR(200) DEFAULT NULL COMMENT 'access_token脱敏预览值,例如前20位...后10位,便于人工排查',

    `issued_time`                  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '签发时间',
    `effective_time`               DATETIME DEFAULT NULL COMMENT '生效时间。通常等于签发时间;若存在延时生效策略可单独记录',
    `expire_time`                  DATETIME NOT NULL COMMENT '过期时间',
    `token_status`                 TINYINT NOT NULL DEFAULT 1 COMMENT 'token状态:0-失效,1-有效,2-已吊销',
    `revoke_time`                  DATETIME DEFAULT NULL COMMENT '吊销时间',
    `revoke_reason`                VARCHAR(500) DEFAULT NULL COMMENT '吊销原因,例如“企业停用”“安全事件”“人工禁用”',

    `source_ip`                    VARCHAR(50) DEFAULT NULL COMMENT '触发签发时的来源IP',
    `remark`                       VARCHAR(500) DEFAULT NULL COMMENT '备注',
    `create_time`                  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`                  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

    PRIMARY KEY (`id`) USING BTREE,
    UNIQUE KEY `uk_token_jti` (`token_jti`) USING BTREE,
    KEY `idx_exchange_log_id` (`exchange_log_id`) USING BTREE,
    KEY `idx_request_id` (`request_id`) USING BTREE,
    KEY `idx_partner_id` (`partner_id`) USING BTREE,
    KEY `idx_tenant_id` (`tenant_id`) USING BTREE,
    KEY `idx_expire_time` (`expire_time`) USING BTREE,
    KEY `idx_token_status` (`token_status`) USING BTREE,
    KEY `idx_create_time` (`create_time`) USING BTREE
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_0900_ai_ci COMMENT='access_token签发与状态日志表(来源于SSO/JWT换token)';

SET FOREIGN_KEY_CHECKS = 1;

再补一句我对这次“新沟通方式”的理解:我记得核心已经从 app_key/app_secret 切到了“JWT + verify接口二次确认”的模式,所以我按这个方向重构了。如果你前面刚沟通的新协议里,还额外明确了某些固定字段名、verify接口入参出参、或者要求同时支持“我方向对方换token”和“对方向我方换token”双向统一模型,我可以直接在这版基础上继续给你收敛成最终版,不需要再推倒重来。