• company-service.js:company.json 加 ownerId(无 SQL 表,纯 JSON 字段);create() 接收
    ownerId;list({ user }) 按传入用户过滤,不传就不过滤(company-heartbeat-service.js 等
    系统内部巡检要看全部公司);新增 canAccessCompany(company, user) 作为全仓库唯一一份
    归属判据(admin 不过滤,判负一律 404 不是 403,不暴露"这个 id 存在只是不是你的")。
    
    company.js 用 router.param('companyId', ...) 统一收口(约 50 条路由,含 okrPath() 系列
    动态路由)。graph-run.js 的两个挂载点参数形状不一样,设计阶段的描述不准确,实现时才
    发现:companyRouter 唯一路由是 /:companyId/graph-runs 直接查;主 router 全部路由是
    /:id/...,:id 指的是 run 不是公司,改成先查 run 的 company_id 再判断,run 不存在或
    公司不是你的统一回"run 不存在"。devloop.js 用 :pid 反查 company_id;POST /projects
    单独校验 body 里的 company_id(放在基本字段校验之后,避免归属检查打乱输入校验的错误
    优先级);GET /projects 按归属过滤列表。
    
    okr.js 最终没有按原计划接入 companyId 归属校验——实现前查证发现 OkrDashboard.vue
    挂载的 /okr 页面全站没有任何导航入口指向它,是个孤儿页面,给访问不到的页面建"选公司"
    前端交互不划算,改成 requireAdmin(跟 dream.js 同款),先堵住直接拼 URL 看到跨公司
    聚合数据的口子。真正在用的公司级 OKR(company.js 的 /:id/okr)已随 company.js 一起隔离。
    
    存量数据迁移:scripts/backfill-company-owner.js(npm run backfill:company-owner),
    两家存量公司已划给种子账号,幂等验证过。
    
    8 个既有测试文件用的公司 id 都是从没真正建过的字符串标签,挂锁后集体 404——新增共享
    helper server/tests/helpers/ensure-test-company.js 修复;两处原有的"跳过已有
    company.json 的目录,那是真公司"防御逻辑,判据从"有没有 company.json"改成"ownerId
    是不是 -1 这个测试专用标记",否则新出现的测试夹具会被误判成真公司永远不清理。
    
    验证:真实 curl 走过(admin 看到全部存量公司、新用户看到空列表、新用户拼 URL 访问
    别人的公司 404、新建公司 ownerId 精确落在创建者、admin 和创建者都看得到其他人看不到);
    全量 vitest 1427 passed,3 个无关既有失败不受影响。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »