| event_payload |
{"ref":"refs/heads/main","befo {"ref":"refs/heads/main","before":"978aaeeb46174baca37960b898e7fd086cec6696","after":"3b55095e3aac481fdce7fde36f0dcc24309d5ef4","compare_url":"https://gitea.g-hi.com/luoanwu/enterprise-platform/compare/978aaeeb46174baca37960b898e7fd086cec6696...3b55095e3aac481fdce7fde36f0dcc24309d5ef4","commits":[{"id":"3b55095e3aac481fdce7fde36f0dcc24309d5ef4","message":"feat(modules): public-file 宿主数据迁移与对账工具(DEC-040 实施工作包第 2—3 步的可执行面)\n\n**不连任何库**:源库在工单仓(另一个 Owner 的环境),目标库在平台,两边凭据不该压进一个进程。\n工具做的是「生成幂等 SQL + 逐条对账」,导出与执行由操作者各自用 psql 完成——与仓里\nstack/provision/db.sh 同形(打印 SQL,由人决定在哪个库上执行)。用法与两条导出命令写在脚本头部。\n\n映射守住 DEC-040 的两条硬约束,各有单测:\n- **业务外键不迁**:宿主 Attachment.ticket_id(必填外键 + onDelete Cascade)降级为 owner_type='ticket'\n + owner_ref 两列,平台侧不建外键;\n- **retention_until 原样带过去**:级联删除迁走后,它是「宿主忘了调删除接口」时唯一兜底的东西。\n另外宿主出现未登记的状态值时落 PENDING 并在对账里单列 unmappedStatuses——**不猜**。\n\nSQL 幂等且不倒退:ON CONFLICT (tenant_id, file_id) DO UPDATE 只覆盖宿主侧的事实列,\n**不覆盖 scan_attempts / purged_at / purge_***(重跑不该把平台已推进的清理进度倒回宿主快照那一刻);\n按租户切分并先 set_config,FORCE RLS 下不设租户一行也写不进去。\n\n对账:行数 + sha256 逐条 + 状态分布;balanced 只在「不缺行且摘要逐条一致」时为真,不平退出码 1。\n**不看 storage_ref**——迁移不搬字节,对象仍在同一个桶里。\n\n端到端实跑过(合成 1 行,跑完已删,表回到 0 行):生成的 SQL 在 dev 平台库真表上 INSERT 0 1 / COMMIT,\n导出目标侧对账判平(源 1 / 目标 1、sha256 一致),目标为空时判不平且退出 1。\n模块单测 31 例通过 / 1 跳过(+9);check:modules / check:module-imports / check:stub-surface / check:pins 均 passed。\n\n**没做也不该由我做的**:真实数据迁移、宿主改调平台端点、级联删除语义的宿主侧改造——那些落在工单仓,\n归该仓 Owner;本仓这侧的可执行面到此齐备。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","url":"https://gitea.g-hi.com/luoanwu/enterprise-platform/commit/3b55095e3aac481fdce7fde36f0dcc24309d5ef4","author":{"name":"juhailaoluo pro","email":"hillao@juhailaoluodeMacBook-Pro.local","username":""},"committer":{"name":"juhailaoluo pro","email":"hillao@juhailaoluodeMacBook-Pro.local","username":""},"verification":null,"timestamp":"2026-09-19T06:16:30-07:00","added":["runtime/modules/public-file/scripts/migrate-from-host.mjs","runtime/modules/public-file/src/migration/from-host.test.ts","runtime/modules/public-file/src/migration/from-host.ts"],"removed":[],"modified":["runtime/modules/public-file/package.json"]}],"total_commits":0,"head_commit":{"id":"3b55095e3aac481fdce7fde36f0dcc24309d5ef4","message":"feat(modules): public-file 宿主数据迁移与对账工具(DEC-040 实施工作包第 2—3 步的可执行面)\n\n**不连任何库**:源库在工单仓(另一个 Owner 的环境),目标库在平台,两边凭据不该压进一个进程。\n工具做的是「生成幂等 SQL + 逐条对账」,导出与执行由操作者各自用 psql 完成——与仓里\nstack/provision/db.sh 同形(打印 SQL,由人决定在哪个库上执行)。用法与两条导出命令写在脚本头部。\n\n映射守住 DEC-040 的两条硬约束,各有单测:\n- **业务外键不迁**:宿主 Attachment.ticket_id(必填外键 + onDelete Cascade)降级为 owner_type='ticket'\n + owner_ref 两列,平台侧不建外键;\n- **retention_until 原样带过去**:级联删除迁走后,它是「宿主忘了调删除接口」时唯一兜底的东西。\n另外宿主出现未登记的状态值时落 PENDING 并在对账里单列 unmappedStatuses——**不猜**。\n\nSQL 幂等且不倒退:ON CONFLICT (tenant_id, file_id) DO UPDATE 只覆盖宿主侧的事实列,\n**不覆盖 scan_attempts / purged_at / purge_***(重跑不该把平台已推进的清理进度倒回宿主快照那一刻);\n按租户切分并先 set_config,FORCE RLS 下不设租户一行也写不进去。\n\n对账:行数 + sha256 逐条 + 状态分布;balanced 只在「不缺行且摘要逐条一致」时为真,不平退出码 1。\n**不看 storage_ref**——迁移不搬字节,对象仍在同一个桶里。\n\n端到端实跑过(合成 1 行,跑完已删,表回到 0 行):生成的 SQL 在 dev 平台库真表上 INSERT 0 1 / COMMIT,\n导出目标侧对账判平(源 1 / 目标 1、sha256 一致),目标为空时判不平且退出 1。\n模块单测 31 例通过 / 1 跳过(+9);check:modules / check:module-imports / check:stub-surface / check:pins 均 passed。\n\n**没做也不该由我做的**:真实数据迁移、宿主改调平台端点、级联删除语义的宿主侧改造——那些落在工单仓,\n归该仓 Owner;本仓这侧的可执行面到此齐备。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","url":"https://gitea.g-hi.com/luoanwu/enterprise-platform/commit/3b55095e3aac481fdce7fde36f0dcc24309d5ef4","author":{"name":"juhailaoluo pro","email":"hillao@juhailaoluodeMacBook-Pro.local","username":""},"committer":{"name":"juhailaoluo pro","email":"hillao@juhailaoluodeMacBook-Pro.local","username":""},"verification":null,"timestamp":"2026-09-19T06:16:30-07:00","added":["runtime/modules/public-file/scripts/migrate-from-host.mjs","runtime/modules/public-file/src/migration/from-host.test.ts","runtime/modules/public-file/src/migration/from-host.ts"],"removed":[],"modified":["runtime/modules/public-file/package.json"]},"repository":{"id":116,"owner":{"id":5,"login":"luoanwu","login_name":"","source_id":0,"full_name":"","email":"law@g-hi.com","avatar_url":"https://gitea.g-hi.com/avatar/627574a890388a2aadc80ab38d22f3a0","html_url":"https://gitea.g-hi.com/luoanwu","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2026-01-30T16:28:30+08:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"luoanwu"},"name":"enterprise-platform","full_name":"luoanwu/enterprise-platform","description":"","empty":false,"private":false,"fork":false,"template":false,"mirror":false,"size":24168,"language":"","languages_url":"https://gitea.g-hi.com/api/v1/repos/luoanwu/enterprise-platform/languages","html_url":"https://gitea.g-hi.com/luoanwu/enterprise-platform","url":"https://gitea.g-hi.com/api/v1/repos/luoanwu/enterprise-platform","link":"","ssh_url":"git@gitea.g-hi.com:luoanwu/enterprise-platform.git","clone_url":"https://gitea.g-hi.com/luoanwu/enterprise-platform.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"branch_count":44,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"main","archived":false,"created_at":"2026-09-11T07:33:25+08:00","updated_at":"2026-09-19T21:12:48+08:00","archived_at":"1970-01-01T08:00:00+08:00","permissions":{"admin":true,"push":true,"pull":true},"has_code":true,"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"all","has_releases":true,"has_packages":true,"has_actions":true,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":true,"allow_rebase_update":true,"allow_manual_merge":false,"autodetect_manual_merge":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":true,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","topics":[],"licenses":[]},"pusher":{"id":5,"login":"luoanwu","login_name":"","source_id":0,"full_name":"","email":"5+luoanwu@noreply.localhost","avatar_url":"https://gitea.g-hi.com/avatar/627574a890388a2aadc80ab38d22f3a0","html_url":"https://gitea.g-hi.com/luoanwu","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2026-01-30T16:28:30+08:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"luoanwu"},"sender":{"id":5,"login":"luoanwu","login_name":"","source_id":0,"full_name":"","email":"5+luoanwu@noreply.localhost","avatar_url":"https://gitea.g-hi.com/avatar/627574a890388a2aadc80ab38d22f3a0","html_url":"https://gitea.g-hi.com/luoanwu","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2026-01-30T16:28:30+08:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"luoanwu"}}... |