# LisgloSIPS V2 实施状态 > 本文件用于跨 Codex 会话持续交接。每次新会话开始必须先读取本文件和 `SOFTSWITCH_PLATFORM_DESIGN_V2.md`;每次任务完成、失败或被阻塞后必须更新本文件。 ## 当前状态 - 当前任务:需求修改/二期变更:供应商启停移除、客户启停入口、落地网关 CPS 数字校验、当前通话 SIP 状态 - 本次变更摘要:本地代码已移除供应商启停 API/前端状态展示;客户管理页面补充启用/禁用按钮和确认弹窗;落地网关 CPS 改为 1-10000 数字输入与后端校验;当前通话列表改展示 SIP 信令状态;同步更新测试用例和设计说明。 - 总体状态:S30 已完成;本地 KVM 开发环境 V2 闭环原冻结 release 为 `s28-v2-20260621220924`;B 二期冻结基线为 `s42-phase2-business-prefix-20260624140000`,当前 release 为 `s44-phase2-gateway-match-20260628225000`;B preflight 通过,API ready 返回 ok,HTTPS 首页加载前端资产 `index-BBq8iMHt.js`;Redis `cfg:active_version=s28-v1`,A 当前 EVALSHA 脚本在 Redis 中存在;A 已加载二期 Lua 热路径 SHA1 `aff9995dbda2a2425e95a3e25b5d7ce194348ce7`,OpenSIPS 配置 SHA-256 为 `f163fa37f7aaa492e12ed496cb82f07d9b0f1030a6ed297855a22d2c4bf1c8a5`;A/B/T 目标服务均 active;已完成 1 通 S40 最小呼叫验收并确认 CDR/计费落库;B 登录依赖 Argon2id 冒烟通过;当前仍未导入真实 80 万手机号段;阿里云迁移前需按 S30 Runbook 重新演练 - 最后更新:2026-06-29 11:29 +08:00 - 当前阻塞:无阻塞。遗留问题:本次未连接服务器、未发布 A/B/T、未执行数据库 schema 变更或服务重启;真实 80 万手机号段尚未导入;OpenSIPS dialog 残留根因仍待单独分析;真实 B 服务器浏览器登录态逐页按钮、真实录音文件播放和 Nginx/X-Accel 链路仍需用户测试复核;S40 呼叫时脚本内置 UAS 端口提示已占用,但现有 UAS 正常接起且通话/CDR/计费成功;A/T 各有一个无关 `fwupd-refresh.service` failed unit,未处理。 - 当前环境:A/B/T 为本地 KVM 开发服务器,通过 Tailscale 联调;开发完成后再迁移阿里云 - 私密访问资料:`.codex-private/SERVER_ACCESS.md`,只在实际连接时读取,禁止回显 ## 服务器索引 | 代号 | 地址 | 用户 | 用途 | | --- | --- | --- | --- | | A | `100.90.90.90` | `hector` | OpenSIPS、RTPEngine、录音内存盘、HEP、Exporter | | B | `100.90.90.91` | `hector` | API、Worker、Redis、MySQL、录音、HOMER、Prometheus/Grafana | | T | `100.93.185.30` | `hector` | 已安装 OpenSIPS,模拟客户 SIP 注册与呼叫 | ## 任务状态表 状态只能使用:`待开始`、`进行中`、`已完成`、`已阻塞`、`已跳过`。 | ID | 任务 | 状态 | 主要产物/验收 | | --- | --- | --- | --- | | S00 | SSH 安全接入与凭据迁移 | 已完成 | 三机 SSH Key、密码轮换、访问验证、访问 Runbook;后续 SSH 加固只调整管理端口并保留 Key 登录 | | S01 | 三机只读资产盘点 | 已完成 | `docs/inventory-A.md`、`inventory-B.md`、`inventory-T.md`、汇总报告 | | S02 | 网络与安全组验收 | 已完成 | `docs/infra-check.md`,含本地端口矩阵和未来阿里云安全组清单 | | S03 | Server B 基础系统初始化 | 已完成 | `docs/SERVER_B_BASELINE_RUNBOOK.md`、`infra/server-b/s03/`、远端备份 | | S04 | Server B MySQL 与 Redis | 已完成 | `docs/SERVER_B_DATA_SERVICES_RUNBOOK.md`、`infra/server-b/s04/`、备份恢复演练 | | S05 | Server B Node.js、Nginx 与 TLS 基线 | 已完成 | `docs/SERVER_B_WEB_RUNTIME_RUNBOOK.md`、`infra/server-b/s05/`、HTTPS 与重启验收 | | S06 | Server B HOMER 与基础监控 | 已完成 | HEP 接收、HOMER API、Prometheus、Grafana、exporters、A Node Exporter | | S07 | V2 后端 Monorepo 骨架 | 已完成 | NestJS/Fastify/Prisma、日志、配置、健康检查、测试框架;本地和 B 均可构建 | | S08 | 数据库 Schema 与迁移 | 已完成 | Prisma Schema、初始化迁移、内置角色/权限种子、B 临时空库迁移和备份恢复测试 | | S09 | 登录认证基础 | 已完成 | 登录、刷新、退出、Argon2id、Session/Token 安全 | | S10 | 用户、角色、权限与审计 | 已完成 | RBAC、内置角色保护、重置密码、审计拦截器 | | S11 | 客户管理 API | 已完成 | 客户 CRUD、启停、余额/授信读取、单元与集成测试 | | S12 | 充值与不可变余额流水 | 已完成 | 客户/供应商充值、幂等、事务、审计、并发测试 | | S13 | 客户网关 API | 已完成 | IP/SIP 注册认证配置、启停、密码安全存储 | | S14 | 客户网关策略与 Redis 配置发布 | 已完成 | 主被叫匹配、优先级、Outbox、配置版本和回滚 | | S15 | 供应商管理 API | 已完成 | 供应商 CRUD、余额/授信、启停 | | S16 | 落地网关 API | 已完成 | CPS/并发/时段/编码/号码转换/周期费率/启停 | | S17 | 落地线路组 API | 已完成 | 成员增删、优先级重排、并发汇总、完整性校验 | | S18 | Server A OpenSIPS 基线 | 已完成 | 15060、认证、防扫描、MI 本地绑定、Redis/HEP 模块 | | S19 | Server A RTPEngine 与录音 tmpfs | 已完成 | 内核转发、RTP 端口段、3 GiB tmpfs、录音 ready 规则 | | S20 | Server A Redis 热路径与 HEP/指标 | 已完成 | Lua 原子检查、配置读取、HEP 9060、Node Exporter | | S21 | Server T 客户模拟配置 | 已完成 | IP/SIP 注册两套测试账号、呼叫脚本、失败场景脚本 | | S22 | CDR Redis Stream | 已完成 | 标准事件、XADD/XREADGROUP、ACK、Pending、死信、幂等 | | S23 | 最小计费与余额扣减 | 已完成 | Decimal 计费、客户费用/成本、重复 CDR 不重复扣费 | | S24 | 录音搬运与播放 | 已完成 | A->B 私网搬运、校验、Range、权限、源文件安全删除 | | S25 | 质检后端 | 已完成 | 抽检规则、稳定抽样、录音详情、质检保存和审计 | | S26 | Dashboard 聚合 | 已完成 | 实时占位/聚合指标、趋势、失败码、异常网关、待质检 | | S27 | React Demo 接入真实 API | 已完成 | API Client、核心运营页真实 API、错误/加载/空状态 | | S28 | 三机端到端联调 | 已完成 | T 发起呼叫,A 转发录音,B 出 CDR/费用/信令/质检 | | S29 | 性能、故障与安全测试 | 已完成 | CPS/并发/录音、Redis/MySQL/Worker 故障、越权与重放 | | S30 | 备份、Runbook、灰度与上线 | 已完成 | 恢复演练、上线/回滚脚本、灰度记录、验收报告 | ## 每次新会话的固定开场指令 可直接向 Codex 发送: ```text 请先读取 SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md, 以及本任务关联的现有脚本/代码。只执行状态文件里的当前任务,不提前执行后续任务。 开始前复核上次交接记录;涉及服务器写操作、服务重启、防火墙、密码、数据库迁移或数据删除时先说明影响和回滚点。 完成后运行验收检查,并更新 IMPLEMENTATION_STATUS.md 的任务状态、产物、验证结果、遗留问题和下一任务。 ``` ## 单任务交接记录模板 每完成一个任务,在本节顶部新增记录: ```markdown ### YYYY-MM-DD HH:mm - Sxx 任务名称 - 状态:已完成 / 已阻塞 - 操作服务器:A / B / T / 本地 - 完成内容: - 修改文件: - 安装软件及固定版本: - 执行的关键命令:只记录无密码版本 - 验证结果: - 回滚方式: - 未解决问题: - 下一任务:Sxx ``` ## 交接记录 ### 2026-06-29 11:29 - 需求修改/二期变更:供应商启停移除、客户启停入口、CPS 数字校验、当前通话 SIP 状态 - 状态:已完成 - 操作服务器:本地 - 完成内容:移除供应商管理的启用/禁用 API 和前端状态列,供应商新增/编辑不再接收或返回启停字段;客户管理页面新增启用/禁用按钮、权限裁剪和确认弹窗,调用既有 `POST /customers/:id/enable|disable`;落地网关 `cpsLimit` 改为 1-10000 整数,Web 表单使用数字输入且不再接受 `120 CPS` 这类文本;当前通话 API 新增 `sipState`、`sipStatusCode`、`sipStatusText`,Web 列表改显示 SIP 信令状态;同步更新 `docs/TEST_PLAN_AND_CASES.md` 和 `SOFTSWITCH_PLATFORM_DESIGN_V2.md`。 - 修改文件:`apps/api/src/modules/vendors/`、`apps/api/src/modules/vendor-gateways/`、`apps/api/src/modules/active-calls/`、`apps/web/src/App.jsx`、`apps/web/src/api.js`、`apps/web/src/pages/CustomersPage.jsx`、`apps/web/src/pages/VendorsPage.jsx`、`apps/web/src/pages/VendorGatewaysPage.jsx`、`apps/web/src/pages/ActiveCallsPage.jsx`、`apps/web/src/utils/formatters.js`、`apps/web/src/fixtures/devFixtures.js`、`docs/TEST_PLAN_AND_CASES.md`、`SOFTSWITCH_PLATFORM_DESIGN_V2.md`、`IMPLEMENTATION_STATUS.md` - 验证结果:`corepack pnpm@10.33.0 typecheck` 通过;`corepack pnpm@10.33.0 test -- apps/api/src/modules/vendors/vendors.e2e.spec.ts apps/api/src/modules/customers/customers.e2e.spec.ts apps/api/src/modules/vendor-gateways/vendor-gateways.e2e.spec.ts apps/api/src/modules/active-calls/active-calls.service.spec.ts` 实际触发 Vitest 全量选择,25 个测试文件 91 条测试通过;`corepack pnpm@10.33.0 lint` 通过;`corepack pnpm@10.33.0 build` 通过,Web 产物为 `index-CBRZZA7t.js` / `index-B2uYBtS3.css`。 - 回滚方式:回退上述本地文件即可恢复旧行为;本次未新增数据库迁移、未连接 A/B/T、未改 Redis/OpenSIPS/RTPEngine/Worker 运行配置、未重启服务。 - 未解决问题:本次仅完成本地代码和测试用例调整,尚未发布到 A/B/T;供应商表的数据库 `status` 字段仍保留用于历史兼容和软删除内部状态,不再作为产品启停能力暴露。 ### 2026-06-29 10:52 - 修复 B Web 登录 500:补齐 Argon2id vendor lib - 状态:已完成 - 操作服务器:B、本地 - 完成内容:根据 B `lisglosips@api` 日志定位 `/api/v2/auth/login` 500 根因:当前 s44 release 缺少 `packages/auth/vendor/argon2id/lib/setup.js`,导致密码校验动态 import 失败;备份当前 s44 Argon2id vendor 目录到 `/var/backups/lisglosips-auth-vendor/20260629T105135`;从已验证的旧 release 复制完整 `packages/auth/vendor/argon2id` 到当前 s44 release,设置属主 `root:lisglosips`、目录 `0750`、文件 `0640`;重启 `lisglosips@api`;修复本地 `scripts/build-release-artifact.mjs`,将 release artifact 条目从仅打包 `packages/auth/vendor/argon2id/dist` 改为打包完整 `packages/auth/vendor/argon2id`。 - 修改文件:`scripts/build-release-artifact.mjs`、`IMPLEMENTATION_STATUS.md`;B 当前 release `/opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000/packages/auth/vendor/argon2id`。 - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:B `journalctl -u lisglosips@api`、`cp -a`、`chown`、`chmod`、`systemctl restart lisglosips@api`、`curl http://127.0.0.1:3000/api/v2/health/ready`、Argon2id hash/verify 冒烟;本地 `node scripts/build-release-artifact.mjs --release-id s-local-auth-vendor-check --check`。 - 验证结果:B API active;API ready 返回 `ok`;当前 release 内 Argon2id hash/verify 输出 `argon2-smoke-ok`;最近 API 日志未再出现 `ERR_MODULE_NOT_FOUND` 或缺少 `setup.js`;本地 release artifact check 通过,确认 39 个 required entries 均存在。 - 回滚方式:如需撤回本次热修复,可恢复 `/var/backups/lisglosips-auth-vendor/20260629T105135` 到 `/opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000/packages/auth/vendor/argon2id` 后重启 `lisglosips@api`,但会恢复登录 500;更完整的应用回滚仍按 S30 Runbook 切回上一 release。 - 未解决问题:尚未由浏览器完成真实账号登录复测;本地脚本已修复,但 s44 已生成 tarball 本身仍是旧 artifact,需要后续重新生成 release artifact 才能让制品与热修复一致。 ### 2026-06-29 10:45 - A/B/T 重启后恢复本地联调环境 - 状态:已完成 - 操作服务器:A、B、T、本地 - 完成内容:按 `IMPLEMENTATION_STATUS.md`、`SOFTSWITCH_PLATFORM_DESIGN_V2.md`、二期迁移 Runbook 和 S30 Runbook 建立边界;先只读检查三机状态;确认 A `opensips`、`rtpengine`、`rtpengine-recording-daemon`、`lisglosips-redis-auth-proxy`、`lisglosips-node-exporter` active,`lisglosips-redis-hotpath-load` 为 oneshot 且上次结果 success;确认 A Lua SHA1 `aff9995dbda2a2425e95a3e25b5d7ce194348ce7` 与 `opensips.cfg` EVALSHA 一致,OpenSIPS 配置 SHA-256 为 `f163fa37f7aaa492e12ed496cb82f07d9b0f1030a6ed297855a22d2c4bf1c8a5`,`opensips -C -f /etc/opensips/opensips.cfg` 通过;确认 B current 为 `/opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000`,启动关机后 inactive 的 `lisglosips@recording-worker` 与 `lisglosips@config-publisher`;确认 B MySQL、Redis、Nginx、API、CDR Worker、Recording Worker、Config Publisher 均 active;确认 T `opensips`、`rtpengine`、`apache2`、`mariadb`、`lisglosips-s28-uas` active,S28/S40 脚本存在可执行。 - 修改文件:`IMPLEMENTATION_STATUS.md` - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`systemctl is-active`、`systemctl --failed`、`readlink -f /opt/lisglosips/current`、B `systemctl start lisglosips@recording-worker lisglosips@config-publisher`、B `lisglosips-release-preflight.sh`、B `curl http://127.0.0.1:3000/api/v2/health/ready`、B `curl -k https://127.0.0.1/`、A/T `opensips -C -f /etc/opensips/opensips.cfg`、Redis `SCRIPT EXISTS`、T S40 单通呼叫、B 按 Call-ID 查询 `raw_cdrs`/`rated_cdrs`。 - 验证结果:B API ready 返回 `ok`,HTTPS 首页返回 200 并加载 `assets/index-BBq8iMHt.js` 与 `assets/index-B2uYBtS3.css`;B preflight ok,最新备份目录显示 `/data/backups/mysql/20260629T013942Z` 与 `/data/backups/redis/20260629T013942Z`;Redis `cfg:active_version=s28-v1`,`SCRIPT EXISTS aff9995dbda2a2425e95a3e25b5d7ce194348ce7` 返回 `1`;A/B/T 目标服务最终均 active;S40 最小呼叫 `s40-01-1782701049649-q3u8wrqi@lisglosips-t` 收到 `200 OK` 与 `200 BYE`;B 新增 `raw_cdrs` 记录 `raw_4281ea422d1c4465964c6bf054a90115`,`rating_status=RATED`,生成 `rated_cdrs` 记录 `rated_b3e4b572d8f94fd6a0d6779a86a0294e`,`bill_sec=6`、客户费用 `0.012000`、供应商成本 `0.012000`。 - 回滚方式:本次未切换 release、未改数据库 schema、未删除数据。若需撤回本次运行态启动,可在 B 执行 `sudo systemctl stop lisglosips@recording-worker lisglosips@config-publisher`;B current 仍指向 `/opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000`。若后续需应用回滚,仍按 S30 Runbook 使用 `/opt/lisglosips/current/infra/server-b/s30/lisglosips-release-rollback.sh`。 - 未解决问题:S40 脚本内置 UAS 端口 `50620` 仍提示已占用,但现有 UAS 正常接起;A/T 各有一个无关 `fwupd-refresh.service` failed unit,未在本次任务中处理;真实 80 万手机号段尚未导入;浏览器逐页权限按钮和真实录音播放仍需用户人工复测。 ### 2026-06-28 22:34 - 需求修改/二期变更:提交并部署客户网关匹配规则 - 状态:已完成 - 操作服务器:A、B、T、本地 - 完成内容:提交客户网关匹配规则、测试用例和 release artifact 脚本;在 B 生成 Linux release artifact `s44-phase2-gateway-match-20260628225000.tar.gz`,SHA256 `f7c1264c5aaac62e35890d783641139aacb54821045521d6c669c41737e9d5e2`;B current 已切换到 `/opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000`;A 已替换 `/etc/opensips/lisglosips_hotpath.lua`,加载 Redis Lua SHA1 `aff9995dbda2a2425e95a3e25b5d7ce194348ce7` 并更新 `opensips.cfg`;T 服务保持 active 并完成最小呼叫。 - 修改文件:`IMPLEMENTATION_STATUS.md`、`SOFTSWITCH_PLATFORM_DESIGN_V2.md`、`docs/PHASE2_BUSINESS_PREFIX_GATEWAY_MIGRATION_RUNBOOK.md`、`docs/TEST_PLAN_AND_CASES.md`、`docs/TEST_RESULTS_STEP1_BASELINE_20260628.md`、`apps/api/src/modules/customer-gateways/customer-gateways.service.ts`、`apps/api/src/modules/customer-gateways/customer-gateways.e2e.spec.ts`、`apps/web/src/pages/CustomerGatewaysPage.jsx`、`apps/web/src/pages/BusinessPrefixesPage.jsx`、`apps/web/src/pages/UsersPage.jsx`、`apps/web/src/pages/RolesPage.jsx`、`infra/server-a/s28/lisglosips_hotpath.lua`、`scripts/build-release-artifact.mjs`、`package.json`、`vitest.config.ts`、Argon2id wasm 发布文件。 - Git 提交:`175166a fix phase2 gateway matching and release artifact`、`ef7874f fix release artifact packaging on linux`、`08c755f clean release artifact manifest status`。 - 执行的关键命令:本地 `eslint`、`tsc -b`、全量 `vitest run`、Web `vite build`、release artifact `--check`;B `pnpm build`、`pnpm release:artifact -- --release-id s44-phase2-gateway-match-20260628225000 --skip-build`、`sha256sum -c`、release 解包、服务重启、preflight;A 备份 Lua/cfg、计算 SHA1、`lisglosips-redis-hotpath-load.service`、`opensips -C -f /etc/opensips/opensips.cfg`、重启 OpenSIPS;T S40 单通呼叫。 - 验证结果:本地 lint/typecheck/全量测试通过,Vitest 25 文件 90 用例通过;B preflight ok,API ready ok,Nginx 首页引用 `index-BBq8iMHt.js`;A OpenSIPS `opensips -C` 通过且 active;T 呼叫 `s40-01-1782657231921-z1w146wc@lisglosips-t` 返回 200 OK/200 BYE;B `raw_cdrs` 新增 `raw_9b630cf678c24390bb27f3c3527f8051`,CDR worker 生成 `rated_29711c328edc44ecba8f4471bb6c8714`,客户费用和供应商成本均为 `0.012000`。 - 回滚方式:B 执行 `sudo /opt/lisglosips/current/infra/server-b/s30/lisglosips-release-rollback.sh /opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854`;A 恢复 `/var/backups/lisglosips-phase2/s44-20260628T225000/opensips.cfg` 与 `lisglosips_hotpath.lua`,执行 `opensips -C -f /etc/opensips/opensips.cfg` 后重启 OpenSIPS;MySQL/Redis 备份点为 `/data/backups/mysql/20260628T143121Z` 与 `/data/backups/redis/20260628T143122Z`。 - 未解决问题:未导入真实 80 万手机号段;S40 脚本自带 UAS 端口提示已占用但现有 UAS 正常接起;浏览器逐页权限按钮和真实录音播放仍需用户人工复测。 ### 2026-06-28 22:09 - 需求修改/二期变更:客户网关匹配口径固化与 release artifact 脚本 - 状态:已完成本地代码与文档修改,未发布服务器 - 操作服务器:本地 - 完成内容:将产品口径固化为“业务前缀优先于主叫前缀,精确规则优先于空业务前缀兜底”;明确业务前缀只作为客户网关匹配字典,不参与计费、报表或权限隔离;明确客户网关决定客户侧计费、落地网关决定供应商侧成本;后端客户网关保存/启用时校验同一 IP + 同一业务前缀唯一、同一 IP 主叫前缀不互相覆盖、同一网关内主叫前缀不互相覆盖;A 侧热路径仓库 Lua 改为业务前缀优先、空业务前缀下再按最长主叫前缀、最后默认兜底;客户网关编辑页新增匹配规则提示;新增固定 release artifact 脚本与 npm script。 - 修改文件:`SOFTSWITCH_PLATFORM_DESIGN_V2.md`、`docs/PHASE2_BUSINESS_PREFIX_GATEWAY_MIGRATION_RUNBOOK.md`、`apps/api/src/modules/customer-gateways/customer-gateways.service.ts`、`apps/api/src/modules/customer-gateways/customer-gateways.e2e.spec.ts`、`apps/web/src/pages/CustomerGatewaysPage.jsx`、`infra/server-a/s28/lisglosips_hotpath.lua`、`scripts/build-release-artifact.mjs`、`package.json`、`IMPLEMENTATION_STATUS.md` - 验证结果:`node_modules/.bin/tsc.cmd -b tsconfig.build.json --pretty false` 通过;`node_modules/.bin/eslint.cmd` 针对后端/脚本文件通过,Web 页面文件因仓库 ignore 规则被跳过且无错误;客户网关 e2e 7/7 通过,覆盖重复业务前缀和主叫前缀覆盖冲突;`node scripts/build-release-artifact.mjs --release-id s-local-check --check` 通过;`apps/web/node_modules/.bin/vite.CMD build` 通过,产物 `index-BBq8iMHt.js`。 - 回滚方式:本地回滚上述文件即可;若未来已发布服务器,B 应回滚到上一 release artifact,A 应恢复发布前备份的 `/etc/opensips/lisglosips_hotpath.lua` 并执行 `opensips -C -f /etc/opensips/opensips.cfg` 后重启 OpenSIPS。 - 未解决问题:未连接服务器、未生成正式 Linux release artifact、未部署 A/B、未做 A/B/T 实呼验收;本地无 Lua/luac,Lua 解释器级语法检查需在 A 或装有 Lua 的环境补跑。 ### 2026-06-28 16:45 - 修复用户管理/角色权限页面空白 - 状态:已完成 - 操作服务器:B - 完成内容: - 修复 `apps/web/src/pages/UsersPage.jsx` 中 `emptyUserForm` 未定义导致用户管理页面空白的问题。 - 修复 `apps/web/src/pages/RolesPage.jsx` 中 `emptyRoleForm` 未定义导致角色与权限页面空白的问题。 - 增加一次轻量源码检查,确认 `apps/web/src/pages` 下 `empty*Form` 引用均有同文件定义。 - 重新构建前端并发布到 B 当前 release `/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854` 的 `apps/web/dist` 与 `public` 目录。 - 修改文件:`apps/web/src/pages/UsersPage.jsx`、`apps/web/src/pages/RolesPage.jsx`、`IMPLEMENTATION_STATUS.md`;B 当前 release 静态资源目录。 - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`corepack pnpm@10.33.0 lint`、`corepack pnpm@10.33.0 typecheck`、`corepack pnpm@10.33.0 build`、页面 `empty*Form` 引用检查、上传并解包前端 dist、`curl -k https://127.0.0.1/`。 - 验证结果: - lint 通过。 - typecheck 通过。 - build 通过。 - 页面 `empty*Form` 引用检查无输出,未发现同类缺失定义。 - B HTTPS 首页已引用 `/assets/index-BK00zN_M.js` 与 `/assets/index-B2uYBtS3.css`。 - 回滚方式:恢复上述两个页面文件并重新构建发布;或将 B `public`/`apps/web/dist` 恢复为上一版前端静态资源。 - 未解决问题:未使用真实浏览器逐页点击复测;请用户强刷后重新进入“用户管理”“角色与权限”确认。 - 下一任务:等待用户复测反馈。 ### 2026-06-28 16:39 - 修复业务前缀页面空白 - 状态:已完成 - 操作服务器:B - 完成内容: - 修复 `apps/web/src/pages/BusinessPrefixesPage.jsx` 中 `emptyBusinessPrefixForm` 未定义导致业务前缀菜单页面空白的问题。 - 重新执行本地 lint、typecheck、build,生成新前端资产 `assets/index-CVzJDPkO.js`。 - 将 `apps/web/dist` 发布到 B 当前 release `/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854` 的 `apps/web/dist` 与 `public` 目录。 - 修改文件:`apps/web/src/pages/BusinessPrefixesPage.jsx`、`IMPLEMENTATION_STATUS.md`;B 当前 release 静态资源目录。 - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`corepack pnpm@10.33.0 lint`、`corepack pnpm@10.33.0 typecheck`、`corepack pnpm@10.33.0 build`、上传并解包前端 dist、`curl -k https://127.0.0.1/`。 - 验证结果: - lint 通过。 - typecheck 通过。 - build 通过。 - B HTTPS 首页已引用 `/assets/index-CVzJDPkO.js` 与 `/assets/index-B2uYBtS3.css`,不再引用旧 `index-BudcefOn.js`。 - 回滚方式:恢复 `BusinessPrefixesPage.jsx` 本次修改并重新构建发布;或将 B `public`/`apps/web/dist` 恢复为上一版前端静态资源。 - 未解决问题:未使用真实浏览器逐页点击复测;请用户刷新页面后重新进入“业务前缀管理”确认。 - 下一任务:等待用户复测反馈。 ### 2026-06-28 16:31 - 修复 B 登录 500:补齐 Argon2id wasm 发布文件 - 状态:已完成 - 操作服务器:B - 完成内容: - 根据 B `lisglosips@api` 日志定位 `/api/v2/auth/login` 500 根因:当前 `s43` release 缺少 `packages/auth/vendor/argon2id/dist/no-simd.wasm`,导致密码校验加载 wasm 失败。 - 从上一可用 release `/opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000` 复制 `no-simd.wasm` 与 `simd.wasm` 到当前 release `/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854`。 - 调整文件属主与权限为 `root:lisglosips`、`0640`,重启 `lisglosips@api`。 - 在 B 当前 release 内执行 Argon2id hash/verify 冒烟,返回 `argon2-smoke-ok`。 - 本地将 `packages/auth/vendor/argon2id/dist/no-simd.wasm` 与 `packages/auth/vendor/argon2id/dist/simd.wasm` 使用 `git add -f` 纳入 Git 跟踪,避免后续发布包再遗漏。 - 修改文件:B 当前 release 新增/补齐 `packages/auth/vendor/argon2id/dist/no-simd.wasm`、`packages/auth/vendor/argon2id/dist/simd.wasm`;本地暂存上述两个 wasm 文件,并更新 `IMPLEMENTATION_STATUS.md`。 - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`journalctl -u lisglosips@api`、`cp -a`、`chown`、`chmod`、`systemctl restart lisglosips@api`、`curl http://127.0.0.1:3000/api/v2/health/ready`、B Argon2id hash/verify 冒烟、`git add -f packages/auth/vendor/argon2id/dist/*.wasm`。 - 验证结果:B `lisglosips@api` active;API ready 返回 ok;Argon2id wasm 冒烟通过。用户可重新尝试 Web 登录;未使用或回显用户密码。 - 回滚方式:如需回滚本次运行修复,可删除 B 当前 release 的两个 wasm 文件并重启 API,但会恢复登录 500;推荐回滚方式仍是将 B `current` 指回 `/opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000` 后重启 API/Worker。 - 未解决问题:`refresh 401` 在无有效登录态时仍属预期;需用户重新登录确认前端流程。 - 下一任务:等待用户复测反馈。 ### 2026-06-28 16:25 - 启动 A/B/T 本地联调环境供功能检查 - 状态:已完成 - 操作服务器:A、B、T - 完成内容: - A:确认 `opensips`、`lisglosips-redis-auth-proxy`、`rtpengine`、`rtpengine-recording-daemon`、`lisglosips-node-exporter` 均为 active;未修改 A 配置。 - B:确认当前 release 为 `/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854`;启动此前 inactive 的 `lisglosips@recording-worker` 与 `lisglosips@config-publisher`;确认 MySQL、Redis、Nginx、API、CDR Worker、Recording Worker、Config Publisher、HEP、Prometheus、Grafana 均为 active。 - T:发现 `opensips` 为 failed;执行 `systemctl reset-failed opensips` 与 `systemctl restart opensips` 后恢复 active;确认 `rtpengine`、`apache2`、`mariadb`、`lisglosips-s28-uas` 均为 active。 - 修改文件:本地更新 `IMPLEMENTATION_STATUS.md`;服务器未修改配置文件,未执行数据库 migration,未改防火墙。 - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`systemctl is-active`、B `systemctl start lisglosips@recording-worker lisglosips@config-publisher`、T `systemctl reset-failed opensips && systemctl restart opensips`、B `lisglosips-release-preflight.sh`、`curl http://127.0.0.1:3000/api/v2/health/ready`、`curl -k https://127.0.0.1/`、`ss -lunpt`。 - 验证结果: - A OpenSIPS 配置 SHA-256 仍为 `ae1544e9a39d2db114e6981babac042c2ab6d399d59ba597a52b85d597aa5aa5`,Lua SHA-1 仍为 `0b7ee595cdcad2f38a670dd8ff3553387820039e`;A 监听 `100.90.90.90:15060/udp`。 - B preflight 通过;API ready 返回 ok;HTTPS 首页返回 200,加载 `/assets/index-BudcefOn.js` 与 `/assets/index-B2uYBtS3.css`。 - T `opensips` active,监听 `100.93.185.30:5060/udp`;UAS 监听 `100.93.185.30:50620/udp`。 - 回滚方式:如需停止本次启动项,可停止 B 的 `lisglosips@recording-worker`、`lisglosips@config-publisher`,以及按需停止 T `opensips`;B 应用回滚仍可将 `/opt/lisglosips/current` 指回 `/opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000` 后重启服务。 - 未解决问题:未执行真实呼叫;T OpenSIPS 曾处于 failed,已恢复但未进一步分析失败前原因。 - 下一任务:等待用户功能检查反馈。 ### 2026-06-24 18:58 - 需求修改/二期变更:Bug 修复发布到 B - 状态:已完成 - 操作服务器:B;A 只读核对;T 未操作 - 完成内容: - 已将本地 Git 提交 `a86de65 feat: complete phase2 baseline cdr quality rbac` 构建并发布到 B 新 release:`/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854`。 - 发布内容包含:CDR 后端/API 前端接线、质检中心真实 API、前端页面拆分、权限驱动导航和按钮裁剪。 - 切换前 B 当前 release 为 `/opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000`,已作为直接回滚点保留。 - 切换前执行 B preflight 通过,并生成 MySQL/Redis 备份:`/data/backups/mysql/20260624T104812Z`、`/data/backups/redis/20260624T104812Z`,SHA256 校验通过。 - 首次切换 `s43` 时发现 release 组装缺少 workspace 级 `apps/*/node_modules` 和部分 `packages/*/node_modules` 链接,服务报 `ERR_MODULE_NOT_FOUND`;已立即回滚到 `s42`,确认 API ready 200;随后补齐链接、完成模块解析冒烟后再次切换到 `s43` 成功。 - A 侧只读核对:运行中 OpenSIPS 配置 SHA-256 仍为 `ae1544e9a39d2db114e6981babac042c2ab6d399d59ba597a52b85d597aa5aa5`,Lua SHA-1 仍为 `0b7ee595cdcad2f38a670dd8ff3553387820039e`;本次 Bug 修复不需要发布 A,且仓库 `infra/server-a/s28/opensips.cfg` 含部署模板占位符 `@@LISGLOSIPS_REDIS_URL@@`,不能直接覆盖运行中 A 配置。 - 修改文件:本地更新 `IMPLEMENTATION_STATUS.md`;服务器新增 B release 目录 `/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854`,未修改数据库 schema,未执行 Prisma migration。 - 安装软件及固定版本:未安装新软件;复用 B 既有 Node.js `v22.22.2`、pnpm 依赖和 systemd 模板。 - 执行的关键命令:本地 `corepack pnpm@10.33.0 build`;B `lisglosips-release-preflight.sh`、`systemctl start lisglosips-backup.service`、`sha256sum -c`、创建 release、切换 `/opt/lisglosips/current`、重启 `lisglosips@api`、`lisglosips@cdr-worker`、`lisglosips@recording-worker`、`lisglosips@config-publisher`、`nginx -t`、`systemctl reload nginx`、`curl http://127.0.0.1:3000/api/v2/health/ready`、`curl -k https://127.0.0.1/`。 - 验证结果: - 本地 build 通过。 - B 当前 release 为 `/opt/lisglosips/releases/s43-bugfix-cdr-quality-rbac-20260624184854`。 - B `mysql`、`redis-server`、`nginx`、`lisglosips@api`、`lisglosips@cdr-worker`、`lisglosips@recording-worker`、`lisglosips@config-publisher` 均为 active。 - B API ready 返回 `{"status":"ok","service":"api",...}`。 - B preflight 通过;HTTPS 首页返回 200,并加载新前端资产 `/assets/index-BudcefOn.js` 与 `/assets/index-B2uYBtS3.css`。 - A OpenSIPS/Lua 哈希与冻结报告一致,未执行 A 侧写操作或重启。 - 回滚方式:在 B 执行 `sudo ln -sfn /opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000 /opt/lisglosips/current`,然后重启 `lisglosips@api lisglosips@cdr-worker lisglosips@recording-worker lisglosips@config-publisher` 并执行 preflight;本次未做数据库迁移,通常不需要数据库回滚。 - 未解决问题:未执行 A/B/T 实呼验收;用户需在 B Web 登录态逐页复测权限按钮、CDR、质检、录音播放和信令入口;真实录音文件播放与 Nginx X-Accel 链路仍需用实际文件复核。 - 下一任务:等待用户测试反馈或指定下一项。 ### 2026-06-24 17:50 - 需求修改/二期变更:权限驱动导航和按钮 - 状态:已完成 - 操作服务器:本地 - 完成内容: - 后端登录/刷新响应新增 `user.permissions`,从用户启用角色的 `role_permissions` 汇总去重并排序后返回;保留原 `roles` 字段。 - 前端新增 `apps/web/src/permissions.js`,提供页面权限表、`permissionSet`、`can`、`canAll`、`canAny`。 - `App.jsx` 根据权限裁剪菜单;启动和手动刷新时只请求当前用户有权限的全局 API,避免无权限接口产生批量 403;无权限页面显示明确“无权限访问”提示。 - 核心页面按钮按后端真实权限 key 裁剪:当前通话挂断、客户/供应商管理、充值、客户网关、业务前缀、落地网关、落地线路组、号码库导入、话单录音播放、质检录音播放/评分/抽检规则、用户管理、角色管理。 - 修正角色权限页 dev fixture 的权限 key,使其与后端 `RequirePermissions` 使用的真实 key 保持一致。 - 修改文件:`apps/api/src/modules/auth/auth.types.ts`、`apps/api/src/modules/auth/auth.repository.ts`、`apps/api/src/modules/auth/auth.service.ts`、`apps/api/src/modules/auth/auth.service.spec.ts`、`apps/api/src/modules/auth/auth.e2e.spec.ts`、`apps/web/src/permissions.js`、`apps/web/src/App.jsx`、`apps/web/src/fixtures/devFixtures.js`、`apps/web/src/pages/*.jsx`、`IMPLEMENTATION_STATUS.md` - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`corepack pnpm@10.33.0 typecheck`、`corepack pnpm@10.33.0 lint`、`corepack pnpm@10.33.0 build`、`corepack pnpm@10.33.0 exec vitest run apps/api/src/modules/auth/auth.service.spec.ts apps/api/src/modules/auth/auth.e2e.spec.ts --hookTimeout=60000 --maxWorkers=1`、`corepack pnpm@10.33.0 exec vitest run --hookTimeout=60000 --maxWorkers=1`、`corepack pnpm@10.33.0 --filter @lisglosips/web dev -- --host 127.0.0.1` - 验证结果: - typecheck/lint/build 均通过。 - Auth 定向测试 2 files / 6 tests 通过,确认 login/refresh 均返回 permissions。 - 全量 Vitest 25 files / 88 tests 通过。 - 使用本地临时 mock API 与 in-app Browser 验证只读账号仅含 `dashboard.view/customers.view`:侧边栏只显示“概览 Dashboard”“客户管理”;“供应商管理”“质检中心”等菜单不可见;客户页可查看客户列表,但无“新增客户”“编辑”“充值”“删除”按钮;无 Vite overlay,无应用 console error/warn。 - 回滚方式:回滚上述后端 auth 文件、前端权限/页面文件和本状态文件即可;本次未修改 Prisma schema、未执行数据库迁移、未连服务器、未重启服务。 - 未解决问题:当前按钮级裁剪覆盖真实 API 核心页面;费率与计费、SIP 运维、监控告警、系统设置仍属于待设计/演示页面,后续接真实 API 时需继续补权限 key 和按钮裁剪。真实 B 登录态逐页按钮仍需后续人工复测。 - 下一任务:等待用户指定。 ### 2026-06-24 17:30 - 需求修改/二期变更:前端拆分与信息架构整理 - 状态:已完成 - 操作服务器:本地 - 完成内容: - 将 `App.jsx` 中的页面组件拆到 `apps/web/src/pages/`:`CdrPage.jsx`、`QualityPage.jsx`、`ActiveCallsPage.jsx`、`CustomersPage.jsx`、`CustomerGatewaysPage.jsx`、`VendorsPage.jsx`、`VendorGatewaysPage.jsx`、`VendorLineGroupsPage.jsx`、`BusinessPrefixesPage.jsx`、`NumberLibraryPage.jsx`、`RechargeRecordsPage.jsx`、`RoutesPage.jsx`、`BillingPage.jsx`、`SipOpsPage.jsx`、`MonitoringPage.jsx`、`SettingsPage.jsx`、`UsersPage.jsx`、`RolesPage.jsx`、`OperationLogsPage.jsx`。 - 将页面共享布局组件拆到 `apps/web/src/components/layout.jsx`,包含 `Icon`、`PageTitle`、`Panel`、`ApiNotice`、`Drawer`、`Modal`、`SimpleTable`、`KeyValue` 等。 - 将格式化和 API 归一化函数拆到 `apps/web/src/utils/formatters.js`,供 App 数据刷新和页面组件复用。 - 将残留静态演示/mock 数据集中到 `apps/web/src/fixtures/devFixtures.js`,避免继续散落在 `App.jsx`;未把这些旧 fixture 接入生产 API 流程。 - `App.jsx` 保留应用入口、登录、导航、全局 API 刷新与页面 props 分发,体积从约 237 KB 降到约 20 KB。 - 修改文件:`apps/web/src/App.jsx`、`apps/web/src/components/layout.jsx`、`apps/web/src/utils/formatters.js`、`apps/web/src/fixtures/devFixtures.js`、`apps/web/src/pages/*.jsx`、`IMPLEMENTATION_STATUS.md` - 安装软件及固定版本:未安装新软件。 - 执行的关键命令:`corepack pnpm@10.33.0 lint`、`corepack pnpm@10.33.0 typecheck`、`corepack pnpm@10.33.0 build`、`corepack pnpm@10.33.0 exec vitest run --hookTimeout=60000 --maxWorkers=1`、`corepack pnpm@10.33.0 --filter @lisglosips/web dev -- --host 127.0.0.1` - 验证结果: - lint/typecheck/build 均通过。 - 全量 Vitest 25 files / 88 tests 通过。 - 使用本地临时 mock API 与 in-app Browser 验证 `http://127.0.0.1:5173/`:登录后可切换“当前通话”“话单中心”“质检中心”“客户管理”,对应页面 `h1` 正确、root 正常挂载、无 Vite overlay、无应用 console error/warn。 - 拆分过程中发现并修复两个运行时拆分问题:`App.jsx` 主壳补回 `Badge` 导入;`emptySamplingRuleForm` 从 `CdrPage.jsx` 挪回 `QualityPage.jsx`。 - 回滚方式:回滚上述前端拆分文件和本状态文件即可;本次未修改 Prisma schema、未执行数据库迁移、未连服务器、未重启服务。 - 未解决问题:`fixtures/devFixtures.js` 仍承载费率、路由、监控、系统设置等未接真实 API 的旧演示数据;后续应按页面逐步接真实 API 或加 dev-only 开关彻底隔离。真实 B 服务器浏览器登录态逐页按钮仍需后续人工复测。 - 下一任务:等待用户指定。 ### 2026-06-24 17:10 - 需求修改/二期变更:质检中心接真实 API - 状态:已完成 - 操作服务器:本地 - 完成内容: - 录音列表从静态数据切换为 `api.recordings()`,接入 `GET /recordings`,支持待质检/已质检/全部筛选、条数筛选、加载中、错误提示与空状态。 - 录音详情接入 `GET /recordings/:id`,详情抽屉展示客户、被叫、业务、录音时长、抽样状态、命中规则与既有质检结果。 - 录音播放接入 `GET /recordings/:id/play`,以 blob URL 驱动页面内 `