跟GPT合作给同事写一个查看版本等的小功能
2026-01-21
今天这个功能,表面看起来很简单:
“项目版本中心,能看到 OA 推送历史,并能把相关底稿、报告、说明下载下来。”
但真正动手以后,你会发现它是一个典型的“企业级泥潭功能”:
不是算法难,不是框架难,而是现实复杂度极高。
我把这次 projectVersionCenter 的共创,拆成 6 个阶段。
一、需求不是“功能”,而是一坨历史沉积物
一开始,并不存在一个清晰的“我要做什么”。
你拿到的是:
OA 推送日志表(system_log)
推送详情表(system_log)
文件相关表(tianjian202-proapi)
FileHistory / FileHistory_V2 / SingleAsset
OssFileRecord
ReportsProjects
Projects
各种 “这个字段现在不用了”
各种 “这个字段以前是 A,现在是 B”
这不是一个新功能,这是一个考古现场。
👉 第一个关键判断
这类需求,千万不要一上来写代码。
你写得越快,返工越惨。
我们做的第一件事,不是写 SQL,而是把真实数据流画出来:
二、先做“事实层建模”,而不是代码建模
我们明确了一个非常重要的事实:
1️⃣ 日志和业务不在一个世界
system_log
只关心:推没推、推给谁、推成功没、要不要重试
tianjian202-proapi
才是真正的业务世界(项目、底稿、报告、文件)
这一步如果不想清楚,后面一定会写出“逻辑上成立、业务上错误”的代码。
2️⃣ “文件”不是一个表,而是一个路径系统
这次最容易踩坑的一点是:
你以为你在连“文件”,
实际上你在穿越 三套文件体系的历史演进。
我们最终确认了三条真实链路:
收益法 / 市场法 / 单项资产
detail.draft_id → TJ_FileHistory(_SingleAsset) → FileId → TJ_Files.Uuid → Path
资产基础法
detail.draft_id → TJ_FileHistory_V2 → FileSignCompound → TJ_OssFileRecord.FileSign → ServerPath
报告 / 说明
detail.related_report_id → TJ_ReportProjects.UUID → ReportFileFinal
👉 这是这次功能最重要的“知识资产”
比任何代码都值钱。
三、跨库不是难点,跨“字符集世界”才是
当 SQL 第一次跑起来报错的时候,错误是这个:
Illegal mix of collations (utf8_general_ci) and (utf8_unicode_ci)
这是一个非常典型、但被严重低估的企业系统问题。
表象
SQL 写得没错
表也存在
数据也在
真正原因
不同年代建的表
不同团队
不同默认 collation
UUID / FileId / FileSign 都是 varchar
一旦跨库 JOIN,MySQL 就会拒绝帮你“猜规则”。
我们做出的工程选择(非常关键)
👉 不改表结构,不搞大手术
👉 在 JOIN 层显式控制规则
也就是这一类写法:
f_income.Uuid COLLATE utf8_unicode_ci = fh_income.FileId COLLATE utf8_unicode_ci
这是一个非常成熟、现实的选择:
不影响现有系统
不锁表
不引入连锁风险
成本极低
这是典型的 “工程解法,而不是教科书解法”
四、工具链细节,也会在真实项目里绊你一跤
一个非常真实、但只有在老项目里才会出现的小插曲:
static fn($v) => ...
PHP 7.1 不支持。
这不是水平问题,这是现实环境问题。
你如果习惯写现代 PHP,很容易下意识用箭头函数;
但在企业项目里,“环境约束”永远高于“语言优雅”。
最终我们选择:
$h = static function ($v) {
return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
};可运行 > 优雅
五、projectVersionCenter 真正“值钱”的地方
这个功能真正的价值,并不只是“能下载文件”。
而是它做了三件以前系统里做不到的事:
1️⃣ 把“推送行为”还原成“可追溯事实”
谁推的
推了什么
推给谁
成功 / 失败
重试了几次
request / response 全量保留
这是审计级能力。
2️⃣ 把“散落在各处的版本实体”拉回一个中心
以前是:
底稿在一套逻辑
报告在一套逻辑
说明在一套逻辑
OA 只知道“我推过”
现在是:
围绕 project_version 这个视角,把所有产物重新聚合。
这才叫 Version Center。
3️⃣ 顺手解决了“权限外分享”的密码问题
从 TJ_Projects.UUID 生成:
md5(UUID + 3)
这一步看似不起眼,但它意味着:
版本中心 ≠ 内部系统
它已经在为“跨系统访问 / OA / 外部协作”做准备
这是架构意识,不是功能意识。
六、这次共创过程,最值得别人借鉴的 5 条原则
最后,给其他读者一个可直接复用的 checklist:
✅ 1. 先画真实数据流,再写一行代码
不画清楚,后面所有 bug 都是必然的。
✅ 2. 不要迷信“表名”,要看字段真实语义
同叫 FileId,可能是:
文件主键
历史表主键
OSS 签名
UUID
✅ 3. 老系统里,JOIN 的敌人不是性能,是字符集
遇到 collation,优先 JOIN 层解决。
✅ 4. 能在 SQL 里解决的,不要推给 PHP
路径拼接、文件定位、过滤,SQL 能干的尽量干。
✅ 5. Version Center 是“治理能力”,不是页面功能
它解决的是:
混乱
追责
历史不可见
版本不可复现
结语
今天这个 projectVersionCenter,不是写了多少行代码,
而是把一个长期被忽略的“版本真相”重新组织了一次。
这种功能,往往不会在发布会上被提到,
但一旦出问题、查历史、要追责,它就是救命的东西。
如果你正在维护一个年头很久、逻辑很杂、谁都不敢动的系统,
希望这次复盘能让你意识到一件事:
真正的工程能力,
不在“写新功能”,
而在让旧世界重新变得可理解。
发表评论: