实现七月迭代公共技术基础
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s

This commit is contained in:
2026-07-23 17:52:48 +09:00
parent f7c42252c0
commit 17782d5f8e
76 changed files with 5246 additions and 158 deletions

View File

@@ -5406,9 +5406,18 @@ components:
download_url:
description: 下载链接(仅已完成任务返回)
type: string
error_code:
description: 安全错误码
type: string
error_message:
description: 错误信息
type: string
error_summary:
description: 安全失败摘要
type: string
failed_count:
description: 统一任务失败数
type: integer
failed_shards:
description: 失败分片数
type: integer
@@ -5442,18 +5451,32 @@ components:
status_name:
description: 任务状态名称(中文)
type: string
success_count:
description: 统一任务成功数
type: integer
success_shards:
description: 成功分片数
type: integer
task_id:
description: 任务ID
minimum: 0
type: integer
task_no:
description: 任务编号
type: string
total_count:
description: 统一任务总数
type: integer
total_rows:
description: 总行数
type: integer
total_shards:
description: 总分片数
type: integer
updated_at:
description: 更新时间
format: date-time
type: string
type: object
DtoExportTaskItem:
properties:
@@ -5486,9 +5509,18 @@ components:
creator_user_type:
description: 创建人用户类型 (2:平台, 3:代理, 4:企业)
type: integer
error_code:
description: 安全错误码
type: string
error_message:
description: 错误信息
type: string
error_summary:
description: 安全失败摘要
type: string
failed_count:
description: 统一任务失败数
type: integer
failed_shards:
description: 失败分片数
type: integer
@@ -5522,18 +5554,32 @@ components:
status_name:
description: 任务状态名称(中文)
type: string
success_count:
description: 统一任务成功数
type: integer
success_shards:
description: 成功分片数
type: integer
task_id:
description: 任务ID
minimum: 0
type: integer
task_no:
description: 任务编号
type: string
total_count:
description: 统一任务总数
type: integer
total_rows:
description: 总行数
type: integer
total_shards:
description: 总分片数
type: integer
updated_at:
description: 更新时间
format: date-time
type: string
type: object
DtoFailedDeviceItem:
properties:
@@ -8680,6 +8726,72 @@ components:
required:
- target_iccid
type: object
DtoSystemConfigItem:
properties:
config_key:
description: 稳定配置 Key
type: string
control:
description: 前端控件提示
type: string
description:
description: 中文说明
type: string
enum_values:
description: 允许的枚举值
items:
type: string
type: array
max:
description: 整数最大值
nullable: true
type: integer
min:
description: 整数最小值
nullable: true
type: integer
module:
description: 所属模块
type: string
readonly:
description: 是否只读
type: boolean
registered:
description: 是否已在代码注册
type: boolean
sensitive:
description: 是否敏感
type: boolean
updated_at:
description: 最近更新时间
format: date-time
nullable: true
type: string
value:
description: 配置值;敏感值按注册策略脱敏
type: string
value_type:
description: 值类型 (string:字符串, int:整数, bool:布尔, json:JSON)
type: string
type: object
DtoSystemConfigListResponse:
properties:
list:
description: 配置列表
items:
$ref: '#/components/schemas/DtoSystemConfigItem'
nullable: true
type: array
page:
description: 页码
type: integer
page_size:
description: 每页数量
type: integer
total:
description: 总数量
type: integer
type: object
DtoTriggerBatchReq:
properties:
card_ids:
@@ -9389,6 +9501,18 @@ components:
required:
- status
type: object
DtoUpdateSystemConfigParams:
properties:
key:
description: 稳定配置 Key
type: string
value:
description: 字符串化配置值
type: string
required:
- key
- value
type: object
DtoUpdateWechatConfigParams:
properties:
ali_app_id:
@@ -24676,6 +24800,157 @@ paths:
summary: 查询操作密码是否已设置
tags:
- 超级管理员
/api/admin/system-configs:
get:
parameters:
- description: 模块筛选
in: query
name: module
schema:
description: 模块筛选
type: string
- description: 页码
in: query
name: page
schema:
description: 页码
minimum: 1
type: integer
- description: 每页数量
in: query
name: page_size
schema:
description: 每页数量
maximum: 100
minimum: 1
type: integer
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoSystemConfigListResponse'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- BearerAuth: []
summary: 查询受控系统配置
tags:
- 系统配置
/api/admin/system-configs/{key}:
put:
parameters:
- description: 稳定配置 Key
in: path
name: key
required: true
schema:
description: 稳定配置 Key
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoUpdateSystemConfigParams'
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoSystemConfigItem'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- BearerAuth: []
summary: 更新受控系统配置
tags:
- 系统配置
/api/admin/wechat-configs:
get:
parameters:

View File

@@ -0,0 +1,19 @@
# 创建命令幂等与并发职责契约
创建类命令使用“调用主体 + 操作类型 + `request_id`”作为入口身份,并把影响业务结果的专用命令 DTO 计算为带版本的 SHA-256 指纹。时间戳、签名、Token、Authorization、Cookie 和 Nonce 等易变传输字段不进入指纹;业务模块应尽量直接传专用 DTO避免把任意 HTTP 请求整体作为业务身份。
同一作用域中,相同 `request_id` 与相同指纹返回已保存的业务结果;指纹不同返回统一冲突且不得覆盖原事实。不同主体或不同操作类型即使复用同一 `request_id`,也属于不同命令。
应用层预查只改善返回体验。并发首写必须由业务表或业务幂等事实上的 PostgreSQL 唯一约束裁决,并与业务事实处于同一事务。公共基础不创建万能幂等表,业务模块自行保存结果定位和稳定业务唯一键。
各类身份与并发职责不得混用:
| 构件 | 唯一职责 |
|---|---|
| `request_id` + 指纹 | 识别入口命令重放与内容冲突 |
| `event_id` | 识别至少一次投递中的同一事件 |
| 业务唯一键/唯一流水 | 防止同一业务副作用重复落库 |
| 状态条件更新 | 裁决状态机是否可从预期状态推进 |
| 钱包 `version` | 裁决余额、冻结额等并发数值更新 |
| Worker 租约 | 暂时决定当前处理者,不证明副作用尚未发生 |
| Redis 锁或 `SETNX` | 降低热点并发;故障、过期或切主不能破坏最终正确性 |

View File

@@ -0,0 +1,69 @@
# 公共技术基础功能总结
## 交付边界
本能力提供公共 Outbox 与 Relay、创建命令幂等原语、统一异步任务五态、受控系统配置、数据库发布门禁以及 Access Log 递归脱敏和敏感路由安全摘要。公共基础只定义稳定接缝和基础设施语义,不拥有 Audit Event、Integration Log、站内通知、业务模型、业务状态机、业务唯一键、任务失败明细或业务事件消费者。
各业务 PRD 必须自行实现消费者副作用幂等,并决定使用事件 ID、业务唯一键、状态条件更新或版本号裁决重复消费。审计和通知由对应公共能力实现后注入现有 Port系统配置更新在审计 Port 不可用时失败关闭,不另建临时审计表。
## 关键流程
### 事务事件与至少一次投递
业务 Application 在同一 GORM 事务中写入业务事实和公共 Outbox事务内不调用 Redis、Asynq、HTTP 或对象存储。Relay 使用行锁跳过竞争记录并写入租约,再通过统一队列客户端把结构化信封交给 Asynq。公开 Handler 按稳定事件类型分发给业务消费者;入队成功但状态未落库时允许使用原事件 ID、载荷和关联标识重复投递。
瞬时错误采用有上限的指数退避;达到最大次数或收到明确永久错误后保留最终失败事实。租约过期后其他实例可以恢复领取,有效租约不能被人工释放或其他实例完成。
### 幂等与异步任务
创建命令指纹只包含影响业务结果的规范化字段并携带算法版本。PostgreSQL 唯一约束是并发首次提交的最终裁决Redis 只用于减少并发和改善体验。请求 ID、事件 ID、业务唯一键、状态条件、数值版本和 Worker 租约不得互相替代。
异步任务固定为 `1=待处理、2=处理中、3=已完成、4=已失败、5=已取消`。任务表由各业务拥有,公共 Adapter 通过可配置列映射执行条件领取、租约恢复、完成和取消,不建设万能任务表。部分成功或业务项全部失败仍为已完成,并满足总数等于成功数加失败数。
### 受控系统配置
业务模块在代码注册表中声明稳定 Key、类型、默认值、值域、只读和敏感策略。PostgreSQL 是唯一事实来源Redis 仅作短缓存;缓存异常会回退数据库并产生不含配置值的安全告警。只有超级管理员可以分页查询或更新,未注册 Key 强制只读,敏感值只返回“已配置”。更新事实和审计接缝处于同一事务,提交后只失效对应缓存 Key。
### 日志安全
Access Log 对 query、请求 JSON 和响应 JSON 复用大小写不敏感的递归脱敏规则,并在脱敏后执行 50KB 截断。登录、支付、企微回调、文件和导出路由只记录白名单安全摘要JSON、XML、表单、multipart、二进制和无法解析载荷均不得回退记录原文。
## 异常闭环与运行监控
发布和运行监控至少包含 Outbox 待投递量、最老待投递年龄、处理中数量、过期租约、成功率、重试分布、最终失败和按事件类型积压。日志和指标只使用事件 ID、关联 ID、安全资源标识、错误码和计数禁止把完整载荷或敏感值作为标签。
人工恢复只接受显式选中的失败或过期租约事件。重放保留原事件身份与内容,记录操作者、中文原因和恢复批次;审计写入失败时整个恢复事务回滚。持续积压、租约大量过期、配置读写不一致、脱敏回归失败或关键任务无法恢复时停止放量并保持事实不变。
## 发布与回滚
发布顺序固定如下:
1. 执行只读前置检查,部署 165/166 迁移,再执行后置检查。
2. 部署兼容 API。
3. 部署 Relay/Worker 并确认指标、日志和告警可用。
4. 部署依赖消费者并验证消费者幂等和可观察结果。
5. 最后允许前端和业务生产者放量;消费者和监控就绪前禁止制造新积压。
新增表为空时可以执行 down 迁移。已有 Outbox 或配置事实后必须停止生产者和 Relay、保留事实、回滚无数据风险的应用版本并向前修复禁止删表或清空数据降级。详细命令、停止条件和测试隔离方式见[迁移发布与数据安全回滚](迁移发布与数据安全回滚.md)。
## 前端跨仓契约
首次加载显示占位成功且真实无数据时才显示空态筛选无结果时提供清除入口。403 显示无权限且不重试;瞬时错误保留已有数据和输入并提供显式重试。创建成功后保存任务 ID刷新或重新进入页面后恢复查询不得重新创建任务。
待处理或处理中按 2 秒、3 秒、5 秒退避轮询,最长间隔 10 秒;终态停止。页面隐藏时暂停,恢复可见后立即刷新一次。字段和状态语义见[统一异步任务与前端轮询契约](统一异步任务与前端轮询契约.md)。
## 下游接入清单
- 生产者:在 Application 的既有 GORM 事务中调用 Outbox Repository保存稳定事件类型、版本、事件 ID、业务键和关联标识。
- 消费者:在 Worker 组合根注册稳定事件类型,实现重复投递无副作用的 `EventConsumer`,并提供业务结果的可观察断言。
- 配置所有者:注册本模块拥有的配置定义;不得复制公共表 DDL也不得由公共基础猜测业务 Key。
- 异步任务所有者:保留业务任务表和失败明细,把公共五态投影到 API并采用租约或等价 PostgreSQL 恢复事实。
- 发布负责人:运行前后置门禁,确认 Relay、消费者、监控和前端依次就绪后再放量。
## 待决策项
- Audit Event 公共实现完成后,需要在系统配置更新和 Outbox 人工恢复的组合根注入正式审计 Adapter。
- 各下游 PRD 需要分别确认事件类型、载荷版本、消费者幂等键、业务失败明细和通知策略;公共基础不预先注册这些内容。
- 生产阈值需结合容量基线确定待投递年龄、积压量、过期租约比例和成功率告警值;当前公共 Query 提供指标与阈值计算接缝,不固化业务容量数字。
只有真实 PostgreSQL、Redis、Asynq、Fiber 接缝测试、全量 Go 测试和累计差异评审全部通过后,才可把本基础标记为可供下游接入。

View File

@@ -0,0 +1,22 @@
# 公共数据库对象所有权与发布门禁
## 对象所有权
| 数据库对象 | 唯一迁移所有者 | 下游接入方式 |
|---|---|---|
| `tb_outbox_event` 及公共索引、约束 | `tech-public-foundation` | 依赖公共 Outbox Port禁止复制 DDL |
| `tb_system_config` 及公共索引、约束 | `tech-public-foundation` | 注册业务 Key禁止复制 DDL 或创建任意 Key |
| Audit Event、Integration Log | `tech-global-audit` | 公共基础只依赖审计 Port |
| 通知、业务任务、业务失败明细 | 对应业务 PRD | 复用公共契约,保留自己的业务表 |
## 门禁执行
迁移和放量前运行前置检查,迁移后运行后置检查。检查只读取 PostgreSQL重复执行不会写入业务事实输出仅包含稳定错误码、对象名和计数不输出事件载荷或配置值。
前置检查阻断以下情况:依赖迁移版本不足或处于脏状态、已有对象定义冲突、重复事件 ID 或配置 Key、必填字段空值、非法状态/类型、未投递事件和过期租约。后置检查还要求公共表及约定字段已经存在,并执行约束、索引和读写冒烟验证。
## 发布与回滚边界
发布顺序固定为:迁移与检查、兼容 API、Relay/Worker、依赖消费者、前端。迁移异常、Outbox 持续积压或租约大量过期、配置读写不一致、访问日志脱敏回归失败、关键任务无法恢复时停止放量。
尚未产生事实的新结构可在验证后回滚。公共表已经保存 Outbox 或配置事实后,必须先停止生产者和 Relay保留事实并向前修复禁止通过降级删表清理。

View File

@@ -0,0 +1,13 @@
# 统一异步任务与前端轮询契约
业务任务固定使用五态:`1=待处理、2=处理中、3=已完成、4=已失败、5=已取消`,所有公开投影同时返回中文状态名。各业务继续拥有自己的任务表、任务项和失败明细;公共基础不建设万能任务表。
公开投影至少包含 `task_id`、状态与状态名、总数、成功数、失败数、进度、安全错误码与中文摘要、开始时间、完成时间和更新时间。业务项到达处理终点时使用“已完成”:部分成功和全部业务项失败均通过计数表达,且必须满足 `total_count = success_count + failed_count`;只有整体无法建立或执行到业务终点时才使用“已失败”。
领取必须使用 PostgreSQL 预期状态条件更新。待处理任务可以进入处理中;处理中任务保存租约所有者与到期时间,仅过期租约可被其他 Worker 恢复。续租和进入终态都校验当前状态、租约所有者与有效期。队列重复投递遇到终态时直接返回,不制造第二次业务副作用。取消仅适用于业务明确支持的任务,并从待处理或处理中条件更新到已取消。
调用统一 `EnqueueTask` 时载荷只包含 `task_id`、必要分片标识和关联标识,并以 struct 或 map 传入;禁止预序列化为 `[]byte`
前端首次加载显示占位成功且确实无数据时才显示空态筛选无结果提供清除筛选入口。403 显示无权限且不提供重试。瞬时失败保留已有数据和输入并提供显式重试。创建成功后持久保存 `task_id`,刷新或重新进入页面后恢复查询。
待处理或处理中按 2 秒、3 秒、5 秒逐步退避,最长不超过 10 秒;终态停止轮询。页面隐藏时暂停,恢复可见后立即刷新一次,再按最新状态继续。网络恢复和页面刷新不得重新创建任务。

View File

@@ -0,0 +1,22 @@
# 公共基础迁移发布与数据安全回滚
## 执行顺序
1. 使用 `./scripts/test-tech-public-foundation.sh` 完成真实依赖公共接缝验收,再使用 `go run ./cmd/foundation-check --phase pre` 执行只读前置检查。
2. 依次执行 `000165_create_public_outbox``000166_create_system_config` 正向迁移。
3. 使用 `go run ./cmd/foundation-check --phase post` 验证字段、约束、索引、异常计数和事务内读写冒烟。
4. 部署兼容 API 和 Outbox Relay/Worker确认监控就绪后再部署依赖消费者最后允许前端或生产者放量。
门禁报告只输出稳定错误码、数据库对象安全标识和计数。依赖迁移版本不足或脏状态、对象定义冲突、重复身份、空值、非法枚举、未完成任务、未投递事件和过期租约均以非零状态阻断。
## 数据迁移与隔离
165/166 只创建新的公共表和索引,不回填历史业务数据,因此没有批次回填步骤。后续确需回填时必须按稳定主键分批记录进度,允许中断重跑并验证行数守恒;不得借本次迁移扫描或改写下游业务表。
自动化验收在独立 PostgreSQL schema 和测试事务内执行,只删除本次创建的 schema 或依赖事务回滚不执行全库清理。Redis/Asynq 测试使用唯一事件 ID 和配置 Key并只删除本次任务与缓存键。
## 回滚边界
新增表为空时down 迁移允许删除结构。任一公共表已经保存事件或配置事实时down 迁移主动失败;发布负责人必须停止新生产者、停止 Relay 领取、保留已有事实,回滚无数据风险的应用版本,并修复后向前恢复。禁止通过删表、清空 Outbox 或删除配置事实完成降级。
停止条件包括迁移门禁异常、Outbox 持续积压或过期租约大量增加、配置读写不一致、Access Log 脱敏回归失败和关键任务无法恢复。