# 风影智慧办公 · 全系统转交文档 更新日期:2026-09-01 当前手机包:**3.1.8+318**(`com.fengying.oa.mobile`) 本次生产修复与当前真实状态:**先读 `/opt/HANDOFF.md`,再读本文件第 0、2、3、14 节和 `.cursor/rules/`。** 本文件覆盖 **Web + API + 手机 + IM + 库表 + 密钥 + 发版**。不是只写手机端。 **含生产密钥,不要推到公开 git。** --- ## 0. 你接手后立刻要知道的事 1. 这是一家约四十人的工程/软件公司内部 OA,从旧 Java OA(生产 MySQL 库 `oa`)迁到本仓库。 2. 用户同时用 **网页** `https://oa.fysxkj.com` 和 **安卓包**。IM 是自研 TCP,不是环信。 3. 办公模块有固定产品规则(第 3 节)。乱改列表筛选或审批不写下游,会被用户打回。 4. 2026-09-01 的 API 数据修复、旧终止投标恢复兼容和项目类型修复已经发到生产;精确结果与备份位置见 `/opt/HANDOFF.md`。后续不要依据本文件旧版本描述判断是否已部署。 5. 回复用户用**简体中文**。不要主动 `git commit` / `push`,除非用户说。 6. 改菜单后必须跑 `apps/api/prisma/sync-menus.ts`,侧栏才更新。 7. 手机登录/刷新必须带 `clientKind: 'mobile'`。 --- ## 1. 产品与架构 一套 API,三端消费: | 端 | 路径 | 技术 | 入口 | |---|---|---|---| | API | `apps/api` | NestJS 11 + Prisma 6 + PostgreSQL | `/api/v1`,Swagger `/api/v1/docs` | | Web | `apps/web` | React 18 + Vite 5 + Ant Design 5 + React Query + Zustand | 路由 `apps/web/src/App.tsx` | | 手机 | `apps/native` | Flutter,包名 **必须** `com.fengying.oa.mobile` | 底栏:消息 / 工作台 / 通讯录 / 我 | | IM | `apps/im-server/server.js` | Node TCP 8901 + 本机 HTTP 8902 | MobileIMSDK 协议 | 生产: - 站点:`https://oa.fysxkj.com` - REST:`https://oa.fysxkj.com/api/v1` - IM TCP:`47.96.23.244:8901` - APK:`https://oa.fysxkj.com/fengying-oa.apk` - 远端代码:`/data/fengying-oa/` - systemd:`fengying-api.service`、`fengying-im.service`(模板在 `infra/fengying-cloud/`) - 生产 API 端口 **3010**(nginx 反代);本机 nest 常在 **3000** 本机构建机 hostname `fykj`,公网不是 `47.96.23.244`。 租户字段 `tenantId` 目前固定 `'1'`,没有做多租户产品化。 --- ## 2. 仓库地图 ``` /opt ├── DELIVERY.md ← 本文件 ├── .cursor/rules/office-process.mdc ├── apps/api Nest 后端 │ ├── prisma/schema.prisma 库表 │ ├── prisma/menus.ts 侧栏菜单源 │ ├── prisma/seed.ts 演示数据 │ ├── prisma/sync-menus.ts 菜单写入 Permission │ ├── src/*.controller.ts 业务入口 │ ├── data/app-release.json 手机 OTA │ └── .env 本机密钥 ├── apps/web/src │ ├── App.tsx 全部路由 │ ├── pages/ 业务页 │ ├── store/auth.ts 登录态 │ ├── api/client.ts axios + sessionStorage │ └── labels.ts 枚举中文 ├── apps/native Flutter ├── apps/im-server/server.js └── infra/fengying-cloud nginx / systemd / compose 模板 ``` 本机常用: ```bash cd /opt/apps/api && npx nest start # http://localhost:3000/api/v1 cd /opt/apps/web && pnpm dev # http://localhost:5173 cd /opt/apps/im-server && node server.js # TCP 8901 cd /opt/apps/native && flutter build apk --release && bash scripts/publish-apk.sh ``` 改 Prisma:`cd /opt/apps/api && npx prisma migrate dev`。 改菜单:改 `prisma/menus.ts` 后 `npx ts-node --transpile-only prisma/sync-menus.ts`。 --- ## 3. 硬约束(违反就是返工) ### 3.1 个人办公怎么长 规则文件:`.cursor/rules/office-process.mdc` - 列表筛选用 **`ApplyBucketBar`**(Segmented + `?bucket=`),有选中态。 - 标准桶:全部 / 待审批 / 已通过 / 已驳回。领域附加桶可以加,不能替代标准桶。 - 禁止页头三个无选中态的 `Link`(「全部」「待审批」「外带未还」这种)。 - 「我的申请」=`/office/flow` 是**跟踪**;人事/用章/办公申请是**发起**;「待我审批」是**审批**。不要做成三套互不相通的列表。 - 申请人页只看自己的单;台账在业务模块(用章登记、资产管理、采购申请)。 审批通过必须写下游: | 申请 | 落到 | |---|---| | 资产购置 `ASSET_BUY` | 采购单 + 待入库台账 + 付款待办 | | 资产领用 / 用车 | 占用记录(挂台账 id),**交回不改保管人** | | 出差(人事) | 差旅报销草稿 | | 用章外带 | 归还待办 | 保管人 = 台账责任人。占用 = 谁正在用。占用时不要把保管人改成持有人。 实现:`apps/api/src/office/office-apply.ts`。 ### 3.2 登录会话 `apps/api/src/auth/auth.service.ts` + Web `apps/web/src/api/client.ts` - Web 和手机**可以同时在线**。 - 同一账号不能开两个 Web(后登挤掉先登)。JWT 含 `sid`。 - 手机 `clientKind: 'mobile'`:refresh **不轮换**,access 7 天,refresh 3650 天,除非退出一直在线。 - Web token 用 **sessionStorage** + BroadcastChannel(同浏览器多标签共享)。关浏览器再开要重登。 - 桌面端只能扫码,**不能账密**。`clientKind: 'desktop'` 账密会被拒绝。页 `/login/desktop`;手机「我 → 确认电脑登录」;Deep Link `fengyingoa://qr`。 - 登出不带 refresh 时只撤 web/desktop,不踢手机。 ### 3.3 安卓 - `applicationId`:`com.fengying.oa.mobile`。`namespace` 仍是 `com.fysxkj.fengying_oa`,不要改混。 - 当前 release 用 **debug 签名**。华为控制台正式 SHA256 对不上 debug,HMS token 可能拿不到。 - **不要**加 `com.huawei.agconnect` Gradle 插件(AGP 8.6 会炸)。HMS 依赖 + `agconnect-services.json` + Manifest `appid` 留着即可。 ### 3.4 数据范围 `AuthUser.dataScope`:`ALL` / `DEPT` / `SELF`。列表过滤在 `apps/api/src/common/data-scope.ts`。改列表不要只改前端。 --- ## 4. 角色、权限、菜单 菜单源:`apps/api/prisma/menus.ts`,写入 `Permission`(`permType=MENU`)。 `admin` / `owner` 前端 `canSee` 直接放行全部。其他人比 `permissions` 前缀。 种子角色(`prisma/seed.ts`,生产还会有导入岗位): | code | 名称 | dataScope | 大致权限 | |---|---|---|---| | admin | 系统管理员 | ALL | * | | owner | 企业负责人 | ALL | * | | biz_director | 商务总监 | DEPT | office/party/seal/bid/contract/finance/report | | biz_staff | 商务专员 | SELF | office/party/seal/bid/contract | | tech_director | 技术总监 | DEPT | office/bid/project/report | | rd_director / 3d_director / video_director / material_director | 各技术条线总监 | DEPT | 见 `role-groups.ts` | | finance | 财务 | ALL | office/finance/contract/report | | hr | 人事 | ALL | office/hr + 人事设置 | | employee / pm / 各 *\_staff | 员工/项目经理 | SELF | office + 工时等 | | seal / asset / cashier … | 导入岗位 | 视脚本 | `fix-oa-posts.ts` | 总监集合:`DIRECTOR_ROLES`。可发短信:`SMS_SEND_ROLES`。可派工作:`WORK_ASSIGN_ROLES`。 `bid-workflow.ts` 仍保留旧的 `SPECIAL_COORDINATORS` 常量用于兼容,但当前特殊筛选/特殊制标的办理人加载逻辑是全体在职人员,禁止再改回写死两人。 首次登录非 admin:改密 + 完善档案(`/office/onboard`)。`mustChangePassword` / `profileCompletedAt`。 Web 侧栏:`AppLayout` 用 `/auth/me` 带回的 `menus`。 发公告:`hr` 与各总监(`NOTICE_PUBLISH_ROLES` in `App.tsx`)。 --- ## 5. Web 功能全表(按侧栏) 路由全部在 `apps/web/src/App.tsx`。没有单独的移动端 Web。 ### 5.1 工作台 `/workbench` `OverviewPage`:待办/待审批数字、天气问候、快捷入口。API:`GET /office/overview`、`GET /office/weather`。 ### 5.2 个人办公 | 菜单 | 路径 | 页面 | API 要点 | 说明 | |---|---|---|---|---| | 待办事项 | `/office/todos` | OfficeTodosPage | `GET /office/todos` `PATCH /office/todos/:id` | 待办可标已办 | | 待我审批 | `/office/approvals` | OfficeApprovalsPage | `GET /office/approvals` `POST .../decide` | 统一审批入口 | | 我的申请 | `/office/flow` | MyFlowPage | `GET /office/flow` | 只跟踪自己的单 | | 我的日程 | `/office/calendar` | CalendarPage | `GET /office/calendar` | 含证照/印章逾期、开标等 | | 工作汇报 | `/office/reports` | WorkReportPage | `/office/reports*` | 可从工时草稿带入 | | 我的费用 | `/office/expenses` | MyExpensesPage + Expense/Loan 详情 | `/office/my-expenses` `/expenses` `/loans` | 员工发起报销/借款 | | 人事申请 | `/office/hr-apply` | HrApplyPage / HrLeaveDetailPage | `/leave-requests` | 请假加班出差外出离职晋升 | | 用章申请 | `/office/seal-apply` | SealRequestListPage mode=apply | `/seal-requests` | 发起;台账在证照用章 | | 办公申请 | `/office/apply` | OfficeApplyPage | `/office/applies` | 购置/领用/用车 | | 公司公告 | `/office/notices` | NoticePage / NoticeDetailPage | `/office/notices` | 员工阅读 | | 发送短信 | `/office/sms` | OfficeSmsPage | `/sms/*` | 总监/人事/admin | | 工作安排 | `/office/assign` | WorkAssignPage | `/office/work-assignments` | 总监派任务 | | 消息 | `/office/im` | ImPage | `/im/*` | Web IM + 通话弹层 | | 员工通讯 | `/office/directory` | DirectoryPage | `/staff` `/directors` | 点人可开聊 | 出差通过后应落差旅报销草稿(人事申请下游)。 ### 5.3 往来单位 | 菜单 | 路径 | 页面 | API | |---|---|---|---| | 招标单位 | `/party/tenders` | TenderPartyPage | `/parties` | | 参与主体 | `/party/entities` | LegalEntityPage | `/legal-entities` | | 联系名录 | `/party/contacts` | ContactDirectoryPage | `/contacts` | 本机已洗过 5 家主体:随行科技(默认,历史投标多挂这里)、风影软件、风影工程、山东鲁兵、隆创。脚本 `prisma/wash-legal-entities.ts`。OA dump 几乎没有「软件/工程」历史投标,不会自动拆单。 ### 5.4 证照用章 | 菜单 | 路径 | 页面 | API | |---|---|---|---| | 资质台账 | `/seal/qualifications` | QualificationListPage | `/qualifications` | | 证照借用 | `/seal/borrows` | BorrowListPage | `/credential-borrows` | | 用章登记 | `/seal/requests` | SealRequestListPage mode=register | `/seal-requests` | | 印章台账 | `/seal/registry` | SealRegistryPage | `/seals` | 印章状态:`IN_STOCK` 在库,`OUT` 外带(Web 台账有特判)。外带审批通过写归还待办。 ### 5.5 投标管理(核心) 状态机:`apps/api/src/bidding/bid-workflow.ts` 列表桶:`BidListPage` + `?bucket=` ``` 普通 FLOW: 项目筛选 TECH_INITIAL → FETCH → TECH_FINAL → 标书制作 DRAFTING → REVIEW1 → REVIEW2 → PRINT → 投标执行 LIST → PENDING → 开标结果 WON / LOST / FAILED 随时可 TERMINATED(权限内) 特殊 SPECIAL:特殊筛选 / 特殊制标的办理人从**全公司在职人员**中选择,不再写死刘运泽、陈铖。 ``` | 菜单 | 路径 | bucket | |---|---|---| | 项目筛选 | `/bid/screening` | screening(可新建) | | 特殊筛选 | `/bid/special-screening` | 特殊 | | 标书制作 | `/bid/drafting` | drafting | | 特殊制标 | `/bid/special-drafting` | drafting+special | | 投标执行 | `/bid/execution` | execution | | 开标结果 | `/bid/result` | result | | 终止投标 | `/bid/terminated` | terminated | 详情 `/bid/cases/:id`:`BidDetailPage`。新建:`BidCreatePage`。 中标:`ensureWonLedger` 立刻落 **合同草稿 + 项目台账**(幂等)。 未中/终止:可「编辑」登记对方中标单位/金额 → 报表「落标对照」。 API:`/bid-cases`,`PATCH /bid-cases/:id/competitor`。 ### 5.6 合同管理 中标自动生成 `kind=TENDER` 草稿。直签/其他在本页新建,不互相重复。 | 菜单 | 路径 | 说明 | |---|---|---| | 投标合同 | `/contract/tender` | 中标来的 | | 直签合同 | `/contract/direct` | 不经投标 | | 其他合同 | `/contract/incremental` | 其余 | | 人事合同 | `/contract/hr` | `LaborContractPage` `/labor-contracts` | 详情 `/contract/:id`。API `/contracts`。 ### 5.7 资产管理 数字资产 `/asset/digital`、固定资产 `/asset/fixed`:`AssetRegistryPage`,`/registered-assets`。 状态含 `IN_STOCK` / `PENDING_IN` / 占用。入库:`POST /registered-assets/:id/inbound`。 领用占用走办公申请,不在本页把保管人改掉。 ### 5.8 项目管理 中标自动写入台账。直签可在台账补登。 | 菜单 | 路径 | API | |---|---|---| | 项目台账 | `/project/list` | `/projects` | | 任务分解 | `/project/tasks` | `/project-tasks` | | 工时填报 | `/project/timesheets` | `/timesheets` | | 资源看板 | `/project/resources` | OpsPages ResourceBoardPage | | 项目验收 | `/project/acceptance` | 同一 ProjectListPage mode=acceptance | | 采购申请 | `/project/purchases` | PurchasePage(购置审批下游) | | 器材占用 | `/project/assets` | AssetPage 占用记录 | 详情 `/project/:id`。预算/报销:商务差旅进商务,技术部门差旅/工时/采购进技术(页面 description 已写)。 ### 5.9 财务收支 员工在「我的费用」发起;财务在本模块确认、付款、开票。草稿财务看不到(`canSeeSubmittedFinance`)。 | 菜单 | 路径 | API | |---|---|---| | 费用报销 | `/finance/expenses` | `/expenses` | | 借支记录 | `/finance/loans` | `/loans` | | 投标/履约/质保金 | `/finance/bonds/*` | `/bonds` type=TENDER/PERFORMANCE/WARRANTY | | 开票收款 | `/finance/invoices` | `/invoices` | | 付款执行 | `/finance/payments` | `/payments` | 报销必须挂费用对象:投标 / 正式项目 / 部门 / 行政,规则 `assertCostObject`。中标前挂投标,中标后挂项目,不要同时挂。 ### 5.10 组织人事 | 菜单 | 路径 | 说明 | |---|---|---| | 在职员工 | `/hr/employees` | 档案、证件、银行卡 | | 组织管理 | `/hr/org` | 部门树 | | 入转调离 | `/hr/lifecycle` | EmploymentEvent | | 假勤结果 | `/hr/attendance` | 月导入 + 打卡 | | 薪酬核算 | `/hr/payroll` | 核算批次 | | 绩效发放 | `/hr/performance` | | | 发布公告 | `/hr/notices` | 人事发 | | 规章制度 | `/hr/policies` | 同类 Notice,kind 不同 | ### 5.11 经营报表 | 菜单 | 路径 | API | |---|---|---| | 投标转化 | `/report/conversion` | `/reports/bid-conversion` | | 落标对照 | `/report/lost` | `/reports/competitor-wins` | | 项目利润 | `/report/profit` | `/reports/profit` | | 部门人效 | `/report/efficiency` | `/reports/efficiency` | | 资金占用 | `/report/cash` | `/reports/cash` | 页面在 `OpsPages.tsx`。 ### 5.12 系统设置 | 菜单 | 路径 | 说明 | |---|---|---| | 员工账号 | `/system/acl` | 用户/角色 | | 人事参数 | `/system/hr-params` | | | 考勤设置 | `/system/attendance-rules` | | | 数据字典 | `/system/dicts` | | | 权限设置 | `/system/perms` | 菜单授权 | | 编号规则 | `/system/numbers` | `nextBizNo` | | 短信服务 | `/system/sms` | 签名/模板/绑定事件 | | 消息通道 | `/system/messages` | 占位页,文案可能过时(IM 已做) | | 手机端升级 | `/system/app-release` | 写 `app-release.json` | | 操作日志 | `/system/logs` | | 公开:`GET /system/app-release`(手机 OTA)。 --- ## 6. 核心流程(改业务先对这条链) ### 投标 → 合同/项目 筛选 → 取标 → 终审 → 制标 → 一审二审 → 印封 → 执行 → 开标。 **WON** → 合同草稿 + 项目台账。售前报销仍挂投标;正式花费挂项目。 ### 报销 / 借款 员工草稿 → 提交 PENDING → 财务/总监审批 APPROVED → 付款执行。借款还有 OPEN / OFFSET 冲账。 ### 办公申请 发起 PENDING → 审批(admin/owner/hr/biz_director)→ 按 kind 写采购/占用/待入库。 ### 用章 申请 → 审批 → 外带则归还待办;台账 status 在库/外带。 ### 人事假勤 `LeaveRequest.kind`:LEAVE / OVERTIME / TRIP / OUT / RESIGN / PROMOTE。出差通过 → 报销草稿。 统一待办 `TodoTask`、统一审批 `ApprovalTask`(`bizType` 区分投标/合同/报销/用章/人事/办公)。 待我审批一页消化,不要为每个 biz 再做孤立审批页。 --- ## 7. API 地图 前缀均 `/api/v1`。鉴权 JWT,除标注 Public。 | 模块 | 文件 | 主要路径 | |---|---|---| | 登录 | `auth/` | `/auth/login` `/auth/refresh` `/auth/me` `/auth/logout` `/auth/profile` `/auth/password` `/auth/qr/*` | | 办公 | `office/` | `/office/*` 待办审批日程公告申请汇报工作安排天气地理 | | 投标 | `bidding/` | `/bid-cases` | | 合同 | `contract/` | `/contracts` | | 项目 | `project/` | `/projects` `/project-tasks` `/timesheets` | | 用章 | `seal/` | `/seal-requests` `/seals` `/qualifications` `/credential-borrows` | | 财务 | `finance/` | `/expenses` `/loans` `/bonds` `/invoices` `/payments` | | 人事 | `hr/` | `/employees` `/leave-requests` `/payroll` `/attendance` `/labor-contracts` `/org` | | 主数据 | `master/` | `/parties` `/legal-entities` `/contacts` `/registered-assets` | | 系统 | `system/` | `/system/menus` `/system/acl` `/system/dicts` `/system/numbers` `/system/app-release` `/system/logs` | | 报表 | `report/` | `/reports/*` | | 文件 | `files/` | `POST /files` `GET /files/:id` | | IM | `im/` | `/im/conversations` `/im/messages` `PATCH /im/messages/:id` `/im/calls*` `/im/read` | | 推送 | `push/` | 设备 token 注册与下发 | | 短信 | `sms/` | 签名模板发送 OTP | | 通讯录 | `business/` | `/staff` `/directors` | | 健康 | `health/` | `/health` | | 协议 | `legal/` | 用户协议页 | 响应外包一层 `{ data }`,Web 用 `unwrapList` / `body.data`。手机 `oa_client.dart` 同样解包。 错误:`HttpErrorFilter`。请求号:`X-Request-Id`。 地理:`/office/geo/*` 走腾讯位置服务,`TENCENT_MAP_KEY`。 --- ## 8. 数据模型(Prisma) 完整定义:`apps/api/prisma/schema.prisma`。 **账号**:User、RefreshToken(`clientKind`)、AuthQrTicket、Role、Permission、UserRole、PushDevice **组织人事**:Department、Employee、LaborContract、EmploymentEvent、AttendanceMonth、AttendancePunch、LeaveRequest、HrSetting、AttendanceRule、PayrollRun、PayrollItem **主数据**:LegalEntity、BusinessParty、PartyContact、DictItem、NumberRule、Qualification、SealItem、RegisteredAsset **投标**:BidCase(含 `winnerName/Amount/Note`)、BidReviewer、BidAssignee、BidDocVersion、BidActionLog **合同项目**:Contract、ContractReviewer、PaymentMilestone、Project、ProjectBudget、ProjectTask、Timesheet、ProjectAcceptance、ProjectAcceptMaterial、ProjectChange **财务**:ExpenseClaim、ExpenseLine、LoanRecord、BondRecord、InvoiceRecord、PaymentRecord、PurchaseRequest **办公**:TodoTask、ApprovalTask、WorkReport、WorkAssignment、OfficeApply、Notice、AssetOccupancy、CredentialBorrow、SealRequest **IM**:ImConversation、ImConversationMember、ImMessage(`fingerprint` 唯一)、ImReadCursor、ImMessageReceipt、ImCall、ImCallMember、ImCallSignal **其它**:FileAsset、SmsSendLog、SmsEventBind、SmsOtp、OperationLog 编号:`NumberRule` + `src/system/numbering.ts`。注意:**项目筛选、特殊筛选的招标编号和合同编号必须手填,系统不得另生成隐藏编号**;报销等内部单据仍可使用系统编号。 --- ## 9. 手机端 Flutter。版本:`apps/native/lib/app_config.dart` + `pubspec.yaml` + `test/widget_test.dart` + `apps/api/data/app-release.json`,四者一起改。 默认连生产 API / IM。底栏四页:`messages_page`、`workbench_page`、`directory_page`、`mine_page`。 工作台宫格来自登录菜单。`open_module.dart` / `module_list_page.dart` 把菜单码映射到 REST 列表(投标→`/bid-cases` 等)。系统设置、发短信、工作台里的「消息」入口故意不打开(消息在底栏)。详情 `record_detail_page.dart` + `biz_route.dart` 反查详情 API。中文:`labels.dart`。 聊天/通话的关键文件:聊天 `chat_page.dart`,来电 `home_shell.dart` + `call_page.dart`。当前版本为 3.1.8+318;音视频、截屏提醒、撤回再编辑、会话已读后保留等需求必须按 `/opt/HANDOFF.md` 的“未完成/需真机回归”继续验证,不能仅凭旧交接宣称已修。 打包: ```bash cd /opt/apps/native && flutter build apk --release && bash scripts/publish-apk.sh ``` 产物:`/opt/apps/native/dist/fengying-oa.apk`。本机 root 跑 Flutter 会警告,仍能编过。下一版必须在 **3.1.8+318** 基础上递增。 OTA:比较 `build`。后台「手机端升级」。 --- ## 10. IM 协议:4 字节大端长度 + UTF-8 JSON Protocal。登录用 OA accessToken 调 `GET /auth/me`。 **新 `server.js`**:`Map>`,同一账号多端/重连 **不再 kick**。旧版会踢旧连接,客户端若把 `_want=false` 会永久断线。 生产必须换新 `server.js` 长连接才稳。新 APK 即使对旧 IM 也不会再「被踢后永不再连」。 HTTP 消息仍落 Postgres。TCP 负责推送。通话信令走 `/im/calls/:id/signal`(HTTP 轮询),WebRTC + 国内 STUN。 `PATCH /im/messages/:id`:2 分钟内改自己的文本。 --- ## 11. 文件、短信、推送、地图 - 文件:`FilesService` 存 MinIO(本机 docker)或本地 `uploads/`。下载带鉴权。手机 `AuthImage` / `fileBytes` 有内存缓存。 - 短信:阿里云 Dysmsapi。系统设置申请签名/模板,事件绑定在 `SmsEventBind`。投标节点会 `emitSms`。 - 推送:vivo / OPPO / 魅族 / 华为。设备 `PushDevice`。华为 App ID `118301849`。 - 地图:腾讯。选点 `location_pick_page.dart` + `tencent-lbs`;POI 用真实距离,不要写死「100m内」。 --- ## 12. 前端约定 **Web** - 新列表:Ant Design Table + 中文 `zh()` / `StatusTag`,不要直接渲染枚举码。 - 申请类列表:`ApplyBucketBar` + URL `bucket`。 - 抽屉表单:`FormDrawer.tsx`。附件:`FileAttachments.tsx`。 - 枚举中文:`apps/web/src/labels.ts`,与手机 `labels.dart` 对齐。 - token 只放 sessionStorage。 **手机** - 企业微信风:`theme.dart`、`widgets/wecom.dart`。 - 英文字段:补 `fieldZh` / `zhMap`,不要在单个页写死。`OUT` 人事=外出、印章=外带,有冲突时按字段上下文。 --- ## 13. 脚本与历史数据 `apps/api/prisma/` 里常见脚本(跑前看文件头,生产库只读): | 脚本 | 用途 | |---|---| | seed.ts | 空库演示 | | sync-menus.ts | 菜单/权限 | | import-hr.ts / import-prod.ts | 从旧 OA 导人事/业务 | | wash-legal-entities.ts | 参与主体清洗 | | wash-oa-ledger.ts / wash-oa-flow-finance.ts | 台账/财务清洗 | | sync-oa-attachments.ts | 附件 | | grant-admin-equivalents.ts / ensure-prod-admin.ts | 管理员 | | fix-oa-posts.ts / fix-expense-applicants.ts | 岗位与报销申请人 | 旧 OA 在生产 MySQL `oa`(只读扒数,用户禁止改生产旧库、禁止整用户过来)。dump 曾放 `/opt/import/prod/`。 --- ## 14. 密钥与账号(Codex 直接用,不必再问) ### 生产 SSH ``` 主机:47.96.23.244 用户:root 密码:VYTnNdfFfrX7xTP 代码目录:/data/fengying-oa/ ``` ```bash export SSHPASS='VYTnNdfFfrX7xTP' RSH='ssh -o StrictHostKeyChecking=no' DEST=root@47.96.23.244 sshpass -e ssh -o StrictHostKeyChecking=no "$DEST" 'hostname; systemctl is-active fengying-api fengying-im' ``` 发版: ```bash export SSHPASS='VYTnNdfFfrX7xTP' RSH='ssh -o StrictHostKeyChecking=no' DEST=root@47.96.23.244 # 先 nest build、确认 APK 已 publish sshpass -e rsync -az -e "$RSH" /opt/apps/api/dist/ $DEST:/data/fengying-oa/apps/api/dist/ sshpass -e rsync -az -e "$RSH" /opt/apps/api/data/app-release.json $DEST:/data/fengying-oa/apps/api/data/app-release.json sshpass -e rsync -az -e "$RSH" /opt/apps/web/dist/ $DEST:/data/fengying-oa/apps/web/dist/ # 若本机有最新 web 构建 sshpass -e rsync -az -e "$RSH" /opt/apps/im-server/server.js $DEST:/data/fengying-oa/apps/im-server/server.js sshpass -e rsync -az -e "$RSH" /opt/apps/native/dist/fengying-oa.apk $DEST:/data/fengying-oa/apps/web/dist/fengying-oa.apk sshpass -e ssh -o StrictHostKeyChecking=no "$DEST" 'systemctl restart fengying-api fengying-im; sleep 2; systemctl is-active fengying-api fengying-im' ``` 生产 API `.env` 在远端 `/data/fengying-oa/apps/api/.env`。上去先 `cat`,**不要用本机 development 的 JWT/Postgres 覆盖生产**。发 IM/API 后确认 Prisma 已有 `RefreshToken.clientKind`、`AuthQrTicket`、`BidCase.winner*`。 ### 旧 OA MySQL(只读) ``` 用户:root 密码:BtH9ywG8gFTXG9w! 库:oa ``` ### 本机 API `apps/api/.env` ``` NODE_ENV=development APP_PORT=3000 DATABASE_URL=postgresql://ops:ops_dev_only@localhost:5432/enterprise_ops REDIS_URL=redis://localhost:6379 MINIO_ACCESS_KEY=opsadmin MINIO_SECRET_KEY=opsadmin_dev_only JWT_ACCESS_SECRET=change-me-access-secret-dev-only JWT_REFRESH_SECRET=change-me-refresh-secret-dev-only POSTGRES_USER=ops POSTGRES_PASSWORD=ops_dev_only POSTGRES_DB=enterprise_ops ALIYUN_SMS_ACCESS_KEY_ID=LTAI5t9DdPycJp7sabQyEWRc ALIYUN_SMS_ACCESS_KEY_SECRET=RPFVqKDUpEvxo3IsqlxLB5qIywfheM ALIYUN_SMS_ENDPOINT=dysmsapi.aliyuncs.com ALIYUN_SMS_REGION=cn-hangzhou ALIYUN_SMS_DEFAULT_SIGN=江苏风影随行科技 ALIYUN_RAM_LOGIN=yongkangdai10@1801267455383083.onaliyun.com TENCENT_MAP_KEY=L6NBZ-47KLI-OL7G2-UMROZ-XPI3T-SLFC7 IM_TCP_PORT=8901 IM_HTTP_PORT=8902 IM_INTERNAL_TOKEN=fengying-im-internal VIVO_PUSH_APP_ID=106114371 VIVO_PUSH_APP_KEY=b521c805229a00e85a53d4b535a0cafe VIVO_PUSH_APP_SECRET=2e1536ed-ed2f-4cd6-88d1-67aa9a93eeba OPPO_PUSH_APP_ID=37438377 OPPO_PUSH_APP_KEY=e23832b52fbb46e18de6eef5affda2e9 OPPO_PUSH_APP_SECRET=e9cf0bf35052484ebcaf15d56576468b OPPO_PUSH_MASTER_SECRET=e9cf0bf35052484ebcaf15d56576468b MEIZU_PUSH_APP_ID=157643 MEIZU_PUSH_APP_KEY=4263a8d18de442b6a17203866ce1546d MEIZU_PUSH_APP_SECRET=2a0bafc2d47a470f8b2aad3f182d2377 HUAWEI_PUSH_APP_ID=118301849 HUAWEI_PUSH_CLIENT_ID=118301849 HUAWEI_PUSH_CLIENT_SECRET=85c01c5c853d571e2d83fd8629efb21e9b2cad0bdfadd6f32c2c5b42e38d7888 ``` 本机 IM `.env` 写的是 `OA_API_URL=http://127.0.0.1:3010/api/v1`,本机 nest 常在 3000,连本机 IM 请改 3000。生产 systemd:`OA_API_URL=http://127.0.0.1:3010/api/v1`。 ### 华为 AGC - 包名 `com.fengying.oa.mobile`,App ID `118301849` - 服务端 Client Secret:`85c01c5c853d571e2d83fd8629efb21e9b2cad0bdfadd6f32c2c5b42e38d7888` - JSON:`apps/native/android/app/agconnect-services.json` - JSON `client.client_id`:`1993069421077225280` - JSON `client.client_secret`:`93585E101B911EFF0F99F215050CD3E4297F67CB004E0D8E070AAB1EEDD7E725` - JSON `client.api_key`:`DgEDAFLpMeUxEsIEiKams+kYWThvAg0ioMuL1rW9BEP4i71BuChEPD7Udg1AbDKbg+ljwD+pBACxktLqHwmUlSxOCwsLHiRAKQDBFw==` - `cp_id`:`10086000847744494`;`product_id`:`101653523864520415` ### OPPO Maven ``` https://maven.columbus.heytapmobi.com/repository/releases/ username: nexus password: c0b08da17e3ec36c3870fed674a0bcb36abc2e23 ``` ### 腾讯地图 / vivo Manifest - `TENCENT_MAP_KEY=L6NBZ-47KLI-OL7G2-UMROZ-XPI3T-SLFC7` - Manifest vivo:`api_key=b521c805229a00e85a53d4b535a0cafe`,`app_id=106114371` ### 本机演示账号 `admin` / `Admin@123456`。种子其他人常见 `Demo@123456` 或导入后 `123456`。不要拿去撞生产。 --- ## 15. 发生产清单 1. `npx nest build`,同步 `apps/api/dist/` 2. 若改了 Web:`pnpm --filter web build`,同步 `apps/web/dist/` 3. 同步 `server.js`、APK、`app-release.json` 4. `systemctl restart fengying-api fengying-im` 5. 后台确认手机端升级为当前最新 build(现为 318) 6. 真机覆盖安装当前最新版(现为 3.1.8) **APK 保留规则:**云主机只保留当前 OTA 指向的最新安装包(对外统一为 `apps/web/dist/fengying-oa.apk`)。每次发版先精确列出 `/data/fengying-oa` 下的 `.apk` / `.aab`,确认新包可下载后删除其他旧包,不要在 `apps/native/dist` 等目录留历史副本。 API/IM/APK 是否为同一版必须分别核对,不能只看网页或 OTA JSON。 --- ## 16. 已知坑 / 未完成 1. 生产 API 已同步 2026-09-01 的数据修复代码;Web、IM、APK 后续每次仍要分别核对版本和构建时间。 2. 华为正式推送:缺正式签名 + AGConnect 插件未进构建。 3. `OUT` 中文:人事外出 vs 印章外带。 4. 系统设置「消息通道」文案还写「本期不做原生 IM」,已过时。 5. Web IM 与手机 IM 都可用,但通话依赖双方打开页 + STUN,失败别先改来电弹层。 6. 手机工作台对报表/部分系统页映射为 `null`,点了可能没页。 7. 打开 docx 依赖手机已装 WPS/Office。 8. 历史投标主体不会自动拆到软件/工程公司。 9. `apps/native/README.md`、部分 api README 版本号过时,以 `app_config.dart` 和本文件为准。 10. 薪酬/绩效/资源看板有页面,深度以现网数据为准,缺的是补流程不是再铺空壳。 11. 问题视频:`/opt/问题视频/`。来电测试对象常为「戴永康」。 --- ## 17. 建议接着做的顺序 1. 用第 14 节发生产(API + Web + IM + APK + OTA),不要再问密码。 2. 真机覆盖 2.3.2:来电能关、消息上屏、图片/地图能点、印章「在库」。 3. 网页重新登录:确认扫码登电脑、Web 互踢、侧栏「落标对照」。 4. 再改业务:遵守第 3 节办公规则;改投标走 `bid-workflow.ts`,不要只改一个列表页。 5. 英文残留:补 `labels.ts` / `labels.dart`。 6. 华为推送真机(正式签名)。 7. 用户新需求再开功能,不要平行重构目录。 改完菜单同步 `sync-menus.ts`。改完枚举两边 labels 一起改。改完版本四个文件一起改。