# 小树陪练旧系统同步桥 该服务部署在能访问旧 SQL Server 的受控网络内,只提供三类窄接口: - `GET /xiaoshu-sync/v1/manifest`:返回同一水位的数据集数量、增量水位和按业务主键排序计算的 SHA-256 校验和。 - `GET /xiaoshu-sync/v1/changes?dataset=...&cursor=...`:按旧业务主键断点续传,返回经白名单转换的 Parse 投影。 - `POST /xiaoshu-sync/v1/commands/{operation}`:携带幂等键、操作人和原因调用旧系统正式业务逻辑。 请求必须包含 `X-Xiaoshu-Key-Id`、`X-Xiaoshu-Timestamp`、`X-Xiaoshu-Nonce` 和 `X-Xiaoshu-Signature`。签名原文为: ```text METHOD\nPATH_AND_QUERY\nTIMESTAMP\nNONCE\nSHA256_HEX(BODY) ``` 返回字段严格白名单化,不查询、不返回 `UserPwd`、`PayPassWord`、`Question`、`Answer`、Cookie 或 Session。 ## 部署 1. 在 SQL Server 执行 `database/install.sql`。脚本会建立增量变更日志,并为用户、目录、内容主表和九个业务附表安装触发器;不会修改原业务字段。 2. 把允许的写操作对接到旧站实际 BLL。对接前写入会安全拒绝,不会退化为无补偿双写;只读全量和增量同步可以先独立启用。 3. 通过环境变量或 IIS 配置提供 `ConnectionStrings__LegacySqlServer`、`XiaoshuSync__KeyId`、`XiaoshuSync__Secret` 和 `XiaoshuSync__AllowedIps__0`。 4. 执行 `npm run sync-bridge:publish`,将 `dist/legacy-sync-bridge-win-x64` 发布目录部署为独立 IIS 应用。该目录是 Windows x64 自包含发布,不依赖服务器安装 .NET 8 Runtime;IIS 仍需具备 `AspNetCoreModuleV2`。密钥不得写入仓库。 5. 在新云函数设置 `XIAOSHU_LEGACY_SYNC_BASE_URL`、`XIAOSHU_LEGACY_SYNC_KEY_ID`、`XIAOSHU_LEGACY_SYNC_SECRET`。`XIAOSHU_LEGACY_SYNC_BASE_URL` 必须是 IIS 应用根地址,例如 `https://a018.2018.z01.com/xiaoshu-sync`;不要再附加 `/v1`,云函数会自动拼接 `/v1/...`。只读同步上线时不设置 `XIAOSHU_LEGACY_SYNC_WRITES_ENABLED`;只有 `XiaoshuSync_ExecuteCommand` 已绑定正式 BLL 并通过回归后,才允许设为 `true`。 ### IIS 交付包 在开发机执行: ```bash npm run sync-bridge:package ``` 会生成 `dist/xiaoshu-legacy-sync-bridge-iis.zip`。服务器管理员解压后: 1. 复制 `deployment/appsettings.Production.template.json` 到包外的安全目录,填写 SQL Server 连接串、KeyId、至少 32 位随机 Secret 和 Parse 服务器出口 IP。不要把正式配置重新打进压缩包。 2. 先在 SQL Server 执行 `database/install.sql`,再执行 `database/verify-readonly.sql`,确认 `WritesFailClosed=1`。 3. 先使用管理员 PowerShell 做零修改部署前检查: ```powershell Get-Website | Select-Object Name, State, PhysicalPath Get-WebBinding | Select-Object ItemXPath, Protocol, BindingInformation # 填写绑定 a018.2018.z01.com 的实际 IIS 站点名称。 $targetSite = Read-Host '目标 IIS 站点名称' .\deployment\Install-XiaoshuLegacySyncBridge.ps1 -PublishPath . -ConfigurationPath C:\secure\xiaoshu-sync.production.json -SiteName $targetSite -HealthUrl https://a018.2018.z01.com/xiaoshu-sync/v1/health -PreflightOnly ``` 4. 部署前检查通过后,再运行正式安装: ```powershell .\deployment\Install-XiaoshuLegacySyncBridge.ps1 -PublishPath . -ConfigurationPath C:\secure\xiaoshu-sync.production.json -SiteName $targetSite -HealthUrl https://a018.2018.z01.com/xiaoshu-sync/v1/health ``` 5. 使用外部可访问地址执行签名验证(应用部署在 `/xiaoshu-sync`,程序内部路由使用 `/v1`,不会重复路径): ```powershell .\deployment\Test-XiaoshuLegacySyncBridge.ps1 -BaseUrl https://a018.2018.z01.com/xiaoshu-sync -ConfigurationPath C:\secure\xiaoshu-sync.production.json ``` 安装脚本必须显式指定目标站点和完整健康地址,先核对域名绑定、端口、协议与配置 PathBase,再备份现有物理目录、创建独立的 64 位应用池、收紧目录 ACL。健康响应必须包含 `service=xiaoshu-legacy-sync` 和 `version=v1`。验收脚本兼容 Windows PowerShell 5.1,除健康与签名 manifest 外,还读取每个数据集第一页,确认数据库查询和投影可用;不会打印业务记录、连接串或 Secret。首次只上线读取桥,写入过程保持失败关闭。需要回滚时,使用安装输出的备份目录执行: ```powershell .\deployment\Rollback-XiaoshuLegacySyncBridge.ps1 -BackupPath C:\inetpub\xiaoshu-sync.backup-YYYYMMDD-HHMMSS -HealthUrl https://a018.2018.z01.com/xiaoshu-sync/v1/health ``` 回滚脚本会先保留当前失败版本,再恢复备份、启动应用池并重新检查健康地址。 ### 健康地址返回 404 时 `/v1/health` 不需要 HMAC 或数据库连接。404 应先检查 IIS 站点、子应用映射与反向代理路径,不能通过重试同步或更换签名密钥修复。文件上传到目录也不代表已创建 IIS 应用。 在服务器管理员 PowerShell 中查看 `Get-WebApplication -Site $targetSite`、`Get-WebBinding -Name $targetSite`、`Get-WebAppPoolState -Name XiaoshuLegacySync`,确认 `/xiaoshu-sync` 是目标域名所在站点下的独立应用,其物理目录含 `Xiaoshu.LegacySyncBridge.dll`、`web.config` 和安全填写的生产配置。若服务文件和应用均存在,检查该站点 IIS 日志的 `sc-status/sc-substatus/sc-win32-status` 及事件查看器中的 ASP.NET Core Module 记录;仅凭外部 404 不能确定具体 IIS 子状态。CMS 的模板编辑、静态站点发布和上传文件管理不能代替 IIS 子应用管理。 外部健康地址为 `/xiaoshu-sync/v1/health`,manifest 为 `/xiaoshu-sync/v1/manifest`。HMAC 原文包含 IIS 的 `PathBase`,因此测试脚本签名的路径与服务器校验路径完全一致。`manifest.consistent=false` 表示读取数量期间旧库发生了变化,调用方必须重试,不能把该次结果作为同水位对账依据。 数据库脚本执行后必须再执行 `database/verify-readonly.sql`。它会核对日志表、命令过程和 12 个业务变更触发器,输出各数据集当前数量与水位,并明确显示写桥是否仍处于失败关闭状态。只读阶段必须保持 `WritesFailClosed=1`;没有取得旧站正式后端/BLL 源码时,不允许用直接更新业务表的方式替代。 ## 增量语义 - 首次同步使用稳定的业务主键分页,同时记录开始全量时的变更日志水位。 - 全量最后一页会自动切换为增量游标;全量期间发生的修改不会漏掉。 - 后续读取 `XiaoshuSyncChangeLog`,能够识别新增、原地更新和硬删除,不再只追踪越来越大的业务 ID。 - 游标是不透明的 Base64 值,调用方不得自行解析或修改。 - `changes` 明确返回 `phase=snapshot|incremental` 和当前 `watermark`,新系统可以区分首次全量与后续增量。 - `manifest` 同时返回各数据集的数量、增量水位和主键校验和,供新后台同步中心做同水位核对。 - Parse 消费端按数据集限定可写入的类和主键字段,同一数据集可连续处理多页,并使用五分钟租约防止并发重复消费;任意一项失败时不推进游标。 上线前执行: ```bash npm run sync-bridge:build npm run sync-bridge:publish ``` 仍需配合每日主键集合与关键字段哈希对账;增量日志不能替代定期全量校验。 ## 写入接入边界 旧业务端源码已经确认以下正式动作: - `e_order_update_v2`:老师开始上课、结束陪练以及最终完课的状态推进。 - `content_add`:生成 Model 59 上课记录,并以预约 `GeneralID` 写入 `yyds`。 - `content_update`:更新课程、点评、评价和学习进度等附表字段。 `XiaoshuSync_ExecuteCommand` 必须调用旧站对应 BLL/正式过程,不能只更新预约状态字段。当前安装脚本会在未绑定 BLL 时回滚并返回错误,这是上线保护,不是可绕过的占位成功。只读同步可以先上线;只有完成写入绑定和回归后,才可在新云函数配置同步桥并启用“旧系统主写”。