含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
30 KiB
风影智慧办公 · 全系统转交文档
更新日期: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. 你接手后立刻要知道的事
- 这是一家约四十人的工程/软件公司内部 OA,从旧 Java OA(生产 MySQL 库
oa)迁到本仓库。 - 用户同时用 网页
https://oa.fysxkj.com和 安卓包。IM 是自研 TCP,不是环信。 - 办公模块有固定产品规则(第 3 节)。乱改列表筛选或审批不写下游,会被用户打回。
- 2026-09-01 的 API 数据修复、旧终止投标恢复兼容和项目类型修复已经发到生产;精确结果与备份位置见
/opt/HANDOFF.md。后续不要依据本文件旧版本描述判断是否已部署。 - 回复用户用简体中文。不要主动
git commit/push,除非用户说。 - 改菜单后必须跑
apps/api/prisma/sync-menus.ts,侧栏才更新。 - 手机登录/刷新必须带
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 模板
本机常用:
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 Linkfengyingoa://qr。 - 登出不带 refresh 时只撤 web/desktop,不踢手机。
3.3 安卓
applicationId:com.fengying.oa.mobile。namespace仍是com.fysxkj.fengying_oa,不要改混。- 当前 release 用 debug 签名。华为控制台正式 SHA256 对不上 debug,HMS token 可能拿不到。
- 不要加
com.huawei.agconnectGradle 插件(AGP 8.6 会炸)。HMS 依赖 +agconnect-services.json+ Manifestappid留着即可。
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 的“未完成/需真机回归”继续验证,不能仅凭旧交接宣称已修。
打包:
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<userId, Set<socket>>,同一账号多端/重连 不再 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 ID118301849。 - 地图:腾讯。选点
location_pick_page.dart+tencent-lbs;POI 用真实距离,不要写死「100m内」。
12. 前端约定
Web
- 新列表:Ant Design Table + 中文
zh()/StatusTag,不要直接渲染枚举码。 - 申请类列表:
ApplyBucketBar+ URLbucket。 - 抽屉表单:
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/
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'
发版:
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 ID118301849 - 服务端 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. 发生产清单
npx nest build,同步apps/api/dist/- 若改了 Web:
pnpm --filter web build,同步apps/web/dist/ - 同步
server.js、APK、app-release.json systemctl restart fengying-api fengying-im- 后台确认手机端升级为当前最新 build(现为 318)
- 真机覆盖安装当前最新版(现为 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. 已知坑 / 未完成
- 生产 API 已同步 2026-09-01 的数据修复代码;Web、IM、APK 后续每次仍要分别核对版本和构建时间。
- 华为正式推送:缺正式签名 + AGConnect 插件未进构建。
OUT中文:人事外出 vs 印章外带。- 系统设置「消息通道」文案还写「本期不做原生 IM」,已过时。
- Web IM 与手机 IM 都可用,但通话依赖双方打开页 + STUN,失败别先改来电弹层。
- 手机工作台对报表/部分系统页映射为
null,点了可能没页。 - 打开 docx 依赖手机已装 WPS/Office。
- 历史投标主体不会自动拆到软件/工程公司。
apps/native/README.md、部分 api README 版本号过时,以app_config.dart和本文件为准。- 薪酬/绩效/资源看板有页面,深度以现网数据为准,缺的是补流程不是再铺空壳。
- 问题视频:
/opt/问题视频/。来电测试对象常为「戴永康」。
17. 建议接着做的顺序
- 用第 14 节发生产(API + Web + IM + APK + OTA),不要再问密码。
- 真机覆盖 2.3.2:来电能关、消息上屏、图片/地图能点、印章「在库」。
- 网页重新登录:确认扫码登电脑、Web 互踢、侧栏「落标对照」。
- 再改业务:遵守第 3 节办公规则;改投标走
bid-workflow.ts,不要只改一个列表页。 - 英文残留:补
labels.ts/labels.dart。 - 华为推送真机(正式签名)。
- 用户新需求再开功能,不要平行重构目录。
改完菜单同步 sync-menus.ts。改完枚举两边 labels 一起改。改完版本四个文件一起改。