彭峰 863a32eb23 fix(sync-bridge): validate IIS target and strengthen deployment checks 2 zile în urmă
..
database 0298322d9c feat: sync student coach and backend migration updates 1 lună în urmă
deployment 863a32eb23 fix(sync-bridge): validate IIS target and strengthen deployment checks 2 zile în urmă
Program.cs 0298322d9c feat: sync student coach and backend migration updates 1 lună în urmă
README.md 863a32eb23 fix(sync-bridge): validate IIS target and strengthen deployment checks 2 zile în urmă
Xiaoshu.LegacySyncBridge.csproj 3a7c5df3ee feat: add legacy realtime sync bridge 1 lună în urmă
appsettings.json 0298322d9c feat: sync student coach and backend migration updates 1 lună în urmă

README.md

小树陪练旧系统同步桥

该服务部署在能访问旧 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。签名原文为:

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 交付包

在开发机执行:

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 做零修改部署前检查:

    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. 部署前检查通过后,再运行正式安装:

    .\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,不会重复路径):

    .\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。首次只上线读取桥,写入过程保持失败关闭。需要回滚时,使用安装输出的备份目录执行:

.\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 消费端按数据集限定可写入的类和主键字段,同一数据集可连续处理多页,并使用五分钟租约防止并发重复消费;任意一项失败时不推进游标。

上线前执行:

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 时回滚并返回错误,这是上线保护,不是可绕过的占位成功。只读同步可以先上线;只有完成写入绑定和回归后,才可在新云函数配置同步桥并启用“旧系统主写”。