247 KiB
247 KiB
LisgloSIPS V2 实施状态
本文件用于跨 Codex 会话持续交接。每次新会话开始必须先读取本文件和
SOFTSWITCH_PLATFORM_DESIGN_V2.md;每次任务完成、失败或被阻塞后必须更新本文件。
当前状态
- 当前任务:需求修改/二期变更:供应商启停移除、客户启停入口、落地网关 CPS 数字校验、当前通话 SIP 状态
- 本次变更摘要:供应商启停移除、客户启停入口、落地网关 CPS 数字校验、当前通话 SIP 状态,以及登录 500 的完整 Argon2id vendor artifact 修复,已提交并发布到 B 新 release
s45-vendor-cps-sipstate-20260629113500。 - 总体状态:S30 已完成;本地 KVM 开发环境 V2 闭环原冻结 release 为
s28-v2-20260621220924;B 二期冻结基线为s42-phase2-business-prefix-20260624140000,当前 release 为s45-vendor-cps-sipstate-20260629113500;B preflight 通过,API ready 返回 ok,HTTPS 首页加载前端资产index-CBRZZA7t.js/index-B2uYBtS3.css;Rediscfg:active_version=s28-v1,A 当前 EVALSHA 脚本在 Redis 中存在;A 已加载二期 Lua 热路径 SHA1aff9995dbda2a2425e95a3e25b5d7ce194348ce7,OpenSIPS 配置 SHA-256 为f163fa37f7aaa492e12ed496cb82f07d9b0f1030a6ed297855a22d2c4bf1c8a5;A/B/T 目标服务均 active;B 登录依赖 Argon2id 完整 vendor 目录已纳入 release artifact;当前仍未导入真实 80 万手机号段;阿里云迁移前需按 S30 Runbook 重新演练 - 最后更新:2026-06-29 12:12 +08:00
- 当前阻塞:无阻塞。遗留问题:本次未执行数据库 schema 变更,未改 A OpenSIPS/Lua/RTPEngine 或 T 配置;真实 80 万手机号段尚未导入;OpenSIPS dialog 残留根因仍待单独分析;真实 B 服务器浏览器登录态逐页按钮、真实录音文件播放和 Nginx/X-Accel 链路仍需用户测试复核;S40 呼叫时脚本内置 UAS 端口提示已占用但现有 UAS 正常接起且通话/CDR/计费成功;A/T 各有一个无关
fwupd-refresh.servicefailed 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 发送:
请先读取 SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md,
以及本任务关联的现有脚本/代码。只执行状态文件里的当前任务,不提前执行后续任务。
开始前复核上次交接记录;涉及服务器写操作、服务重启、防火墙、密码、数据库迁移或数据删除时先说明影响和回滚点。
完成后运行验收检查,并更新 IMPLEMENTATION_STATUS.md 的任务状态、产物、验证结果、遗留问题和下一任务。
单任务交接记录模板
每完成一个任务,在本节顶部新增记录:
### YYYY-MM-DD HH:mm - Sxx 任务名称
- 状态:已完成 / 已阻塞
- 操作服务器:A / B / T / 本地
- 完成内容:
- 修改文件:
- 安装软件及固定版本:
- 执行的关键命令:只记录无密码版本
- 验证结果:
- 回滚方式:
- 未解决问题:
- 下一任务:Sxx
交接记录
2026-06-29 12:12 - 需求修改/二期变更发布到 B:供应商启停移除、客户启停、CPS 与当前通话 SIP 状态
- 状态:已完成
- 操作服务器:B,本地;A/T 只读服务复核
- 完成内容:将本轮供应商启停移除、客户启停按钮、落地网关 CPS 1-10000 数字校验、当前通话 SIP 信令状态展示,以及登录 500 的完整 Argon2id vendor artifact 修复一起发布为 B 新 release
s45-vendor-cps-sipstate-20260629113500;B/opt/lisglosips/current已从s44-phase2-gateway-match-20260628225000切换到新 release;重启lisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker、lisglosips@config-publisher,Nginx 配置检查通过并 reload。 - 修改文件:
IMPLEMENTATION_STATUS.md - Release artifact:B 上 Linux 构建并生成
/tmp/lisglosips-build-s45-vendor-cps-sipstate-20260629113500/dist/releases/s45-vendor-cps-sipstate-20260629113500.tar.gz,SHA25687f1372b726a6c0a3a83c68b0ada2559a3f92be35d92451551a24ad1e25d9663,staged files19673;Web 产物为index-CBRZZA7t.js/index-B2uYBtS3.css。 - 执行的关键命令:本地
git commit;本地git archive并上传源码 tar;B 从 s44 current 复用 Linuxnode_modules构建,执行corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 release:artifact -- --release-id s45-vendor-cps-sipstate-20260629113500 --skip-build、sha256sum、systemctl start lisglosips-backup.service、解包 release、切换/opt/lisglosips/current、重启 API/Worker、nginx -t、reload Nginx、lisglosips-release-preflight.sh、API ready 和 HTTPS 首页检查;A/T 只读systemctl is-active复核。 - 验证结果:B MySQL、Redis、Nginx、API、CDR Worker、Recording Worker、Config Publisher 均 active;preflight ok;API ready 返回
ok,database/redis/config checks 均ok;HTTPS 首页返回 200 并引用新 Web 资产index-CBRZZA7t.js、index-B2uYBtS3.css;发布前备份目录为/data/backups/mysql/20260629T041103Z和/data/backups/redis/20260629T041103Z;Aopensips、rtpengine、rtpengine-recording-daemon、lisglosips-redis-auth-proxyactive;Topensips、rtpengine、lisglosips-s28-uasactive。 - 回滚方式:B 可执行
/opt/lisglosips/current/infra/server-b/s30/lisglosips-release-rollback.sh /opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000或手动将/opt/lisglosips/current指回该 release 后重启lisglosips@api lisglosips@cdr-worker lisglosips@recording-worker lisglosips@config-publisher并 reload Nginx;本次未做数据库 schema 变更,通常不需要数据库回滚,必要时使用本次发布前 MySQL/Redis 备份先在隔离环境验证。 - 未解决问题:未执行新的 A/B/T 实呼验收,因本次只发布 Web/API/Worker 业务管理变更且不改 OpenSIPS 热路径;浏览器逐页按钮、登录态和录音播放仍需用户在网页验收。
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/login500 根因:当前 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 建立边界;先只读检查三机状态;确认 Aopensips、rtpengine、rtpengine-recording-daemon、lisglosips-redis-auth-proxy、lisglosips-node-exporteractive,lisglosips-redis-hotpath-load为 oneshot 且上次结果 success;确认 A Lua SHA1aff9995dbda2a2425e95a3e25b5d7ce194348ce7与opensips.cfgEVALSHA 一致,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;确认 Topensips、rtpengine、apache2、mariadb、lisglosips-s28-uasactive,S28/S40 脚本存在可执行。 - 修改文件:
IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
systemctl is-active、systemctl --failed、readlink -f /opt/lisglosips/current、Bsystemctl start lisglosips@recording-worker lisglosips@config-publisher、Blisglosips-release-preflight.sh、Bcurl http://127.0.0.1:3000/api/v2/health/ready、Bcurl -k https://127.0.0.1/、A/Topensips -C -f /etc/opensips/opensips.cfg、RedisSCRIPT 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;Rediscfg: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.servicefailed unit,未在本次任务中处理;真实 80 万手机号段尚未导入;浏览器逐页权限按钮和真实录音播放仍需用户人工复测。
2026-06-28 22:34 - 需求修改/二期变更:提交并部署客户网关匹配规则
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:提交客户网关匹配规则、测试用例和 release artifact 脚本;在 B 生成 Linux release artifact
s44-phase2-gateway-match-20260628225000.tar.gz,SHA256f7c1264c5aaac62e35890d783641139aacb54821045521d6c669c41737e9d5e2;B current 已切换到/opt/lisglosips/releases/s44-phase2-gateway-match-20260628225000;A 已替换/etc/opensips/lisglosips_hotpath.lua,加载 Redis Lua SHA1aff9995dbda2a2425e95a3e25b5d7ce194348ce7并更新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、Webvite build、release artifact--check;Bpnpm 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 OpenSIPSopensips -C通过且 active;T 呼叫s40-01-1782657231921-z1w146wc@lisglosips-t返回 200 OK/200 BYE;Braw_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本次修改并重新构建发布;或将 Bpublic/apps/web/dist恢复为上一版前端静态资源。 - 未解决问题:未使用真实浏览器逐页点击复测;请用户刷新页面后重新进入“业务前缀管理”确认。
- 下一任务:等待用户复测反馈。
2026-06-28 16:31 - 修复 B 登录 500:补齐 Argon2id wasm 发布文件
- 状态:已完成
- 操作服务器:B
- 完成内容:
- 根据 B
lisglosips@api日志定位/api/v2/auth/login500 根因:当前s43release 缺少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
- 修改文件: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@apiactive;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。
- A:确认
- 修改文件:本地更新
IMPLEMENTATION_STATUS.md;服务器未修改配置文件,未执行数据库 migration,未改防火墙。 - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
systemctl is-active、Bsystemctl start lisglosips@recording-worker lisglosips@config-publisher、Tsystemctl reset-failed opensips && systemctl restart opensips、Blisglosips-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
opensipsactive,监听100.93.185.30:5060/udp;UAS 监听100.93.185.30:50620/udp。
- A OpenSIPS 配置 SHA-256 仍为
- 回滚方式:如需停止本次启动项,可停止 B 的
lisglosips@recording-worker、lisglosips@config-publisher,以及按需停止 Topensips;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 配置。
- 已将本地 Git 提交
- 修改文件:本地更新
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;Blisglosips-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 驱动页面内<audio controls>,支持播放/暂停、进度、播放失败提示和自动播放下一条。 - 质检评分保存接入
PUT /recordings/:id/review,提交结果、评分、问题标签和备注;保存后刷新录音列表与当前详情,避免“看似保存但未刷新”。 - 抽检规则接入
/quality/rules,支持新增、编辑、启用、禁用、删除;规则表单使用真实客户与落地线路组选项,并支持全部客户/全部线路。
- 录音列表从静态数据切换为
- 修改文件:
apps/web/src/App.jsx、apps/web/src/api.js、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 验证质检中心主流程:进入质检中心、打开录音详情、读取
/recordings/:id/playblob、填写标签/备注/评分并保存,页面出现保存反馈且刷新详情与列表;无应用 console error/warn。 - 定向验证录音播放:详情抽屉内
<audio>存在,src为 blob URL,抽样状态与质检控件可见。 - 定向验证抽检规则 CRUD:新增
QA 保留验证规则成功,禁用后状态刷新为禁用,删除后列表移除;无应用 console error/warn。
- 回滚方式:回滚上述 2 个前端文件和本状态文件即可;本次未修改 Prisma schema、未执行数据库迁移、未连服务器、未重启服务。
- 未解决问题:真实 B 服务器浏览器登录态逐页按钮仍需后续人工复测;真实录音文件的播放、Range/Nginx X-Accel 链路仍需在 B 上用实际文件复测;话单信令入口仍是 CDR payload/Call-ID 索引展示,未接独立 HEP/SIP trace 检索接口;CSV 导出仍未实现。
- 下一任务:等待用户指定。
2026-06-24 16:55 - 需求修改/二期变更:话单中心前端接真实 API
- 状态:已完成
- 操作服务器:本地
- 完成内容:
- 替换话单中心静态
cdrs/ratedCdrs数据,页面改为调用api.cdrs()查询真实列表,并调用api.cdrDetail()按需读取详情。 - 新增话单查询条件:主叫、被叫、客户网关、落地网关、地级市代码、运营商、开始时间、结束时间、每页条数;查询时重置到第一页。
- 新增分页状态:展示当前范围、总数、上一页、下一页,并使用后端
meta.total/take/skip/hasMore。 - 详情抽屉改为真实契约展示,包含链路信息、关联对象、计费结果、时间轴、录音与信令;信令入口展示 Call-ID、eventId、网关、配置版本和 payload 索引。
- 新增录音播放接线:
api.recordingPlayback()拉取/recordings/:id/playblob,详情抽屉内使用<audio controls>播放;无录音或读取失败时给出禁用/错误状态。 - 保留导出 CSV 入口但禁用,避免误导为已实现导出。
- 替换话单中心静态
- 修改文件:
apps/web/src/App.jsx、apps/web/src/api.js、apps/web/src/styles.css、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 typecheck、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 通过。
- in-app Browser 验证
http://127.0.0.1:5173/应用壳可加载,标题为LisgloSIPS - 聆界SIP管理平台,无 Vite overlay,无 console error/warn。 - 使用本地临时 mock API 验证登录后话单中心:查询主叫
1001后列表过滤为 1 条;详情抽屉打开成功;计费结果、信令索引、录音<audio>控件可见;查询不存在主叫999999后出现空状态且总数为 0;console error/warn 为 0。
- 回滚方式:回滚上述 3 个前端文件和本状态文件即可;本次未修改 Prisma schema、未执行数据库迁移、未连服务器、未重启服务。
- 未解决问题:真实 B 服务器浏览器登录态逐页按钮仍需后续人工复测;信令入口当前展示 CDR payload/Call-ID 索引,未接独立 HEP/SIP trace 检索接口;CSV 导出仍未实现。
- 下一任务:等待用户指定。
2026-06-24 16:28 - 需求修改/二期变更:CDR/话单后端 API 补强
- 状态:已完成
- 操作服务器:本地
- 完成内容:
- 在现有
CdrsModule上补强话单后端能力,保持路由GET /cdrs与GET /cdrs/:id不变。 - 列表筛选新增
startedFrom/startedTo,并兼容from/to、startTime/endTime;保留主叫、被叫、客户网关、落地网关、归属地城市、运营商、take/skip分页。 - 列表返回新增
meta: { total, take, skip, hasMore },并补充业务前缀、原始被叫、落地主被叫、录音摘要字段。 - 详情返回整理为前端可直接展示的稳定契约,关联 raw CDR、rated CDR、recording、customer、vendor、customer gateway、vendor gateway、line group、business prefix、customer gateway policy,并保留 payload、receivedAt、createdAt。
- 在现有
- 修改文件:
apps/api/src/modules/cdrs/cdrs.repository.ts、apps/api/src/modules/cdrs/cdrs.service.ts、apps/api/src/modules/cdrs/cdrs.service.spec.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
corepack pnpm@10.33.0 exec vitest run apps/api/src/modules/cdrs/cdrs.service.spec.ts --hookTimeout=60000、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 --hookTimeout=60000 --maxWorkers=1 - 验证结果:CDR service spec 3 tests 通过;typecheck/lint/build 均通过;全量 Vitest 25 files / 88 tests 通过。
- 回滚方式:回滚上述 3 个 CDR 模块文件和本状态文件即可;本次未修改 Prisma schema、未执行数据库迁移、未连服务器、未重启服务。
- 未解决问题:前端话单列表/详情页尚未接入新契约;如未来要按客户、供应商、落地线路组、SIP 状态码等更多条件筛选,可在当前
CdrQuery上继续扩展。 - 下一任务:等待用户指定。
2026-06-24 16:16 - 需求修改/二期变更:S42 二期业务前缀基线冻结
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:基于当前文档、代码和服务器状态对
s42-phase2-business-prefix-20260624140000执行冻结前收口。补齐 B 当前 release 中infra/server-b/s30/lisglosips-release-preflight.sh执行权限为0755,直接执行 preflight 通过;确认 B current、API/CDR/Recording/Config Publisher/Nginx/MySQL/Redis/HEP/Prometheus/Grafana active,API ready 正常,Prisma 4 条 migration up to date,Redis active/previous 配置版本为1782286812696/1782286145681,最新 MySQL/Redis 备份/data/backups/*/20260624T055810ZSHA256 校验通过。抽样确认 Redisstream:cdr_deadletter26 条为早期修复前历史死信,冻结验收呼叫未新增 deadletter。A 侧历史 4 条 dialog 使用dlg_end_dlg仍返回Operation failed且未消失,已先执行opensips -C再重启 OpenSIPS 清空;冻结验收实呼后新增 1 条测试 dialog 残留,再次语法检查并重启 OpenSIPS,最终dlg_list为空。新增正式冻结报告docs/S42_PHASE2_FREEZE_REPORT.md。 - 修改文件:
docs/S42_PHASE2_FREEZE_REPORT.md、IMPLEMENTATION_STATUS.md;B 当前 release 脚本权限已修正为0755。 - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:本地
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 --hookTimeout=60000 --maxWorkers=1;Blisglosips-release-preflight.sh、Prisma migrate status、RedisMGET cfg:active_version cfg:previous_version、备份sha256sum -c SHA256SUMS、deadletter 抽样;Aopensips -C -f /etc/opensips/opensips.cfg、systemctl restart opensips、lisglosips-call-control dlg_list;Tpython3 /opt/lisglosips-s28/lisglosips-s28-sip.py invite --host 100.90.90.90 --port 15060 --bind-host 100.93.185.30 --contact-host 100.93.185.30 --caller s28-ip-1001 --callee 13800138000 --hold 3 --timeout 20。 - 验证结果:本地 typecheck/lint/build 均通过;全量 Vitest 在
--hookTimeout=60000下 25 个测试文件、87 条测试通过;B preflight 直接执行通过,API ready 正常,Prisma up to date,最新 MySQL/Redis 备份哈希通过;T 冻结验收实呼 Call-IDs28-1782288889943-ckv83lce@lisglosips-t收到100 Giving it a try、200 OK、BYE200 OK;B raw CDRraw_d5732df32b8d496686512870fb087232入库,caller=s28-ip-1001、callee=13800138000、raw_callee=13800138000、business_prefix=NULL、landing_caller=s28-ip-1001、landing_callee=13800138000、sip_code=200、rating_status=RATED,客户费用和供应商成本均为0.012000;CDR Worker 日志processed=1 deadlettered=0;A 最终dlg_list为空。 - 回滚方式:B 应用可执行
/opt/lisglosips/current/infra/server-b/s30/lisglosips-release-rollback.sh /opt/lisglosips/releases/s33-active-calls-20260623094000;Redis 可从当前1782286812696切回上一版本1782286145681,不删除 Stream/deadletter;A 可从最近/var/backups/lisglosips-phase2/备份恢复opensips.cfg与lisglosips_hotpath.lua,先opensips -C再重启;数据库如必须恢复,使用/data/backups/mysql/20260624T055810Z先在隔离库验证后进入维护窗口处理。 - 未解决问题:真实 80 万手机号段尚未导入;浏览器登录态逐页按钮未做完整人工复测;OpenSIPS dialog 在测试呼叫结束后可能残留,冻结前已通过重启清空,后续应单独分析 dialog 生命周期和
dlg_end_dlg Operation failed根因;当前仍为本地 KVM/Tailscale 冻结,不进入阿里云生产迁移。
2026-06-24 15:42 - 需求修改/二期变更:业务前缀重构第 10 步 A/B/T 端到端验收
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:复核 B 当前 release
/opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000,A OpenSIPS 与 Redis AUTH 代理 active,热路径 Lua SHA0b7ee595cdcad2f38a670dd8ff3553387820039e,T UAS100.93.185.30:50620监听正常。临时写入 Step 10 验收数据:业务前缀671、S28 客户网关业务前缀匹配、北京/移动号码段、S28 第一落地网关屏蔽北京、同线路组第二落地网关、落地被叫前缀88、指定主叫02160010001。发布临时配置版本1782286145681后从 T 发起组合实呼s28-1782286191063-ig215mpx@lisglosips-t,A 热路径选择第二落地网关vgw_91379ef9dc254ab29039d4b77197,B CDR 入库raw_callee=67113800138000、callee=13800138000、business_prefix=671、callee_city_code=110100、callee_operator=MOBILE、landing_caller=02160010001、landing_callee=8813800138000、rating_status=RATED。随后通过/api/v2/active-calls验证当前通话列表包含callerIp与landingIp,并通过/api/v2/active-calls/:id/hangup对bae.39c14471发起强制挂断,返回HANGUP_IN_PROGRESS且目标进入 state5。 - 修改文件:
IMPLEMENTATION_STATUS.md - 验证结果:B
lisglosips@api、lisglosips@cdr-worker、lisglosips@config-publisheractive;Aopensips、lisglosips-redis-auth-proxyactive;T UAS 监听正常;组合实呼 SIP100/200/BYE 200;A OpenSIPS 日志记录热路径 allow 与 CDR XADD;B CDR Workerprocessed=1 deadlettered=0;当前通话 API 返回 6 条 dialog 并包含呼叫方 IP/落地 IP;强制挂断 API 返回HANGUP_IN_PROGRESS。 - 回滚方式:已执行回滚 SQL:S28 客户网关
callee_match_mode恢复ANY,删除临时客户网关业务前缀绑定、临时线路组成员、屏蔽地区、指定主叫池、号码段和运营商规则;第二落地网关landing_callee_prefix恢复NULL;临时业务前缀bp_p2e2e_671软删除以保留 CDR 引用;回滚配置已发布为1782286812696,manifest 回到customerGatewayBusinessPrefixCount=0、callerRewriteCount=0、phoneSegmentCount=0、carrierPrefixRuleCount=0、blockedRegionCount=0。 - 未解决问题:A
dlg_list仍有历史测试 dialog 残留,包括旧 S28 呼叫;强制挂断 OpenSIPS MI 对部分 dialog 仍可能返回Operation failed但会推进终止态;本次验证使用 API 级当前通话/挂断,未在浏览器登录态逐页点击 Web 按钮;真实 80 万手机号段尚未导入。
2026-06-24 15:00 - 需求修改/二期变更:业务前缀重构第 9 步 A/B/T 验收续跑与 CDR 修复
- 状态:已完成第 9 步主链路验收;仍有历史 dialog 清理和 Web 登录态页面按钮复测遗留
- 操作服务器:A、B、T、本地
- 完成内容:按用户要求先 ping B,
100.90.90.914 次 ping 0% 丢包,SSH 可达,B current 为s42-phase2-business-prefix-20260624140000,API/CDR/Recording/Config Publisher 均 active。重新上传 A/B 候选 Lua/配置,发现直接redis-cli SCRIPT LOAD "$(cat file)"会丢失末尾换行导致 SHA 为e1a853...,已改用redis-cli -x SCRIPT LOAD < file,Redis 中确认SCRIPT EXISTS 0b7ee595cdcad2f38a670dd8ff3553387820039e返回 1。A 侧首次重启时误将 B 本机REDIS_URL写入 OpenSIPS,导致 cachedb 连接127.0.0.1:6379失败;根据 S20 Runbook 修正为 OpenSIPS cachedb URLredis:s20://127.0.0.1:6380/,通过 A 本地lisglosips-redis-auth-proxy连接 B Redis,重新opensips -C并重启 OpenSIPS,日志显示连接127.0.0.1:6380,无新增 Redis connection refused。T 发起 S28 实呼s28-1782283559224-yo6iqlqg@lisglosips-t成功,但 B CDR Worker 将事件投 deadletter;定位为二期热路径写customer_gateway_policy_id=single_gateway占位值,而raw_cdrs.customer_gateway_policy_id仍有外键。修复apps/worker-cdr/src/rating.ts的 nullable ID 归一化,将single_gateway与no_policy_match一并转null,补充单元测试,构建并仅重启 Blisglosips@cdr-worker。再次发起实呼s28-1782283796126-nx2488rc@lisglosips-t,SIP 100/200/BYE 200 成功,A 日志显示 hotpath allow 和 CDR XADD,B CDR Workerprocessed=1 deadlettered=0,raw_cdrs入库customer_gateway_policy_id=NULL、business_prefix_id=NULL、landing_caller=s28-ip-1001、landing_callee=13800138000、rating_status=RATED。随后发起长呼s28-1782283960806-f6ct79b4@lisglosips-t验证当前通话/强制挂断控制面,Adlg_list可见目标 dialog03a.af0b33d6,包含呼叫方 SDP/IP 与落地侧 SDP/IP;执行dlg_end_dlg 03a.af0b33d6返回 MIOperation failed,但目标 dialog 状态进入5且timeout=0,二次检查目标 Call-ID/ID 已不可见,T 测试脚本收到平台侧 BYE 形式结束。该行为与 API 现有兼容逻辑一致:Operation failed 后若 dialog 消失则视为HANGUP_CONFIRMED,若 state=5 则视为HANGUP_IN_PROGRESS。 - 修改文件:
apps/worker-cdr/src/rating.ts、apps/worker-cdr/src/rating.spec.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
ping 100.90.90.91 -n 4、B SSH 服务状态检查、B RedisSCRIPT LOAD/SCRIPT EXISTS、Alisglosips-redis-load-hotpath、Aopensips -C -f /etc/opensips/opensips.cfg、Asystemctl restart opensips、Tpython3 /opt/lisglosips-s28/lisglosips-s28-sip.py invite、A/usr/local/sbin/lisglosips-call-control dlg_list、A/usr/local/sbin/lisglosips-call-control dlg_end_dlg 03a.af0b33d6、Braw_cdrs查询、node_modules\.bin\vitest.cmd run apps/worker-cdr/src/rating.spec.ts、node_modules\.bin\tsc.cmd -p apps/worker-cdr/tsconfig.json - 验证结果:B ping 0% 丢包,SSH 可达;A OpenSIPS 与 Redis proxy active;A OpenSIPS 新配置语法检查通过并重启成功,日志显示 cachedb 连接
127.0.0.1:6380;B API ready 正常,config-publisher 仍为 schemaVersion 2 配置版本1782282403866;Worker 定向测试 4 条通过;CDR Worker 修复后实呼入库并计费,deadlettered=0;强制挂断控制面对目标 dialog 生效,目标二次检查不可见。 - 回滚方式:A 可恢复
/var/backups/lisglosips-phase2/20260624144208中的opensips.cfg与lisglosips_hotpath.lua后opensips -C并重启。B 可切回/opt/lisglosips/releases/s33-active-calls-20260623094000并重启 API/CDR/Recording/Config Publisher;数据库如需回滚,使用/data/backups/mysql/20260624T055810Z先在隔离环境验证。CDR Worker 单点修复可恢复上一 release 的apps/worker-cdr/dist并重启lisglosips@cdr-worker。 - 未解决问题:A
dlg_list仍显示几条历史 S28 测试 dialog 残留,需要单独清理或确认 dialog timeout;Web 页面级“当前通话”列表和按钮因缺少登录态未通过浏览器/API 直接复测,本次以底层 MI 和实呼验证为准;真实 80 万手机号段仍未导入,号码归属地为 UNKNOWN。 - 下一任务:清理历史 dialog 后,在网页登录态下复测“当前通话”页面展示呼叫方 IP/落地 IP 与强制挂断按钮;随后按需导入真实号码库数据并做业务前缀命中场景测试。
2026-06-24 14:40 - 需求修改/二期变更:业务前缀重构第 9 步 B 侧发布与 A 侧发布阻塞
- 状态:部分完成;B 侧数据库 migration、release 切换、API/Worker 重启、Redis 配置发布和 preflight 已完成;A 侧 OpenSIPS 未重启到新热路径,A/B/T 实呼验收未完成;当前因 B 再次不可达阻塞
- 操作服务器:B、A、本地;T 未执行实呼
- 完成内容:B 开机恢复后完成 S30 备份,MySQL 备份
/data/backups/mysql/20260624T055810Z与 Redis 备份/data/backups/redis/20260624T055810Z哈希校验通过。创建 B 新 release/opt/lisglosips/releases/s42-phase2-business-prefix-20260624140000,从旧 current 复制基底并覆盖本地构建产物、Prisma schema/migration、业务前缀模块、迁移脚本与 Web dist。执行 Prisma generate 后,发现不能用应用账号跑 DDL,改用/etc/lisglosips/secrets/mysql-migrate.env迁移账号。号码库 migration20260624095000_number_library先因应用账号无 CREATE 权限失败,随后又暴露vendor_gateway_blocked_regions_vendor_gateway_id_region_scope_province_code_city_code_key索引名过长;已修正本地 migration 为短名vgb_regions_gateway_scope_region_key,并在 B 上补齐部分落地后的剩余 DDL,再prisma migrate resolve --applied。业务前缀 migration20260624121000_business_prefix_gateway_rewrite暴露customer_gateway_business_prefixes_gateway_id_business_prefix_id_key索引名过长;已修正为cgbp_gateway_business_prefix_key,在 B 上幂等补齐客户网关新字段、多 IP 表、业务前缀绑定表、主叫前缀表、落地主叫池、CDR 新字段及索引/外键,再标记 applied。Prisma status 显示 4 条 migration 全部 up to date。旧客户网关迁移脚本 dry-run 显示 2 个 safe、0 blockers;apply 成功,更新 2 个客户网关,新增 1 条主叫前缀,写入 1 条 outbox。B current 已切换到s42-phase2-business-prefix-20260624140000,lisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker、lisglosips@config-publisher重启后均 active。API live/ready 和 HTTPS ready 通过,config-publisher 发布 schemaVersion 2 配置版本1782282403866,B preflight 通过。A 侧候选opensips.cfg与lisglosips_hotpath.lua已上传到/tmp并安装到/etc/opensips后opensips -C通过,备份目录为/var/backups/lisglosips-phase2/20260624142935;但发现本地模板cachedb_url占位符仍未替换,且 B 随后再次不可达,无法安全读取 Redis URL、加载 Redis Lua 或重启 A OpenSIPS。为避免 A 半发布,已将 A/etc/opensips/opensips.cfg与/etc/opensips/lisglosips_hotpath.lua恢复到该备份,恢复后opensips -C通过且opensips服务 active。 - 修改文件:
prisma/migrations/20260624095000_number_library/migration.sql、prisma/migrations/20260624121000_business_prefix_gateway_rewrite/migration.sql、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:B
systemctl start lisglosips-backup.service、备份目录内sha256sum -c SHA256SUMS、创建 release 并覆盖构建产物、prisma generate、prisma migrate resolve --rolled-back 20260624095000_number_library、迁移账号执行补齐 DDL、prisma migrate resolve --applied 20260624095000_number_library、prisma migrate resolve --applied 20260624121000_business_prefix_gateway_rewrite、prisma migrate status、node scripts/phase2-gateway-migration.mjs --json、node scripts/phase2-gateway-migration.mjs --apply --json、systemctl restart lisglosips@api lisglosips@cdr-worker lisglosips@recording-worker lisglosips@config-publisher、Blisglosips-release-preflight.sh;Aopensips -C -f /etc/opensips/opensips.cfg、恢复 A phase2 备份。 - 验证结果:本地
prisma validate使用占位DATABASE_URL通过;B MySQL/Redis 备份哈希通过;B Prisma status 为Database schema is up to date!;B API live/ready 与 HTTPS ready 返回ok;config-publisher 日志显示 schemaVersion 2 快照发布成功,manifest 包含businessPrefixCount、customerGatewayIpCount、customerGatewayCallerPrefixCount、号码库与屏蔽地区统计;B preflight ok。A 候选配置语法检查曾通过;A 恢复备份后语法检查通过,OpenSIPS active。B 在最后阶段再次 ping/SSH 超时,A/B/T 实呼未执行。 - 回滚方式:B 应用可执行
infra/server-b/s30/lisglosips-release-rollback.sh /opt/lisglosips/releases/s33-active-calls-20260623094000或手动将/opt/lisglosips/current指回该 release 并重启 API/CDR/Recording/Config Publisher。数据库已执行向前兼容新增表/列,优先用新页面/API 修正配置;如必须恢复,使用/data/backups/mysql/20260624T055810Z在隔离环境验证后恢复。Redis 可切回上一配置版本。A 侧已恢复到/var/backups/lisglosips-phase2/20260624142935,未重启进入新热路径。 - 未解决问题:B Tailscale/主机网络不稳定,发布后再次不可达;A Redis URL 占位符问题需在后续发布脚本中修正,不能直接部署模板;尚未将 Lua SHA 加载到 Redis;A OpenSIPS 未重启到新热路径;A/B/T 端到端呼叫、强制挂断、话单详情字段验证未执行;真实 80 万手机号段仍未导入。
- 下一任务:先恢复并稳定 B 连通性;确认 B current、服务、Redis 配置版本仍正常;读取受控 Redis URL 并安全替换 A cfg 占位符,加载 Lua SCRIPT 到 Redis,
opensips -C通过后重启 A OpenSIPS;再执行 T 实呼和当前通话/话单验收。
2026-06-24 13:40 - 需求修改/二期变更:业务前缀重构第 9 步三机发布前验收
- 状态:已阻塞;未执行数据库迁移,未部署 release,未发布 Redis,未替换 A OpenSIPS,未重启服务
- 操作服务器:本地、A、T 只读探测;B 未连通
- 完成内容:按第 9 步 Runbook 先复核影响、风险与回滚点;确认本步骤会涉及 B MySQL/Redis 备份、Prisma migration、旧客户网关迁移 dry-run/apply、Redis 配置发布、A OpenSIPS/Lua 语法检查和 A/B/T 实呼验收。执行三机连通性检查时,A
100.90.90.90与 T100.93.185.30SSH 可达且 OpenSIPS active;B100.90.90.91从开发机 SSH 超时,开发机Test-NetConnection显示 Ping 与 TCP 22 失败;从 A、T 到 B 的 ping 也 100% 丢包,nc -vz 100.90.90.91 22超时。由于 B 是数据库、Redis、API/Worker 与 release 目标机,未满足安全发布前提,停止执行。 - 修改文件:
IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
ssh -F .codex-private/ssh/config lisglosips-a "hostname; whoami; systemctl is-active opensips || true"、ssh -F .codex-private/ssh/config lisglosips-t "hostname; whoami; systemctl is-active opensips || true"、Test-NetConnection 100.90.90.91 -Port 22、ping 100.90.90.91 -n 4、从 A/T 执行ping -c 2 -W 2 100.90.90.91与nc -vz -w 3 100.90.90.91 22 - 验证结果:A/T SSH 与 OpenSIPS 状态正常;B 当前不可达,无法记录 B current release、无法执行 S30 备份、无法执行 Prisma migration dry-run、无法做 Redis 配置发布与 A/B/T 端到端验收。
- 回滚方式:本次未对服务器做写操作,无需远端回滚。恢复 B 连通后,从第 9 步开头重新执行:先记录 B release/migration/Redis 版本,再执行 S30 MySQL/Redis 备份和恢复校验,然后部署新 release 到独立目录并 dry-run 迁移。
- 未解决问题:需要恢复 B 的 Tailscale/主机网络/SSH 可达性;恢复后需确认 B 上
lisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker、lisglosips@config-publisher、MySQL、Redis 状态,再继续第 9 步。 - 下一任务:恢复 B 可达性后重新执行第 9 步发布前验收;如需现场处理 B,优先通过 KVM/控制台检查电源、Tailscale、nftables、sshd 与系统状态。
2026-06-24 13:25 - 需求修改/二期变更:业务前缀重构第 8 步旧数据迁移与发布前验收准备
- 状态:已完成本地代码与 Runbook;未执行数据库迁移,未连接服务器,未发布 A/B,未重启服务
- 操作服务器:本地
- 完成内容:新增
scripts/phase2-gateway-migration.mjs,用于审计和安全回填旧客户网关数据。脚本默认 dry-run,不写库;只有显式--apply才写入。自动迁移仅允许“每个客户网关恰好 1 条启用旧策略、被叫任意、主叫任意或主叫前缀”的安全场景;多旧策略、无启用策略、旧被叫等值/前缀、旧主叫等值、新旧线路组冲突均列为人工 blockers。apply 时会写入customer_gateways.line_group_id/caller_match_mode/callee_match_mode、补齐customer_gateway_ips与customer_gateway_caller_prefixes,并写customer_gateway_configoutbox 触发配置发布。新增docs/PHASE2_BUSINESS_PREFIX_GATEWAY_MIGRATION_RUNBOOK.md,覆盖 B migration、dry-run/apply、Redis 发布、A OpenSIPS 备份/语法检查/重启、A/B/T 实呼验收和回滚。 - 修改文件:
scripts/phase2-gateway-migration.mjs、docs/PHASE2_BUSINESS_PREFIX_GATEWAY_MIGRATION_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
node --check scripts/phase2-gateway-migration.mjs、node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty、node_modules\.bin\eslint.cmd .、node_modules\.bin\vitest.cmd run、逐包node_modules\.bin\tsc.cmd -p ...、apps\web\node_modules\.bin\vite.CMD build - 验证结果:迁移脚本语法检查通过;typecheck 通过;lint 通过;全量 test 通过,25 个测试文件、86 条测试通过;所有 TypeScript package 构建通过;Web 使用
apps/web自身 Vite 5 构建通过。 - 回滚方式:本地回滚可删除上述脚本/Runbook 并恢复状态文件。若未来在 B 执行
--apply后需回滚,优先通过客户网关 API/页面修正新字段并重新发布 Redis;若必须恢复数据库,使用执行前 MySQL 备份在隔离环境验证后恢复。Redis 可切回上一cfg:previous_version;A OpenSIPS 可恢复发布前 cfg/Lua 备份并重新opensips -C后重启。 - 未解决问题:尚未在 B 执行 Prisma migration 和 dry-run;真实 blockers 数量未知;A 侧
opensips -C和uac_replace_from实呼行为尚未验证;客户侧计费仍未切换到客户网关费率;业务前缀绑定仍需按真实客户号码规则人工配置。 - 下一任务:第 9 步本地/三机发布前总验收:先在 B 执行 dry-run 审计并处理 blockers,再按 Runbook 执行 migration、配置发布、A OpenSIPS 语法检查和 A/B/T 实呼验收;执行前必须再次确认备份和维护窗口。
2026-06-24 13:15 - 需求修改/二期变更:业务前缀重构第 7 步 CDR Stream 与入库链路
- 状态:已完成本地代码;未执行数据库迁移,未发布服务器,未重启服务
- 操作服务器:本地
- 完成内容:CDR Redis Stream 事件契约新增
raw_callee、business_prefix_id、business_prefix、landing_caller、landing_callee、normalized_callee,发布端可显式写入这些字段,解析端兼容旧消息缺字段。CDR Worker 创建raw_cdrs时写入rawCallee、businessPrefixId、businessPrefix、landingCaller、landingCallee,并将normalized_callee保留在 payload 中。话单 API DTO 透出原始被叫、业务前缀、落地主叫、落地被叫;Web 话单详情弹窗的链路信息区展示这些字段,列表列宽不增加。 - 修改文件:
packages/redis/src/cdr-stream.ts、packages/redis/src/cdr-stream.spec.ts、apps/worker-cdr/src/rating.ts、apps/worker-cdr/src/rating.spec.ts、apps/api/src/modules/cdrs/cdrs.repository.ts、apps/api/src/modules/cdrs/cdrs.service.spec.ts、apps/web/src/App.jsx、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
node_modules\.bin\vitest.cmd run packages/redis/src/cdr-stream.spec.ts apps/worker-cdr/src/rating.spec.ts apps/api/src/modules/cdrs/cdrs.service.spec.ts、node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty、node_modules\.bin\eslint.cmd .、node_modules\.bin\vitest.cmd run、逐包node_modules\.bin\tsc.cmd -p ...、apps\web\node_modules\.bin\vite.CMD build - 验证结果:定向测试通过,3 个测试文件、13 条测试通过;typecheck 通过;lint 通过;全量 test 通过,25 个测试文件、86 条测试通过;所有 TypeScript package 构建通过;Web 使用
apps/web自身 Vite 5 构建通过。 - 回滚方式:恢复上述 Stream/Worker/CDR API/Web/状态文件即可;若未来已发布并产生新字段 CDR,回滚旧 Worker 前需确认旧版本能忽略 Stream 中额外字段,或先停止 A 侧新 Lua XADD;数据库已含兼容 nullable 字段,本次无额外 migration。
- 未解决问题:真实 B 环境仍需执行第 1 步 migration 后才能写入新
raw_cdrs字段;A/B/T 尚未发布验收;客户侧计费仍未切换到客户网关费率;旧客户网关策略迁移脚本仍待实现;OpenSIPSuac_replace_from行为仍需实呼验证。 - 下一任务:第 8 步旧数据迁移与发布前验收准备:迁移现有客户网关单 IP/旧策略到新表和单线路组字段,准备 B migration、配置发布、A OpenSIPS 语法检查与 A/B/T 实呼验收脚本。
2026-06-24 13:05 - 需求修改/二期变更:业务前缀重构第 6 步 OpenSIPS/Lua 热路径本地改造
- 状态:已完成本地代码;未连接服务器,未发布 A/B,未重启 OpenSIPS,未执行
opensips -C - 操作服务器:本地
- 完成内容:重写 S28 热路径 Lua,使其优先读取
auth:ip:{source}:gateways候选客户网关列表,兼容旧auth:ip:{source}单网关;按客户网关状态、客户状态、主叫前缀规则、被叫业务前缀规则匹配客户网关;被叫命中业务前缀时剥离前缀并输出真实被叫,同时保留原始被叫、业务前缀 ID/值、号码库归一化号码。线路组选择继续按同组落地网关优先级和地区屏蔽跳过逻辑执行;落地网关命中后按landingCalleePrefix生成落地被叫,按callerRewritePool权重和 Call-ID 确定性哈希选择落地指定主叫。保留旧customer_gateway_policies兼容分支,供未迁移客户网关继续路由。OpenSIPS S28 模板加载uac.so,更新 Lua SHA,接收 24 个热路径返回字段,转发前改写$ru为落地被叫,并在需要时通过uac_replace_from改写 From;成功/失败 CDR XADD 追加raw_callee、business_prefix_id、business_prefix、landing_caller、landing_callee、normalized_callee。 - 修改文件:
infra/server-a/s28/lisglosips_hotpath.lua、infra/server-a/s28/opensips.cfg、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty、node_modules\.bin\eslint.cmd .、node_modules\.bin\vitest.cmd run、逐包node_modules\.bin\tsc.cmd -p ...、apps\web\node_modules\.bin\vite.CMD build、Node 静态校验 Lua SHA 与 OpenSIPS AVP 索引。 - 验证结果:typecheck 通过;lint 通过;全量 test 通过,25 个测试文件、86 条测试通过;所有 TypeScript package 构建通过;Web 使用
apps/web自身 Vite 5 构建通过;静态校验确认opensips.cfg中 EVALSHA 与当前 Lua SHA0b7ee595cdcad2f38a670dd8ff3553387820039e一致,s28_hotpathAVP 映射覆盖 0-23。当前本地无 OpenSIPS/Lua 运行时,尚未执行opensips -C -f /etc/opensips/opensips.cfg,发布到 A 前必须补做。 - 回滚方式:本地回滚可恢复
infra/server-a/s28/lisglosips_hotpath.lua、infra/server-a/s28/opensips.cfg与状态文件。若未来已发布到 A,回滚前先停止变更窗口,恢复发布前备份的/etc/opensips/opensips.cfg与/etc/opensips/lisglosips_hotpath.lua,重新加载旧 Lua SHA,执行opensips -C -f /etc/opensips/opensips.cfg通过后重启 OpenSIPS;B 侧 Redis 可切回上一配置版本。 - 未解决问题:真实 A 机语法检查和重启验收尚未执行;
uac_replace_from的最终行为需在 A/B/T 呼叫中确认;CDR Worker 目前仍未解析并入库raw_callee、business_prefix_id、business_prefix、landing_caller、landing_callee、normalized_callee新字段;客户侧计费仍未切换到客户网关费率;旧客户网关策略迁移脚本仍待实现。 - 下一任务:第 7 步改造 CDR Stream/Worker 入库链路,解析并写入业务前缀与落地主被叫字段,同时准备 A/B/T 端到端验收脚本。
2026-06-24 12:55 - 需求修改/二期变更:业务前缀重构第 5 步 Redis 配置发布结构
- 状态:已完成本地代码;未执行数据库迁移,未发布服务器,未重启服务,未写真实 Redis
- 操作服务器:本地
- 完成内容:
worker-config-publisher快照清单升级为schemaVersion: 2,新增业务前缀数量、客户网关 IP 数、客户网关业务前缀数、客户网关主叫前缀数、落地指定主叫池数量等统计;配置快照新增businessPrefixes,客户网关快照新增多 IP、单线路组、客户侧计费、主叫前缀规则、被叫业务前缀规则,落地网关快照新增landingCalleePrefix与callerRewritePool。Redis 写入新增business_prefix:*、business_prefix_value:*、business_prefixes、auth:ip:*:gateways、customer_gateway:*:caller_prefixes、customer_gateway:*:business_prefixes、customer_gateway:*:line_group、vendor_gateway:*:caller_rewrite_pool等热路径 key,同时保留旧auth:ip:*、旧客户策略列表和旧落地前缀规则兼容输出。业务前缀 CRUD 增删改启停时新增business_prefix_configoutbox,发布器纳入该 aggregate type。 - 修改文件:
apps/worker-config-publisher/src/publisher.ts、apps/worker-config-publisher/src/publisher.spec.ts、apps/api/src/modules/business-prefixes/business-prefixes.repository.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;执行
prisma generate重新生成本地 Prisma Client。 - 执行的关键命令:
node_modules\.bin\prisma.cmd format、node_modules\.bin\prisma.cmd generate、node_modules\.bin\vitest.cmd run apps/worker-config-publisher/src/publisher.spec.ts、node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty、node_modules\.bin\eslint.cmd .、node_modules\.bin\vitest.cmd run、逐包node_modules\.bin\tsc.cmd -p ...、apps\web\node_modules\.bin\vite.CMD build - 验证结果:Prisma format/generate 通过;发布器定向测试通过,1 条测试通过;typecheck 通过;lint 通过;全量 test 通过,25 个测试文件、86 条测试通过;所有 TypeScript package 构建通过;Web 使用
apps/web自身 Vite 5 构建通过。根级node scripts/pnpm-run.mjs typecheck/build因当前 shell 找不到pnpm报spawnSync pnpm ENOENT,已用本地.cmd可执行文件完成等效验证。 - 回滚方式:恢复上述发布器、业务前缀仓储和状态文件即可;若未来已经发布到 B 并产生
business_prefix_configoutbox,可停止 config-publisher 后将相关 outbox 标为已处理或恢复发布前数据库备份,再切回旧 release。Redis 活跃配置可通过CONFIG_PREVIOUS_VERSION_KEY回滚到上一版本;本次未对服务器 Redis 进行写入。 - 未解决问题:真实 B 环境仍需先执行第 1 步 migration 后才能运行新发布器;OpenSIPS/Lua 尚未读取新 Redis key,尚未完成业务前缀剥离、客户网关候选匹配、客户侧计费、落地被叫加前缀和指定主叫权重选择;CDR 写入 raw callee/business prefix/landing caller/landing callee 与 A/B/T 验收仍待后续步骤。
- 下一任务:第 6 步改造 OpenSIPS/Lua 热路径:按 IP 候选客户网关、主叫前缀和被叫业务前缀匹配客户网关,并输出剥离后的真实被叫号码。
2026-06-24 12:50 - 需求修改/二期变更:业务前缀重构第 4 步落地网关新改写字段
- 状态:已完成本地代码;未执行数据库迁移,未发布服务器,未重启服务
- 操作服务器:本地
- 完成内容:落地网关 API 增加
landingCalleePrefix和callerRewritePool;创建/更新时写入vendor_gateways.landing_callee_prefix与vendor_gateway_caller_rewrite_pool,并继续保留旧vendor_gateway_prefix_rules表作为兼容字段。落地被叫前缀支持空值或英文/数字;指定主叫号码池支持多个号码、权重和启停状态,并校验重复号码。前端落地网关编辑页将旧“被叫前缀转换/主叫前缀转换”替换为“落地要求被叫前缀”和“落地要求指定主叫”,列表新增落地被叫前缀与指定主叫数展示;保存时调用真实PATCH /api/v2/vendor-gateways/:id。顺带修正落地网关子表 ID 生成长度,避免vgfp_...、vgcr_...超过 32 字符字段长度。 - 修改文件:
apps/api/src/modules/vendor-gateways/vendor-gateways.repository.ts、apps/api/src/modules/vendor-gateways/vendor-gateways.service.ts、apps/api/src/modules/vendor-gateways/vendor-gateways.e2e.spec.ts、apps/web/src/api.js、apps/web/src/App.jsx、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;执行
prisma generate重新生成本地 Prisma Client。 - 执行的关键命令:
node_modules\.bin\prisma.cmd generate、node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty false、node_modules\.bin\vitest.cmd run apps/api/src/modules/vendor-gateways/vendor-gateways.e2e.spec.ts、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 test - 验证结果:Prisma generate 通过;typecheck 通过;落地网关 e2e 通过,4 条测试通过;lint 通过;workspace build 通过;全量 test 通过,25 个测试文件、86 条测试通过。
- 回滚方式:恢复上述落地网关 API/Web 文件和
IMPLEMENTATION_STATUS.md即可;若只回滚第 4 步,可保留第 1-3 步。若未来已执行 migration 且已录入新落地改写数据,回滚前需导出vendor_gateways.landing_callee_prefix与vendor_gateway_caller_rewrite_pool,确认没有生产配置依赖后再删列/删表或恢复备份。 - 未解决问题:当前前端仍只完善落地网关编辑路径,新增按钮仍是占位;真实 B 环境必须先执行第 1 步 migration 后才能使用本页面新字段;Redis 发布结构、OpenSIPS/Lua 热路径落地被叫加前缀/主叫按权重改写、CDR 写入 landing caller/callee 和 A/B/T 验收仍待后续步骤。
- 下一任务:第 5 步改造 Redis 配置发布与 Worker/热路径配置结构,先在本地生成新配置快照契约,再安排 OpenSIPS/Lua 处理顺序。
2026-06-24 12:35 - 需求修改/二期变更:业务前缀重构第 3 步客户网关新模型
- 状态:已完成本地代码;未执行数据库迁移,未发布服务器,未重启服务
- 操作服务器:本地
- 完成内容:客户网关 API 扩展为多 IP、单落地线路组、客户侧计费周期/周期费率、主叫匹配模式与前缀列表、被叫匹配模式与业务前缀多选;创建/更新时写入
customer_gateway_ips、customer_gateway_caller_prefixes、customer_gateway_business_prefixes,同时保留旧source_ip为首个 IP 兼容字段,旧customer_gateway_policies表和模块未删除。前端客户网关页面新增客户选择、多个 IP、SIP 域、落地线路组、客户费率、主叫规则和业务前缀多选;列表展示落地线路组、客户费率、主叫规则和被叫规则;旧“策略配置”入口已从客户网关列表隐藏。客户网关新增/编辑已接入真实 API。 - 修改文件:
apps/api/src/modules/customer-gateways/customer-gateways.repository.ts、apps/api/src/modules/customer-gateways/customer-gateways.service.ts、apps/api/src/modules/customer-gateways/customer-gateways.e2e.spec.ts、apps/web/src/api.js、apps/web/src/App.jsx、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;执行
prisma generate重新生成本地 Prisma Client。 - 执行的关键命令:
node_modules\.bin\prisma.cmd generate、node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty false、node_modules\.bin\vitest.cmd run apps/api/src/modules/customer-gateways/customer-gateways.e2e.spec.ts、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 test - 验证结果:Prisma generate 通过;typecheck 通过;客户网关 e2e 通过,5 条测试通过;lint 通过;workspace build 通过;全量 test 通过,25 个测试文件、86 条测试通过。
- 回滚方式:恢复上述客户网关 API/Web 文件和
IMPLEMENTATION_STATUS.md即可;若只回滚第 3 步,可保留第 1 步 Schema 与第 2 步业务前缀管理。本次未连接服务器、未执行 MySQL migration、未写 Redis、未重启 OpenSIPS/RTPEngine/Worker/Web。 - 未解决问题:真实 B 环境必须先执行第 1 步 migration 后才能使用本页面新字段;旧客户网关策略数据尚未迁移到
customer_gateways.line_group_id与新匹配表;落地网关“落地要求被叫前缀/指定主叫池”、Redis 发布结构、OpenSIPS/Lua 热路径号码剥离与落地改写、CDR 写入链路和 A/B/T 验收仍待后续步骤。 - 下一任务:第 4 步改造落地网关 API/Web:用
landing_callee_prefix和vendor_gateway_caller_rewrite_pool替代旧主被叫前缀转换规则。
2026-06-24 12:20 - 需求修改/二期变更:业务前缀重构第 2 步 API 与 Web 菜单
- 状态:已完成本地代码;未执行数据库迁移,未发布服务器,未重启服务
- 操作服务器:本地
- 完成内容:新增
BusinessPrefixesModule,提供GET/POST/PATCH/DELETE /api/v2/business-prefixes以及启用/禁用接口;权限复用customer_gateways.view/manage,审计模块记录 business-prefixes 创建、修改、启停、删除。前端新增“业务前缀管理”菜单和页面,支持关键字/状态筛选、新增、编辑、启用/禁用确认、删除确认,删除按钮使用红色警示色;前端 API Client 增加业务前缀相关方法和错误文案。业务前缀删除时若已被客户网关引用会返回BUSINESS_PREFIX_IN_USE。 - 修改文件:
apps/api/src/modules/business-prefixes/、apps/api/src/modules/app.module.ts、apps/web/src/api.js、apps/web/src/App.jsx、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;执行
prisma generate重新生成本地 Prisma Client。 - 执行的关键命令:
node_modules\.bin\prisma.cmd generate、node_modules\.bin\tsc.cmd -b tsconfig.build.json --pretty false、corepack pnpm@10.33.0 lint、node_modules\.bin\vitest.cmd run packages/database/src/schema-contract.spec.ts、corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 test - 验证结果:Prisma generate 通过;typecheck 通过;lint 通过;database contract 定向测试通过;workspace build 通过;全量 test 通过,25 个测试文件、86 条测试通过。
- 回滚方式:恢复
apps/api/src/modules/business-prefixes/、apps/api/src/modules/app.module.ts、apps/web/src/api.js、apps/web/src/App.jsx、IMPLEMENTATION_STATUS.md,并保留/恢复第 1 步 Schema 取决于是否继续二期;本次未连接服务器、未执行 MySQL migration、未写 Redis、未重启 OpenSIPS/RTPEngine/Worker/Web。 - 未解决问题:当前业务前缀页面依赖第 1 步 migration 实际执行后才可在真实 B API 使用;客户网关多 IP、单线路组、客户侧费率、业务前缀多选、主叫前缀规则、落地网关新改写字段、Redis 发布结构、OpenSIPS/Lua 热路径和 A/B/T 验收仍待后续步骤。
- 下一任务:第 3 步改造客户网关 API/Web:支持多 IP、单落地线路组、客户侧费率、业务前缀多选和主叫前缀规则,同时设计旧策略兼容/迁移方式。
2026-06-24 12:05 - 需求修改/二期变更:业务前缀重构第 1 步本地数据模型
- 状态:已完成本地代码;未执行数据库迁移,未发布服务器,未重启服务
- 操作服务器:本地
- 完成内容:按兼容迁移方式新增业务前缀、客户网关多 IP、客户网关单落地线路组、客户侧计费周期/费率、客户网关主叫/被叫匹配模式、客户网关业务前缀关联、客户网关主叫前缀列表、落地网关“落地要求被叫前缀”、落地网关指定主叫号码池和权重、话单 raw callee/business prefix/landing caller/landing callee 等二期字段。保留旧
customer_gateway_policies与vendor_gateway_prefix_rules,用于后续 API/Web/OpenSIPS 分阶段切换。 - 修改文件:
prisma/schema.prisma、prisma/migrations/20260624121000_business_prefix_gateway_rewrite/migration.sql、packages/database/src/schema-contract.spec.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用本地仓库依赖中的 Prisma
6.19.0、Vitest4.0.14。 - 执行的关键命令:
node_modules\.bin\prisma.cmd format、$env:DATABASE_URL='mysql://lisglosips_app@127.0.0.1:3306/lisglosips'; node_modules\.bin\prisma.cmd validate、node_modules\.bin\vitest.cmd run packages/database/src/schema-contract.spec.ts - 验证结果:Prisma format 通过;Prisma validate 通过;数据库 contract 定向测试通过,1 个测试文件、6 条测试通过。全量 lint/typecheck/test/build 尚未运行,因为本步只改 Schema、迁移与 contract,下一步 API/Web 改动后再跑全量验证。
- 回滚方式:恢复
prisma/schema.prisma、删除prisma/migrations/20260624121000_business_prefix_gateway_rewrite/并恢复packages/database/src/schema-contract.spec.ts、IMPLEMENTATION_STATUS.md即可;本次未连接服务器、未执行 MySQL migration、未写 Redis、未重启 OpenSIPS/RTPEngine/Worker/Web。 - 未解决问题:业务前缀管理 API/Web、客户网关新表单与新 API、旧策略迁移脚本、落地网关新字段 API/Web、Redis 发布结构、OpenSIPS/Lua 热路径号码剥离与落地改写、CDR 写入链路、A/B/T 端到端验收仍待后续步骤实施。
- 下一任务:第 2 步实现“业务前缀管理” API 与 Web 菜单,继续保持本地开发,不直接执行 B MySQL migration。
2026-06-24 11:58 - 需求修改/二期变更:业务前缀、客户网关匹配和落地号码改写重构需求评估
- 状态:已完成需求文档更新;未改代码,未执行数据库迁移,未发布服务器
- 操作服务器:本地
- 完成内容:将新增“业务前缀管理”菜单、客户网关多 IP、客户网关单线路组、客户网关客户侧费率、主叫前缀/被叫业务前缀匹配规则、真实被叫号码语义、落地要求被叫前缀、落地要求指定主叫号码池和权重选择等需求写入
SOFTSWITCH_PLATFORM_DESIGN_V2.md。明确该需求会替代旧客户网关多路由策略模型和旧落地网关主被叫前缀转换规则,必须分阶段迁移,不能两套热路径并存。补充数据模型建议、Redis 热路径结构、OpenSIPS/Lua 处理顺序、CDR 字段、失败原因、风险约束和 10 步实施拆分。 - 修改文件:
SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
rg检查现有客户网关策略、落地前缀转换、号码库、OpenSIPS 热路径和文档位置;文档更新后用关键字复核“业务前缀管理”“customer_gateway_ips”“landing_callee_prefix”“vendor_gateway_caller_rewrite_pool”“BUSINESS_PREFIX_NOT_MATCHED”等条目。 - 验证结果:本次为 Markdown 需求文档更新,未运行 typecheck/lint/test/build;未连接服务器,未执行 Prisma migration,未写 Redis,未重启 OpenSIPS。
- 回滚方式:恢复
SOFTSWITCH_PLATFORM_DESIGN_V2.md和IMPLEMENTATION_STATUS.md本次文档改动即可;不涉及 B release、MySQL、Redis、OpenSIPS、RTPEngine、Worker 或 Web 回滚。 - 未解决问题:实施前仍需确认业务前缀允许字符和长度、是否全局唯一、同一 IP 下多个客户网关规则冲突策略、客户侧费率是否立即替代 S23 最小计费逻辑、旧客户网关多策略如何迁移、落地指定主叫权重选择是否要求严格随机或按 Call-ID 可复现。
- 下一任务:建议先做第 1 步“需求冻结与冲突口径确认”,随后单独开数据库建模/migration,不要直接改 OpenSIPS 热路径。
2026-06-24 11:41 - 需求修改/二期变更:号码库第 6、7 步本地实现
- 状态:已完成本地代码;准备提交 Git;未发布到 B/A,未执行数据库迁移,未重启服务
- 操作服务器:本地
- 完成内容:CDR Stream 契约新增可选号码识别快照字段,并保持旧 Stream/pending 消息兼容;CDR Worker 入库
raw_cdrs时写入calleeCityCode、calleeCityName、calleeProvinceName、calleeOperator、calleeNumberType。新增CdrsModule,提供GET /api/v2/cdrs与GET /api/v2/cdrs/:id,复用cdr.view权限,支持主叫、被叫、客户网关、落地网关、地级市代码、运营商分页筛选,并返回客户/网关/线路组名称与 rated CDR 费用。Web 话单中心改为读取真实 CDR API,列表和详情展示地级市、运营商、号码类型、SIP 状态码、客户费用、成本和毛利。 - 修改文件:
packages/redis/src/cdr-stream.ts、packages/redis/src/cdr-stream.spec.ts、apps/worker-cdr/src/rating.ts、apps/worker-cdr/src/rating.spec.ts、apps/api/src/modules/app.module.ts、apps/api/src/modules/cdrs/cdrs.module.ts、apps/api/src/modules/cdrs/cdrs.controller.ts、apps/api/src/modules/cdrs/cdrs.service.ts、apps/api/src/modules/cdrs/cdrs.repository.ts、apps/api/src/modules/cdrs/cdrs.service.spec.ts、apps/web/src/api.js、apps/web/src/App.jsx、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用 pnpm
10.33.0、Prisma6.19.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 vitest run packages/redis/src/cdr-stream.spec.ts apps/worker-cdr/src/rating.spec.ts apps/api/src/modules/cdrs/cdrs.service.spec.ts、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 build - 验证结果:typecheck 通过;目标测试 3 个文件、13 条测试通过;lint 通过;workspace build 通过;第一次将全量 test 与 build 并行执行时 3 个依赖本地服务的 e2e
beforeAll因资源争用超时,随后单独重跑corepack pnpm@10.33.0 test通过,25 个测试文件、85 条测试全部通过。 - 回滚方式:恢复上述文件即可;本次未连接服务器、未发布 B/A、未写 Redis、未执行 MySQL migration、未重启 OpenSIPS,因此无远端回滚项。后续若发布,B 发布前需备份 MySQL 和当前 release;A 发布仍需先备份 OpenSIPS 配置/Lua 并执行
opensips -C -f。 - 未解决问题:真实 80 万号段文件导入、Redis 发布容量评估、落地网关屏蔽地区 API/Web 管理入口、A/B/T 地区屏蔽端到端验收仍未实施;当前 CDR API 已能显示新字段,但历史话单在迁移前/旧消息场景下会显示
UNKNOWN或-。 - 下一任务:提交 Git 后,建议优先补落地网关屏蔽地区管理入口和大批量号码库导入 Runbook,再安排 B/A 发布与 A/B/T 验收。
2026-06-24 11:31 - 需求修改/二期变更:号码库第 4、5 步本地实现
- 状态:已完成本地代码;未发布到 B/A,未执行数据库迁移,未重启服务
- 操作服务器:本地
- 完成内容:Config Publisher 快照新增地级市、手机 7 位号段、固话区号、运营商前缀和落地网关屏蔽地区;Redis 写入版本化
geo_city、phone_segment、area_code、carrier_prefixkey,以及vendor_gateway:{id}:blocked_city_codes/blocked_province_codesset。号码库导入新增number_library_configoutbox,导入后可触发发布器生成新快照。S28 OpenSIPS Lua 热路径新增被叫号码规范化、手机 7 位号段解析、固话区号解析、运营商前缀解析、落地网关城市/省份屏蔽跳过逻辑;全部可用网关都被地区屏蔽时返回NO_VENDOR_ROUTE_REGION_BLOCKED。S28opensips.cfg更新 Lua SHA1、读取追加返回字段,并在成功/失败 CDR Stream 中携带callee_city_code、callee_city_name、callee_province_name、callee_operator、callee_number_type。 - 修改文件:
apps/worker-config-publisher/src/publisher.ts、apps/worker-config-publisher/src/publisher.spec.ts、apps/api/src/modules/number-library/number-library.repository.ts、infra/server-a/s28/lisglosips_hotpath.lua、infra/server-a/s28/opensips.cfg、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用 pnpm
10.33.0、Prisma6.19.0。 - 执行的关键命令:
corepack pnpm@10.33.0 vitest run apps/worker-config-publisher/src/publisher.spec.ts apps/api/src/modules/number-library/number-library.e2e.spec.ts、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 build、Get-FileHash -Algorithm SHA1 infra/server-a/s28/lisglosips_hotpath.lua - 验证结果:目标测试 2 个文件、4 条测试通过;typecheck 通过;lint 通过;全量 Vitest 24 个测试文件、82 条测试通过;workspace build 通过;本机无
lua、无opensips命令,因此未在本地执行 Lua 解释器语法检查和opensips -C。 - 回滚方式:恢复上述本地文件即可;本次未连接服务器、未发布 B/A、未写 Redis、未执行 MySQL migration、未重启 OpenSIPS,因此无远端回滚项。后续若发布:B 需先备份 MySQL/Redis 和当前 release,回滚 Redis 可切回
cfg:previous_version;A 需先备份/etc/opensips/opensips.cfg与/etc/opensips/lisglosips_hotpath.lua,回滚时恢复备份、执行opensips -C -f /etc/opensips/opensips.cfg通过后重启 OpenSIPS。 - 未解决问题:第 6 步 CDR Worker 尚未把
callee_city_code、callee_city_name、callee_province_name、callee_operator、callee_number_type入库到raw_cdrs新字段;落地网关屏蔽地区仍缺少 Web/API 管理入口;真实 80 万号段文件导入、Redis 发布容量评估、A/B/T 端到端地区屏蔽验收仍未执行。 - 下一任务:建议第 6 步改造 CDR Stream/Worker 入库号码识别字段,并补落地网关屏蔽地区的 API/Web 管理入口后,再安排 B/A 发布和 A/B/T 验收。
2026-06-24 11:15 - 需求修改/二期变更:号码库第 3 步 Web 本地实现
- 状态:已完成本地代码;未发布到 B,未执行数据库迁移
- 操作服务器:本地
- 完成内容:新增 Web “号码库”运营菜单,页面包含地级市字典、手机号码库、城市区号、运营商号码段规则四个 Tab。每个 Tab 接入
/number-library/*API,支持服务端查询入口、筛选、刷新、总数显示和批量 JSON 导入弹窗;导入成功后刷新当前 Tab。同步更新 V2 设计文档,将号码库菜单从三个 Tab 调整为四个 Tab,并补充第 3 步本地实施状态。 - 修改文件:
apps/web/src/api.js、apps/web/src/App.jsx、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用 pnpm
10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 test - 验证结果:typecheck 通过;lint 通过;workspace build 通过;第一次将 test 与 build 并行执行时 5 个依赖本地服务的 e2e
beforeAll因资源争用超时,随后单独重跑corepack pnpm@10.33.0 test通过,23 个测试文件、81 条测试全部通过。 - 回滚方式:恢复
apps/web/src/api.js、apps/web/src/App.jsx、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md本次改动即可;本次未连接服务器、未发布 B、未执行 MySQL migration、未写入 Redis、未重启服务,因此无远端回滚项。 - 未解决问题:号码库前端当前使用批量 JSON 导入,只适合小批验证和接口联调;真实 80 万号段仍需文件导入/后台任务/进度与失败明细。号码解析服务、Config Publisher Redis 快照、OpenSIPS/Lua 屏蔽地区跳转、CDR Worker 新字段入库、话单中心展示与 A/B/T 端到端验收仍未实施。
- 下一任务:建议第 4 步实现 Config Publisher/Redis 热路径快照前,先补大批量导入任务 Runbook 和文件导入接口,避免把 80 万号段通过普通 JSON 表单导入。
2026-06-24 11:07 - 需求修改/二期变更:号码库第 1、2 步本地实现
- 状态:已完成本地代码;未发布到 B,未执行数据库迁移
- 操作服务器:本地
- 完成内容:完成号码库第 1、2 步本地基线。Prisma Schema 和迁移新增
geo_cities、phone_number_segments、phone_area_codes、carrier_prefix_rules、vendor_gateway_blocked_regions,并在raw_cdrs预留被叫地级市、省份、运营商和号码类型快照字段。新增NumberLibraryModule,提供城市、手机 7 位号段、固话区号、运营商前缀规则的分页查询与批量 upsert 导入 API,单批限制 1000 条,接入 RBAC 和审计。新增权限number_library.view、number_library.manage并写入 seed;更新 schema contract 和号码库 e2e 测试。 - 修改文件:
prisma/schema.prisma、prisma/migrations/20260624095000_number_library/migration.sql、apps/api/src/modules/app.module.ts、apps/api/src/modules/number-library/number-library.module.ts、apps/api/src/modules/number-library/number-library.controller.ts、apps/api/src/modules/number-library/number-library.service.ts、apps/api/src/modules/number-library/number-library.repository.ts、apps/api/src/modules/number-library/number-library.e2e.spec.ts、packages/auth/src/index.ts、packages/database/src/schema-contract.spec.ts、prisma/seed.ts、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用 Prisma
6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 prisma format、corepack pnpm@10.33.0 prisma:validate、corepack pnpm@10.33.0 prisma:generate、corepack pnpm@10.33.0 vitest run apps/api/src/modules/number-library/number-library.e2e.spec.ts packages/database/src/schema-contract.spec.ts、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 build - 验证结果:Prisma format/validate/generate 通过;号码库 e2e 与 schema contract 共 8 条测试通过;typecheck 通过;lint 通过;全量 Vitest 23 个测试文件、81 条测试通过;workspace build 通过。
- 回滚方式:恢复上述本地文件改动即可;因为未连接服务器、未发布 B、未执行 MySQL migration、未导入真实数据,所以无远端回滚项。若后续发布到 B,执行迁移前必须先备份 B MySQL;回滚需恢复迁移前备份或执行经验证的反向迁移,并清理新增权限/seed 变更。
- 未解决问题:真实 80 万号段文件导入、导入任务进度/失败明细、号码解析服务、Config Publisher Redis 发布、OpenSIPS/Lua 屏蔽地区跳转、CDR Worker 入库新字段、话单中心展示与筛选、A/B/T 端到端验收仍未实施。
- 下一任务:建议第 3 步 Web “号码库”菜单和三个 Tab;或先实现第 4 步 Config Publisher/Redis 快照结构前的导入任务 Runbook。
2026-06-24 09:48 - 需求修改/二期变更:号码库与屏蔽地区路由需求评估
- 状态:已完成
- 操作服务器:本地
- 完成内容:评估新增“号码库”菜单及手机号码库、城市区号、运营商号码段规则三个 Tab 的影响范围;明确需要新增地级市字典
geo_cities,用于手机 7 位号段、固话区号、落地网关屏蔽地区和 CDR 快照统一引用;明确每通话单需要保存被叫地级市、省份、运营商和号码类型快照;明确落地网关屏蔽地区应在 OpenSIPS/Lua 热路径中跳过当前网关并尝试同一线路组下一落地网关。已将需求、数据模型建议、Redis 热路径结构、CDR 字段、路由规则、风险和 8 步实施拆分写入SOFTSWITCH_PLATFORM_DESIGN_V2.md,文档版本升级为 V2.2。 - 修改文件:
SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:
rg检查现有 Schema、CDR Stream、OpenSIPS Lua 和 Web 中的地区/运营商/屏蔽地区相关实现;文档更新后用rg校验 V2.2、号码库、geo_cities、phone_number_segments、carrier_prefix_rules、vendor_gateway_blocked_regions、NO_VENDOR_ROUTE_REGION_BLOCKED等关键条目。 - 验证结果:本次为需求评估和 Markdown 文档更新,未运行 typecheck/lint/test/build;未连接服务器,未做数据库迁移、服务重启或数据导入。
- 回滚方式:恢复
SOFTSWITCH_PLATFORM_DESIGN_V2.md和IMPLEMENTATION_STATUS.md本次文档改动即可;不涉及 B release、MySQL、Redis、OpenSIPS、RTPEngine、Worker 或 Web 发布回滚。 - 未解决问题:真实实施前仍需确认号码库数据来源、更新频率、导入格式、行政区划编码口径、运营商枚举口径、省级屏蔽是否需要、以及 80 万号段导入对 MySQL/Redis 内存和发布耗时的影响。
- 下一任务:如确认实施,建议从“数据建模与迁移 + 导入 Runbook”单独开工,不要与 OpenSIPS 热路径改造混在同一次发布。
2026-06-24 09:31 - 需求修改/二期变更:用户与角色删除
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:用户管理新增红色“删除”按钮和自研确认弹窗,确认后调用
DELETE /api/v2/users/:id;角色与权限新增红色“删除”按钮和自研确认弹窗,内置角色前端禁用删除,后端同时保护内置角色并在角色仍有关联用户时拒绝删除。后端用户删除为软删除:禁用账号、写入deletedAt、清理用户角色关联以释放角色用户数;角色删除为软删除:禁用角色并写入deletedAt。两个删除接口均接入 RBACusers.manage/roles.manage和审计日志。未改 Prisma Schema,未执行数据库迁移。 - 修改文件:
apps/api/src/modules/users/users.controller.ts、apps/api/src/modules/users/users.service.ts、apps/api/src/modules/users/users.repository.ts、apps/api/src/modules/roles/roles.controller.ts、apps/api/src/modules/roles/roles.service.ts、apps/api/src/modules/roles/roles.repository.ts、apps/api/src/modules/security/security-rbac-audit.e2e.spec.ts、apps/api/src/modules/users-roles-delete.e2e.spec.ts、apps/web/src/App.jsx、apps/web/src/api.js、IMPLEMENTATION_STATUS.md;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中已覆盖 API/Web dist。 - 安装软件及固定版本:未安装新软件。
- 执行的关键命令:本地
corepack pnpm@10.33.0 vitest run apps/api/src/modules/users-roles-delete.e2e.spec.ts、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 build;Btar --no-same-owner -xzf覆盖当前 release API/Web dist、systemctl restart lisglosips@api、nginx -t、systemctl reload nginx、curl http://127.0.0.1:3000/api/v2/health/ready。 - 验证结果:新增用户/角色删除 E2E 3 条通过;typecheck 通过;lint 通过;全量 Vitest 22 个测试文件、77 条测试通过;workspace build 通过。B
lisglosips@api与nginxactive,API ready 返回config/database/redis均 ok;服务日志确认已映射DELETE /api/v2/users/:id与DELETE /api/v2/roles/:id;Web 首页https://100.90.90.91/返回 200 并引用新构建资源。 - 回滚方式:B 已备份覆盖前 dist 到
/var/backups/lisglosips-s41-user-role-delete/20260624T092950/api-dist与/var/backups/lisglosips-s41-user-role-delete/20260624T092950/web-dist;如需回滚,将两目录恢复到/opt/lisglosips/releases/s33-active-calls-20260623094000/apps/api/dist与/opt/lisglosips/releases/s33-active-calls-20260623094000/apps/web/dist,重启lisglosips@api并执行nginx -t && systemctl reload nginx。本次无数据库 schema migration;若已通过新功能误删用户/角色,删除为软删除,需要在维护窗口受控恢复对应deleted_at/status,用户还需按审计和业务确认恢复user_roles关联。 - 未解决问题:用户管理、角色与权限的新增/编辑/启停仍沿用旧的前端本地态实现,未纳入本次需求重构;未做 A/B/T SIP 端到端,因为本次只涉及 B API/Web 管理面,不影响 Redis 热路径、OpenSIPS、RTPEngine、Worker 或三机呼叫链路。
- 下一任务:等待下一个二期需求。
2026-06-23 22:07 - 需求修改/二期变更:S30 后需求写入 V2 设计文档
- 状态:已完成
- 操作服务器:本地
- 完成内容:将 S30 以后用户新增需求整理进
SOFTSWITCH_PLATFORM_DESIGN_V2.md,文档版本从 V2.0 调整为 V2.1,新增“25.5 S30 后二期需求变更”章节;同步更新 V2 范围表,纳入当前通话、受控删除、网关/线路组计数、人工充值记录语义、待设计菜单隐藏等内容;补充登录验证码、当前通话和强制挂断、删除/启停确认、充值与计费流水语义、动态路由和 CDR 补齐、前端交互、10 路虚拟呼叫测试工具、二期回归要求;修正 S27 描述中“待设计菜单继续保留标识”的旧表述。 - 修改文件:
SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件。
- 执行关键命令:
rg检查 V2 文档关键章节和条目;git diff -- SOFTSWITCH_PLATFORM_DESIGN_V2.md --stat复核文档差异。 - 验证结果:文档已包含 V2.1 状态、当前通话实施范围、待设计菜单隐藏说明、“25.5 S30 后二期需求变更”及 25.5.1-25.5.8 子章节;本次为纯 Markdown 文档更新,未运行 typecheck/lint/build。
- 回滚方式:恢复
SOFTSWITCH_PLATFORM_DESIGN_V2.md和IMPLEMENTATION_STATUS.md本次文档改动即可;不涉及服务器、数据库、Redis、OpenSIPS、RTPEngine 或 Worker 回滚。 - 未解决问题:S40 10 路虚拟呼叫完整场景此前仍在 T 后台运行,本文档更新未等待其全程结束;若需确认最终结果,应继续查看
/tmp/lisglosips-s40-virtual-calls-20260623T215430.log。
2026-06-23 21:51 - 需求修改/二期变更:10 路虚拟呼叫测试工具与当前通话自动刷新
- 状态:已完成工具部署,完整 10 路场景已启动并运行中
- 操作服务器:A、B、T、本地
- 完成内容:当前通话页面新增默认开启的 5 秒自动刷新开关,保留手动刷新;T 新增
/opt/lisglosips-s40/lisglosips-s40-virtual-calls.py,可按 15 秒间隔发起 10 路虚拟呼叫,前 5 路先回180 Ringing、45 秒后200 OK、保持 600 秒后 BYE,后 5 路先回180 Ringing、70 秒后480 Temporarily Unavailable。脚本默认使用当前已打通策略的主叫s36-1001、被叫13800136036,并按 Call-ID 序号区分接通/不接通场景。首次完整场景暴露 Afr_inv_timeout=30会在 45 秒接通前返回 408,已将 A OpenSIPSfr_inv_timeout临时调为 95 并重启后重新启动场景。 - 修改文件:
apps/web/src/App.jsx、infra/server-t/s40/lisglosips-s40-virtual-calls.py、infra/server-a/s28/opensips.cfg、IMPLEMENTATION_STATUS.md;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中已覆盖apps/web/dist;T 已部署/opt/lisglosips-s40;A 已就地修改/etc/opensips/opensips.cfg。 - 安装软件及固定版本:未安装新软件;继续使用 Node.js
v22.22.2、pnpm10.33.0、Vite5.4.21、T 既有 Python 3.12。 - 执行关键命令:本地
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;Btar -xzf覆盖 web dist、nginx -t、systemctl reload nginx、curl -sk https://127.0.0.1/;Tpython3 -m py_compile /opt/lisglosips-s40/lisglosips-s40-virtual-calls.py、短烟测--total 1 --answer-ring 2 --hold 3、后台启动完整场景;Aopensips -C -f /etc/opensips/opensips.cfg、systemctl restart opensips。 - 验证结果:本地 typecheck/lint/build 均通过;B nginx 配置测试通过且 active,首页 HTML 返回新资源
/assets/index-DwIXfg91.js和/assets/index-TA3aiKvG.css;T 脚本 Python 编译通过。短烟测先发现50620被旧 S28 UAS 占用且新号码无策略,已调整为临时停旧 UAS、固定主叫/被叫并按 Call-ID 分流;最终 1 路短烟测通过:收到100 Giving it a try、180 Ringing、延迟后200 OK、3 秒后 BYE 且 UAS 返回200 OK。首次完整场景因 Afr_inv_timeout=30导致 45 秒接通前被 A 返回 408,已停止并恢复旧 UAS;A 超时调为 95 后重新启动完整场景,PID32120,日志/tmp/lisglosips-s40-virtual-calls-20260623T215430.log,已验证第 1、2 路均在 45 秒后收到200 OK并进入connected hold=600.0s。 - 回滚方式:B 前端回滚可从
/var/backups/lisglosips-s40-active-calls-autorefresh/20260623T214444/web-dist恢复当前 release 的apps/web/dist,再执行nginx -t && systemctl reload nginx。A 超时回滚可从/var/backups/lisglosips-s40-fr-inv-timeout/20260623T215330/opensips.cfg恢复/etc/opensips/opensips.cfg,再执行opensips -C -f /etc/opensips/opensips.cfg && systemctl restart opensips。T 脚本回滚可删除/opt/lisglosips-s40,或从/var/backups/lisglosips-s40-virtual-calls/20260623T214907/lisglosips-s40恢复上一版。若需提前停止完整场景:在 T 执行sudo pkill -f /opt/lisglosips-s40/lisglosips-s40-virtual-calls.py,然后确认100.93.185.30:50620有旧 S28 UAS;若未恢复,执行sudo nohup runuser -u nobody -- /usr/bin/python3 /opt/lisglosips-s28/lisglosips-s28-sip.py uas --host 100.93.185.30 --port 50620 >/tmp/lisglosips-s28-uas.log 2>&1 &。 - 未解决问题:完整 10 路场景仍在运行中,尚未等待约 12 分钟全程结束;测试会产生真实本地开发环境呼叫事件和少量计费/失败 CDR,使用的是现有 S36 测试客户网关/路由。当前通话页面观察地址为
https://100.90.90.91/。
2026-06-23 18:38 - 需求修改/二期变更:话单详情弹窗布局重排
- 状态:已完成
- 操作服务器:B、本地
- 完成内容:话单中心“查看详情”抽屉内部从单列 KeyValue 堆叠调整为主被叫概览、通话摘要、链路信息、时间轴和录音/信令操作分区;新增专用
cdr-detail系列样式,移动端自动切为单列;补充本地Icon的arrow图标用于主叫到被叫方向展示。 - 修改文件:
apps/web/src/App.jsx、apps/web/src/styles.css、IMPLEMENTATION_STATUS.md;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中已覆盖apps/web/dist。 - 安装软件及固定版本:未安装新软件;继续使用 Node.js
v22.22.2、pnpm10.33.0、Vite5.4.21。 - 执行关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、Btar -xzf覆盖 web dist、nginx -t、systemctl reload nginx、curl -sk https://127.0.0.1/。 - 验证结果:本地 typecheck/lint/build 均通过;B nginx 配置测试通过且 active;首页 HTML 返回新资源
/assets/index-CYrIkAAk.js和/assets/index-TA3aiKvG.css。 - 回滚方式:从 B
/var/backups/lisglosips-s39-cdr-detail-layout/20260623T183751/web-dist恢复当前 release 的apps/web/dist,再执行nginx -t && systemctl reload nginx;本地恢复apps/web/src/App.jsx、apps/web/src/styles.css的本次改动。 - 未解决问题:未做浏览器截图交互验证;本次为纯前端抽屉布局调整,不涉及 API、数据库、Redis 热路径、OpenSIPS、RTPEngine 或 Worker。
2026-06-23 18:29 - 需求修改/二期变更:列表列宽优化与隐藏待设计菜单
- 状态:已完成
- 操作服务器:B、本地
- 完成内容:增强
SimpleTable支持可选列宽和单元格 class;客户管理、客户网关管理、充值记录、供应商管理、落地网关管理页面的 ID/名称类列已适当收窄,并对长文本使用省略号避免撑宽列表;左侧菜单渲染时过滤pending项,隐藏“费率与计费”“SIP 运维”“监控告警”“系统设置”等待设计菜单。 - 修改文件:
apps/web/src/App.jsx、apps/web/src/styles.css、IMPLEMENTATION_STATUS.md;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中已覆盖apps/web/dist。 - 安装软件及固定版本:未安装新软件;继续使用 Node.js
v22.22.2、pnpm10.33.0、Vite5.4.21。 - 执行关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、Btar -xzf覆盖 web dist、nginx -t、systemctl reload nginx、curl -sk https://127.0.0.1/。 - 验证结果:本地 typecheck/lint/build 均通过;B nginx 配置测试通过且 active;首页 HTML 返回新资源
/assets/index-N60ySjGq.js和/assets/index-uhdBdVZH.css。 - 回滚方式:从 B
/var/backups/lisglosips-s38-web-ui-layout/20260623T182343/web-dist恢复当前 release 的apps/web/dist,再执行nginx -t && systemctl reload nginx;本地恢复apps/web/src/App.jsx、apps/web/src/styles.css的本次改动。 - 未解决问题:未做浏览器截图交互验证;本次为纯前端布局/菜单渲染调整,不涉及 API、数据库、Redis 热路径、OpenSIPS、RTPEngine 或 Worker。
2026-06-23 17:47 - 需求修改/二期变更:自研确认弹窗与删除按钮警示色
- 状态:已完成
- 操作服务器:B、本地
- 完成内容:新增通用
ConfirmDialog,按现有 Modal/UI 风格替换原生window.confirm和旧确认弹窗;当前通话强制挂断、客户/供应商/客户网关/落地网关/落地线路组删除、客户网关/落地网关启停、策略删除、抽检规则删除、重置密码确认均使用自研弹窗;所有删除按钮改为danger红色警示态,包括表格删除、编辑抽屉中的删除行按钮以及线路组移除网关按钮。 - 修改文件:
apps/web/src/App.jsx、apps/web/src/styles.css、IMPLEMENTATION_STATUS.md;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中已覆盖apps/web/dist。 - 安装软件及固定版本:未安装新软件;继续使用 Node.js
v22.22.2、pnpm10.33.0、Vite5.4.21。 - 执行关键命令:
rg 'window\.confirm' apps/web/src/App.jsx、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、Btar -xzf覆盖 web dist、nginx -t、systemctl reload nginx、curl -sk https://127.0.0.1/。 - 验证结果:本地 typecheck/lint/build 均通过;
rg 'window\.confirm' apps/web/src/App.jsx无匹配;B nginx 配置测试通过且 active;首页 HTML 返回新资源/assets/index-CWnw3J5O.js和/assets/index-BuNsq8w9.css。 - 回滚方式:从 B
/var/backups/lisglosips-s37-confirm-dialog-ui/20260623T094434/web-dist恢复当前 release 的apps/web/dist,再执行nginx -t && systemctl reload nginx;本地恢复apps/web/src/App.jsx、apps/web/src/styles.css的本次改动。 - 未解决问题:未做浏览器交互截图验证;功能行为已通过构建和页面资源烟测。
2026-06-23 16:20 - 需求修改/二期变更:充值记录过滤、充值性能与新建链路通话测试
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:客户话单消费不再写入
customer_recharges,CDR worker 仅写 raw/rated CDR 并扣客户余额;充值记录 API 过滤历史worker-cdr/cdr:/CDR_CHARGE:客户消费流水;Web 客户/供应商充值成功后改为局部更新余额与充值列表,不再触发全量refreshApi(),避免供应商充值后长时间停留在“拉取API”。完整流程测试时发现 A 热路径仍硬编码成功 CDR 和落地网关,已新增 S28 Lua 动态选择线路组首个启用落地网关,并让 OpenSIPS 成功 CDR 从 dialog 变量写入动态 customer/gateway/policy/vendorGateway/lineGroup;补齐 B/etc/lisglosips/config-publisher.env并启动lisglosips@config-publisher;修复 CDR worker 对超大config_version写 MySQL INT 越界的问题。 - 修改文件:
apps/worker-cdr/src/rating.ts、apps/worker-cdr/src/rating.spec.ts、apps/api/src/modules/recharges/recharges.repository.ts、apps/web/src/App.jsx、infra/server-a/s28/opensips.cfg、infra/server-a/s28/lisglosips_hotpath.lua、IMPLEMENTATION_STATUS.md;B 当前 release 的apps/api/dist、apps/web/dist、apps/worker-cdr/dist已覆盖部署;A/etc/opensips/opensips.cfg与/usr/local/lib/lisglosips/lisglosips_hotpath.lua已更新;B 新增/etc/lisglosips/config-publisher.env。 - 验证结果:本地
corepack pnpm@10.33.0 typecheck通过;corepack pnpm@10.33.0 exec vitest run apps/worker-cdr/src/rating.spec.ts apps/api/src/modules/recharges/recharges.e2e.spec.ts --maxWorkers=1通过;全量corepack pnpm@10.33.0 exec vitest run --maxWorkers=121 个测试文件 74 条测试通过;最终lint与全 workspacebuild通过。B API/api/v2/health/ready正常;客户充值记录按测试客户查询仅 1 条人工充值,未出现 CDR 消费充值;供应商充值接口实测约 818ms,客户充值约 1063ms。Aopensips -C通过并 active;Redis active config 版本1782201896870下auth:ip:100.93.185.30命中新建cgw_zz_s36_20260623160046。完整流程测试创建供应商、落地网关、落地线路组、客户、客户网关、策略、客户充值、供应商充值后,由 T 发起s36-1001 -> 13800136036,当前通话 API 显示callerIp=100.93.185.30、landingIp=100.93.185.30,强制挂断返回HANGUP_IN_PROGRESS,随后当前通话列表清空;最终 CDRraw_66b73108dc1e485ab17c6ac52a9464c1为RATED,rated CDRcustomer_fee=0.012000、vendor_cost=0.012000,测试客户余额从10.000000扣为9.988000。 - 回滚方式:B 应用回滚可从
/var/backups/lisglosips-s36-recharge-flow/20260623T074955恢复api-dist、web-dist、worker-cdr-dist并重启lisglosips@api、lisglosips@cdr-worker;worker 后续补丁可从/var/backups/lisglosips-s36-worker-cdr-config-version/20260623T081403恢复。A OpenSIPS 热路径回滚点包括/var/backups/lisglosips-s36-hotpath/20260623T080010和/var/backups/lisglosips-s36-dialog-cdr/20260623T080956,恢复/etc/opensips/opensips.cfg后执行opensips -C -f /etc/opensips/opensips.cfg并重启opensips;B config-publisher env 回滚点为/var/backups/lisglosips-s36-config-publisher-env/20260623T080456。测试业务数据使用前缀S36 Flow 20260623160046,如需清理,应按 API 软删除客户网关/策略、线路组、落地网关、客户和供应商,不直接删除充值/话单审计记录。 - 遗留问题:测试实体当前保留启用,T 源 IP 会命中新建测试客户网关;若后续要恢复 S28 原始测试路由,应软删除或停用该测试客户网关并等待 config-publisher 发布。两次早期测试呼叫因 A 成功 CDR 动态变量和 worker
config_version越界进入stream:cdr_deadletter,未进入 raw/rated CDR,已作为本次排障证据保留。T 在测试中曾出现一次 SSH banner 超时和一次 ping 丢包,重试后恢复。
2026-06-23 15:36 - 需求修改/二期变更:列表计数、删除功能和启停确认
- 状态:已完成
- 操作服务器:B、本地
- 完成内容:供应商列表显示落地网关数;客户管理、供应商管理前端增加删除按钮和确认弹窗,后端沿用已有软删除并分别在客户网关数/落地网关数大于 0 时拒绝删除。客户网关、落地网关后端新增
DELETE软删除接口并纳入审计;客户网关删除会同步软删该网关下的路由策略并发布配置 outbox;落地网关删除在仍被未删除落地线路组引用时拒绝,避免热配置断链。落地线路组 API 新增customerGatewayCount,按正在使用该线路组的去重客户网关数统计;删除落地线路组时若该数大于 0 则拒绝。Web 客户网关、落地网关启用/禁用前增加确认弹窗;客户网关、落地网关、落地线路组页面增加删除按钮和确认弹窗,并接入真实 API 列表/操作。 - 修改文件:
IMPLEMENTATION_STATUS.md、apps/api/src/modules/customer-gateways/customer-gateways.controller.ts、apps/api/src/modules/customer-gateways/customer-gateways.service.ts、apps/api/src/modules/customer-gateways/customer-gateways.repository.ts、apps/api/src/modules/customer-gateways/customer-gateways.e2e.spec.ts、apps/api/src/modules/vendor-gateways/vendor-gateways.controller.ts、apps/api/src/modules/vendor-gateways/vendor-gateways.service.ts、apps/api/src/modules/vendor-gateways/vendor-gateways.repository.ts、apps/api/src/modules/vendor-gateways/vendor-gateways.e2e.spec.ts、apps/api/src/modules/landing-line-groups/landing-line-groups.repository.ts、apps/api/src/modules/landing-line-groups/landing-line-groups.e2e.spec.ts、apps/web/src/api.js、apps/web/src/App.jsx;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中覆盖 API/Web dist。 - 安装软件及固定版本:未安装新软件;继续使用 B 既有 Node.js
v22.22.2、pnpm10.33.0、NestJS、Vite、Prisma、Nginx。 - 执行的关键命令:本地
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/customer-gateways/customer-gateways.e2e.spec.ts apps/api/src/modules/vendor-gateways/vendor-gateways.e2e.spec.ts apps/api/src/modules/landing-line-groups/landing-line-groups.e2e.spec.ts apps/api/src/modules/customers/customers.e2e.spec.ts --maxWorkers=1、corepack pnpm@10.33.0 exec vitest run apps/api/src/modules/vendors/vendors.e2e.spec.ts --maxWorkers=1;Btar -xzf覆盖当前 release API/Web dist、systemctl restart lisglosips@api、nginx -t、systemctl reload nginx、curl http://127.0.0.1:3000/api/v2/health/ready - 验证结果:typecheck、lint、build 均通过。相关 e2e 验证通过:客户、供应商、客户网关、落地网关、落地线路组;一次组合运行中
vendors.e2e因 Nest 多模块启动超过 10 秒 hookTimeout 被跳过/失败,随后单独运行vendors.e2e3 条测试全部通过。最终全量 Vitest 单 worker 模式 21 个测试文件、74 条测试通过。Blisglosips@api与nginxactive,API ready 返回config/database/redis均 ok。B 只读烟测通过:/vendors返回gatewayCount,/landing-line-groups返回customerGatewayCount;当前本地数据示例ven_s28_t.gatewayCount=1、llg_s28_t.customerGatewayCount=1。 - 回滚方式:B 已备份覆盖前 dist 到
/var/backups/lisglosips-s35-delete-count-confirm/20260623T153500/api-dist与/var/backups/lisglosips-s35-delete-count-confirm/20260623T153500/web-dist;如需回滚,将两目录恢复到/opt/lisglosips/releases/s33-active-calls-20260623094000/apps/api/dist与/opt/lisglosips/releases/s33-active-calls-20260623094000/apps/web/dist,重启lisglosips@api并nginx -t && systemctl reload nginx。本次无数据库 schema migration;如已通过新功能执行业务删除,删除为软删除,需要按具体对象在维护窗口受控恢复deleted_at/status与相关 outbox。 - 未解决问题:落地网关删除目前在被落地线路组引用时会拒绝,比本次需求更保守,目的是避免线路组热配置引用已删除网关;如果后续希望“删除落地网关时自动从线路组移除”,需单独设计优先级重排和配置发布语义。
- 下一任务:继续等待下一项二期需求;阿里云迁移仍不要直接开始。
2026-06-23 15:12 - 需求修改/二期变更:当前通话增加呼叫方 IP 与落地 IP
- 状态:已完成
- 操作服务器:B、T、本地
- 完成内容:在当前通话 API 归一化结果中新增
callerIp与landingIp字段;解析优先级为 SIP Contact、SDPc=IN IP4/IP6、SIP URI、bind addr 兜底。Web “当前通话 / 实时呼叫列表”新增“呼叫方 IP”和“落地 IP”两列。该变更不涉及数据库 schema、Redis 热路径、OpenSIPS 配置、RTPEngine 或 Worker。 - 修改文件:
IMPLEMENTATION_STATUS.md、apps/api/src/modules/active-calls/active-calls.service.ts、apps/api/src/modules/active-calls/active-calls.service.spec.ts、apps/web/src/App.jsx;B 当前 release/opt/lisglosips/releases/s33-active-calls-20260623094000中覆盖 API/Web dist。 - 安装软件及固定版本:未安装新软件;继续使用 B 既有 Node.js
v22.22.2、pnpm10.33.0、NestJS、Vite、Nginx。 - 执行的关键命令:本地
corepack pnpm@10.33.0 exec vitest run apps/api/src/modules/active-calls/active-calls.service.spec.ts --maxWorkers=1、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;Btar -xzf覆盖当前 release API/Web dist、systemctl restart lisglosips@api、nginx -t、systemctl reload nginx、curl http://127.0.0.1:3000/api/v2/health/ready;T/opt/lisglosips-s28/lisglosips-s28-sip.py invite --timeout 20 --hold 60 - 验证结果:当前通话单测 5 条通过;typecheck、lint、build 均通过。B
lisglosips@api与nginxactive,API ready 返回config/database/redis均 ok。A/B/T 端到端验证:T 发起保持呼叫后,B API 查询到当前通话并返回callerIp=100.93.185.30、landingIp=100.93.185.30;当前本地 T 同时模拟主叫与落地 UAS,因此两者相同符合测试拓扑。API 挂断返回HANGUP_IN_PROGRESS,等待 OpenSIPS 删除延迟后当前通话列表清空。 - 回滚方式:B 已备份覆盖前 dist 到
/var/backups/lisglosips-s34-active-call-ips/20260623T150800/api-dist与/var/backups/lisglosips-s34-active-call-ips/20260623T150800/web-dist;如需回滚,将两目录恢复到/opt/lisglosips/releases/s33-active-calls-20260623094000/apps/api/dist与/opt/lisglosips/releases/s33-active-calls-20260623094000/apps/web/dist,重启lisglosips@api并nginx -t && systemctl reload nginx。无数据库、A/OpenSIPS、Redis、RTPEngine 回滚项。 - 未解决问题:真实生产落地网关若不返回 SIP Contact 或 SDP 连接地址,
landingIp会退回到可解析的 URI/bind 地址,可能显示为 OpenSIPS 本地地址;后续可在路由侧显式写入 dialog 变量以获得更精确的落地网关 IP。 - 下一任务:继续等待下一项二期需求;阿里云迁移仍不要直接开始。
2026-06-23 10:15 - 需求修改/二期变更:新增“当前通话”菜单与强制挂断
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:新增 Web “业务 / 当前通话”菜单,可刷新查看 OpenSIPS 当前 Dialog 列表,展示主叫、被叫、Call-ID、状态、时长、联系方式,并提供确认后的“挂断”按钮。B API 新增
GET /api/v2/active-calls与POST /api/v2/active-calls/:id/hangup,使用新增权限active_calls.view、active_calls.manage和审计拦截器;超级管理员具备全部权限,技术运维具备查看/挂断,运营管理员仅查看。A 启用 OpenSIPSdialog.so并在 INVITE 路径执行create_dialog();新增受限 SSH forced-command/usr/local/sbin/lisglosips-call-control,仅允许dlg_list与dlg_end_dlg,MI HTTP 仍绑定本机127.0.0.1:8888。B 新增专用lisglosips服务用户出站 SSH key 与/etc/lisglosips/call-control/ssh_config,API 通过该受限通道访问 A;修复AUTH_COOKIE_SECURE和ACTIVE_CALLS_SSH_HOST空值导致的 B API 启动/调用兼容问题。 - 修改文件:
IMPLEMENTATION_STATUS.md、apps/api/src/modules/active-calls/active-calls.controller.ts、apps/api/src/modules/active-calls/active-calls.module.ts、apps/api/src/modules/active-calls/active-calls.service.ts、apps/api/src/modules/active-calls/active-calls.service.spec.ts、apps/api/src/modules/active-calls/opensips-mi.client.ts、apps/api/src/modules/app.module.ts、apps/api/src/shared/config.ts、apps/web/src/App.jsx、apps/web/src/api.js、packages/auth/src/index.ts、prisma/seed.ts、infra/server-a/s28/opensips.cfg、infra/server-a/s32/lisglosips-call-control;A/etc/opensips/opensips.cfg、A/usr/local/sbin/lisglosips-call-control、B/etc/lisglosips/api.env、B/etc/lisglosips/call-control/*、B release/opt/lisglosips/releases/s33-active-calls-20260623094000 - 安装软件及固定版本:未安装新软件;使用 A 既有 OpenSIPS
3.4.11dialog 模块、B 既有 Node.jsv22.22.2、pnpm10.33.0、NestJS、Vite、Prisma、MySQL、Redis、Nginx。 - 执行的关键命令:本地
corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 exec vitest run --maxWorkers=1、corepack pnpm@10.33.0 build;Aopensips -C -f、systemctl restart opensips、systemctl is-active opensips rtpengine;Bsystemctl start lisglosips-backup.service、corepack pnpm@10.33.0 db:seed、systemctl restart lisglosips@api、nginx -t、systemctl reload nginx、curl http://127.0.0.1:3000/api/v2/health/ready;T/opt/lisglosips-s28/lisglosips-s28-sip.py invite --timeout 20 --hold 60 - 验证结果:本地 lint 通过;typecheck 通过;全量 Vitest 单 worker 模式 21 个测试文件、73 条测试通过;workspace build 通过。A OpenSIPS 配置语法检查通过并已重启,
opensips、rtpengineactive;Blisglosips@api、nginx、mysql、redis-serveractive,API ready 返回config/database/redis均 ok。未登录访问/api/v2/active-calls返回 401;登录态访问空闲列表返回source=opensips-mi,total=0。A/B/T 端到端验收:T 发起保持呼叫后,B API 查询到 1 条当前通话;调用 B API 挂断返回HANGUP_IN_PROGRESS,OpenSIPS Dialog 状态进入5,等待删除延迟后 B API 当前通话列表清空。 - 回滚方式:B 应用回滚可将
/opt/lisglosips/current切回上一 release/opt/lisglosips/releases/s32-captcha-admin-20260622124659,重启lisglosips@api并 reload Nginx;如需还原 B 环境变量,使用/var/backups/lisglosips-s33-active-calls-b/20260623T014402Z/api.env。A 回滚可从/var/backups/lisglosips-s33-active-calls/20260623T014315Z恢复/etc/opensips/opensips.cfg后重启opensips,并移除/usr/local/sbin/lisglosips-call-control、lisglo-callctl用户/authorized_keys 以及 B/etc/lisglosips/call-control专用 key。数据库无 schema migration;新增权限/角色授权来自 seed,可保留,若需清理需维护窗口执行受控 SQL。B 发布前 MySQL/Redis 备份为/data/backups/mysql/20260623T014114Z与/data/backups/redis/20260623T014114Z。 - 未解决问题:OpenSIPS HTTP MI 的
dlg_end_dlg在实际终止 Dialog 时会返回500 Operation failed,但 Dialog 会进入 state5并随后清空;API 已按“500 后复查消失或 state 5 则视为已受理”处理。当前通话列表依赖 OpenSIPS dialog 模块,只覆盖本地 KVM A 单节点;未来多 OpenSIPS 节点需按节点聚合。强制挂断会影响被选中的实时呼叫,操作已纳入 RBAC 与审计,生产迁移前需再次演练。 - 下一任务:继续等待下一项二期需求;阿里云迁移仍不要直接开始。
2026-06-22 14:20 - 需求修改/二期变更:修复登录验证码刷新后不显示
- 状态:已完成,等待用户浏览器复验
- 操作服务器:B、本地
- 完成内容:定位验证码接口返回的是
data:image/svg+xml;base64,...SVG 图片,而 B Nginx 安全头Content-Security-Policy未显式允许img-src data:,浏览器会拦截验证码图片渲染,表现为“验证码刷新不出来”。已将仓库和 B 当前 Nginx 安全头更新为default-src 'self'; img-src 'self' data:; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; object-src 'none',保留原有同源、禁止嵌入和禁止 object 的安全基线。 - 修改文件:
IMPLEMENTATION_STATUS.md、infra/server-b/s05/nginx/lisglosips-security-headers.conf;B/etc/nginx/snippets/lisglosips-security-headers.conf - 安装软件及固定版本:未安装新软件;继续使用 B 既有 Nginx
1.24.0-2ubuntu7.12和当前 release/opt/lisglosips/releases/s32-captcha-admin-20260622124659 - 执行的关键命令:本地
corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 prisma:validate、corepack pnpm@10.33.0 exec vitest run --maxWorkers=1、corepack pnpm@10.33.0 build;Bcp -a备份 Nginx 安全头、nginx -t、systemctl reload nginx、curl -k -sI https://127.0.0.1/、curl http://127.0.0.1:3000/api/v2/auth/captcha - 验证结果:本地 lint、typecheck、Prisma validate 均通过;全量 Vitest 单 worker 模式 20 个测试文件、69 条测试通过;workspace build 通过。B 当前 release 确认为
/opt/lisglosips/releases/s32-captcha-admin-20260622124659;nginx -t通过并已 reload;HTTPS 首页响应头已返回Content-Security-Policy: default-src 'self'; img-src 'self' data:; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; object-src 'none';GET http://127.0.0.1:3000/api/v2/auth/captcha返回新的captchaId、data:image/svg+xml;base64,...图片和过期时间;Bnginx、lisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker均 active。本次未做 A/B/T SIP 呼叫链路,因为变更只涉及 B Web 安全头,不影响 Redis 热路径、OpenSIPS、RTPEngine、CDR 或录音链路。 - 回滚方式:B 已备份原安全头到
/var/backups/lisglosips-s32-captcha-csp/20260622T061944Z/lisglosips-security-headers.conf;如需回滚,恢复该文件到/etc/nginx/snippets/lisglosips-security-headers.conf,执行nginx -t通过后systemctl reload nginx。仓库侧回滚infra/server-b/s05/nginx/lisglosips-security-headers.conf的本次一行 CSP 修改即可。无数据库、Redis、服务 release、OpenSIPS 或 RTPEngine 回滚项。 - 未解决问题:尚未由用户在真实浏览器中点击验证码刷新并确认图片可见;验证码仍为 API 单进程内存存储,后续多 API 实例/生产高可用建议迁移到 Redis 或共享缓存。
- 下一任务:用户浏览器复验 B 登录页验证码刷新;继续等待下一项二期需求。
2026-06-22 12:54 - 需求修改/二期变更发布到 B:登录图形验证码与 admin 初始账号
- 状态:已完成,等待用户验收
- 操作服务器:B、本地
- 完成内容:在登录接口新增一次性图形验证码,
GET /api/v2/auth/captcha生成 5 位 SVG 验证码并只返回captchaId、图片 data URL 和过期时间;POST /api/v2/auth/login必须携带captchaId和captchaCode,验证码错误返回401 AUTH_CAPTCHA_INVALID,缺少登录字段继续返回统一认证失败以降低账号枚举风险。Web 登录页新增验证码图片、输入框和刷新按钮,登录失败后自动刷新验证码。B 数据库已新增/更新用户名admin,启用状态,授予超级管理员角色,并按用户提供的初始口令写入 Argon2id 哈希;未在文档或命令输出中记录明文口令。发布为 B 新 release/opt/lisglosips/releases/s32-captcha-admin-20260622124659,/opt/lisglosips/current已切换到该 release。 - 修改文件:
IMPLEMENTATION_STATUS.md、apps/api/src/modules/auth/auth.controller.ts、apps/api/src/modules/auth/auth.module.ts、apps/api/src/modules/auth/captcha.service.ts、apps/api/src/modules/auth/auth.e2e.spec.ts、apps/web/src/App.jsx、apps/web/src/api.js、apps/web/src/styles.css;B 新 release 中覆盖 API/Web dist 与对应源码。延续上一轮登录防爆破相关文件:apps/api/src/modules/auth/auth.service.ts、apps/api/src/modules/auth/auth.service.spec.ts、apps/api/src/shared/config.ts。 - 安装软件及固定版本:未安装新软件;继续使用 B 既有 Node.js
v22.22.2、pnpm10.33.0、NestJS、Vite、Prisma、Nginx、Grafana13.0.2、Prometheus2.45.3。 - 执行的关键命令:本地
corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 prisma:validate、corepack pnpm@10.33.0 exec vitest run --maxWorkers=1、corepack pnpm@10.33.0 build、tar -czf打包、scp上传;Bsystemctl start lisglosips-backup.service、cp -a创建新 release、tar --no-same-owner -xzf覆盖产物、ln -sfn切换 current、systemctl restart lisglosips@api、nginx -t、systemctl reload nginx、/tmp/lisglosips-release-preflight.sh;B 通过临时脚本按用户提供口令 upsertadmin用户并撤销旧会话。 - 验证结果:本地 lint、typecheck、Prisma validate、build 均通过;全量 Vitest 单 worker 模式 20 个测试文件、69 条测试通过,新增认证 e2e 覆盖 captcha 接口不泄露答案、无验证码登录被拒绝、携带正确验证码后可登录。B 发布前备份目录为 MySQL
/data/backups/mysql/20260622T044834Z、Redis/data/backups/redis/20260622T044834Z,两者SHA256SUMS校验通过。B preflight 通过:MySQL、Redis、Nginx、API、CDR Worker、Recording Worker、Heplify、Prometheus、Grafana 均 active,API ready 返回 ok,Nginx 配置测试成功。GET http://127.0.0.1:3000/api/v2/auth/captcha返回 captcha JSON 与 SVG data URL 且不含答案;使用错误验证码调用登录返回401 AUTH_CAPTCHA_INVALID;https://127.0.0.1/返回新前端构建资产index-B6ms7F5U.js与index-C60rDiFV.css。 - 回滚方式:如需回滚应用,按
docs/S30_RELEASE_BACKUP_RUNBOOK.md将 B/opt/lisglosips/current切回上一稳定 release/opt/lisglosips/releases/s31-auth-login-20260622115851或原冻结 release/opt/lisglosips/releases/s28-v2-20260621220924,重启lisglosips@api并 reload Nginx。数据库只新增/更新admin用户密码哈希和会话撤销记录;如需回滚账号变更,可在维护窗口按 B 发布前 MySQL 备份/data/backups/mysql/20260622T044834Z恢复,或单独执行受控账号禁用/密码重置脚本,执行前需再次说明影响和回滚点。 - 未解决问题:尚未由用户在浏览器中完成真实登录验收;验证码当前为单 API 进程内存存储,适配当前 B 单实例开发环境,后续多 API 实例/生产高可用应迁移到 Redis 或共享缓存。登录防爆破仍为进程内存级节流,生产多实例同样建议升级为 Redis/网关级共享限流。本次未做 A/B/T SIP 呼叫链路,因为变更只涉及 Web/API 登录鉴权和 B 用户数据,不影响 Redis 热路径、OpenSIPS、RTPEngine、CDR 或录音链路。
- 下一任务:用户验收 B 登录页;继续等待下一项二期需求。
2026-06-22 12:04 - 需求修改/二期变更发布到 B:登录页与登录防爆破
- 状态:已完成,等待用户验收
- 操作服务器:B、本地
- 完成内容:将本地登录页与登录防爆破变更发布为 B 新 release
/opt/lisglosips/releases/s31-auth-login-20260622115851,/opt/lisglosips/current已切换到该 release。发布方式为复制上一 release/opt/lisglosips/releases/s28-v2-20260621220924后覆盖本次 API/Web 构建产物和相关源码;未执行数据库迁移,未改 Redis 配置,未触碰 A/T、OpenSIPS 或 RTPEngine。发布前触发 B MySQL/Redis 备份并校验哈希。 - 修改文件:本地
IMPLEMENTATION_STATUS.md;B 新 release 中的apps/api/dist/、apps/api/src/modules/auth/auth.service.ts、apps/api/src/modules/auth/auth.service.spec.ts、apps/api/src/shared/config.ts、apps/web/dist/、apps/web/src/App.jsx、apps/web/src/api.js、apps/web/src/styles.css - 安装软件及固定版本:未安装新软件;继续使用 B 既有 Node.js
v22.22.2、pnpm10.33.0、NestJS、Vite、Prisma、Nginx、Grafana13.0.2、Prometheus2.45.3 - 执行的关键命令:本地
corepack pnpm@10.33.0 build、tar -czf打包发布覆盖包、scp上传;Bsystemctl start lisglosips-backup.service、cp -a创建新 release、tar --no-same-owner -xzf覆盖产物、ln -sfn切换 current、systemctl restart lisglosips@api、nginx -t、systemctl reload nginx、/tmp/lisglosips-release-preflight.sh - 验证结果:发布前备份目录为 MySQL
/data/backups/mysql/20260622T040339Z、Redis/data/backups/redis/20260622T040339Z,两者SHA256SUMS校验通过。B preflight 通过:MySQL、Redis、Nginx、API、CDR Worker、Recording Worker、Heplify、Prometheus、Grafana 均 active,API ready 返回 ok,Nginx 配置测试成功。GET http://127.0.0.1:3000/api/v2/health/ready返回 ok;未登录访问/api/v2/dashboard/summary返回401 AUTH_REQUIRED;https://127.0.0.1/返回新前端构建资产index-7KkZS1Fz.js与index-ChBVfysf.css。Grafana healthhttp://127.0.0.1:3001/api/health返回 database ok/version13.0.2,Prometheus/-/ready返回 ready。 - 回滚方式:B 应用可执行
sudo /tmp/lisglosips-release-preflight.sh复核状态;如需回滚,按docs/S30_RELEASE_BACKUP_RUNBOOK.md将/opt/lisglosips/current切回/opt/lisglosips/releases/s28-v2-20260621220924,重启lisglosips@api并 reload Nginx;数据库和 Redis 未变更,备份目录仅作为发布前恢复点保留。 - 未解决问题:尚未由用户在浏览器中完成真实登录验收;本次未做 A/B/T SIP 呼叫链路,因为变更只涉及 Web/API 登录鉴权,不影响 Redis 热路径、OpenSIPS、RTPEngine、CDR 或录音链路。后续多 API 实例/生产环境仍建议把登录节流升级为 Redis 或网关级共享限流。
- 下一任务:用户验收登录页面;继续等待下一项二期需求。
2026-06-22 11:53 - 需求修改/二期变更:登录页与登录防爆破
- 状态:已完成
- 操作服务器:本地
- 完成内容:完善 Web 登录闭环,打开应用时先尝试
/api/v2/auth/refresh恢复会话,未认证时只显示登录页面;输入用户名和密码调用/api/v2/auth/login,登录成功后保存 access token 并加载业务数据;顶部显示当前登录用户并支持退出,退出时调用/api/v2/auth/logout、清理本地 token 和页面数据。后端在既有 Argon2id、用户失败次数和账号锁定基础上,新增内存级登录节流,按用户名与来源 IP 统计失败次数;即使用户名不存在,连续失败也会触发429 AUTH_LOGIN_THROTTLED,避免撞库/爆破时泄露账号存在性。新增运行时配置AUTH_LOGIN_THROTTLE_MAX_FAILURES和AUTH_LOGIN_THROTTLE_WINDOW_SECONDS。 - 修改文件:
IMPLEMENTATION_STATUS.md、apps/web/src/App.jsx、apps/web/src/api.js、apps/web/src/styles.css、apps/api/src/shared/config.ts、apps/api/src/modules/auth/auth.service.ts、apps/api/src/modules/auth/auth.service.spec.ts - 安装软件及固定版本:未安装新软件;继续使用 Node.js/pnpm、NestJS、Vite、Vitest、Prisma 既有版本。
- 执行的关键命令:本地
corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 typecheck、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、corepack pnpm@10.33.0 exec vitest run --maxWorkers=1、corepack pnpm@10.33.0 prisma:validate、corepack pnpm@10.33.0 build - 验证结果:lint 通过;typecheck 通过;认证单测/e2e 6 条通过;全量 Vitest 使用
--maxWorkers=1后 20 个测试文件、69 条测试全部通过;Prisma schema validate 通过;workspace build 通过。首次全量并行测试曾出现多个 Nest e2ebeforeAll超时,改为单 worker 后全部通过,判断为本地并行资源/初始化竞争导致的假失败。 - 回滚方式:恢复上述 7 个文件到本次修改前版本即可;无数据库 schema 变更、无服务器写操作、无 Redis/OpenSIPS/RTPEngine 配置变更。若已发布到 B,可按
docs/S30_RELEASE_BACKUP_RUNBOOK.md使用上一 release 切回并重启 API/Web 相关服务。 - 未解决问题:本次只完成本地代码修改,尚未发布到 B 当前冻结 release,也未做浏览器视觉截图或 A/B/T 端到端验收;登录节流为单进程内存级,足以覆盖当前单 API 进程开发环境,后续多 API 实例或生产环境建议迁移到 Redis/网关级限流以跨进程共享计数。
- 下一任务:等待下一个二期需求;如需上线本变更,应按 S30 发布流程创建新 B release 并做 preflight/登录冒烟。
2026-06-22 10:35 - S30 备份、Runbook、灰度与上线完成
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:完成 S30 本地 KVM 开发环境上线前冻结与演练。新增 S30 总控 Runbook、最终验收报告、B release preflight/rollback 脚本和 A OpenSIPS 配置恢复脚本;触发 B MySQL/Redis 新备份并完成隔离恢复演练;使用 T 发起灰度呼叫并核对 B raw CDR、rated CDR 和 READY 录音;实际运行 B preflight;A OpenSIPS 当前配置语法检查通过;三机关键服务最终复核 active。
- 修改文件:
IMPLEMENTATION_STATUS.md、docs/S30_RELEASE_BACKUP_RUNBOOK.md、docs/S30_FINAL_ACCEPTANCE_REPORT.md、infra/server-b/s30/lisglosips-release-preflight.sh、infra/server-b/s30/lisglosips-release-rollback.sh、infra/server-a/s30/lisglosips-opensips-config-restore.sh - 安装软件及固定版本:未安装新系统软件;继续使用 OpenSIPS
3.6.x、RTPEngine11.5.1.18-1ubuntu1.2、Node.jsv22.22.2、pnpm10.33.0、Prisma6.19.0、MySQL8.0.46、Redis7.0.15 - 执行的关键命令:B
systemctl start lisglosips-backup.service、MySQL 备份恢复到临时库lisglosips_s30_restore、Redis RDB 恢复到隔离 Unix socket 临时实例、bash -n检查 S30 脚本、/tmp/lisglosips-release-preflight.shpreflight;Aopensips -C -f /etc/opensips/opensips.cfg;T/opt/lisglosips-s28/lisglosips-s28-sip.py invite --timeout 12 --hold 3。所有 sudo/数据库/API secret 均从.codex-private/或远端 env 读取并只在进程内使用,未写入文档。 - 验证结果:MySQL 备份
/data/backups/mysql/20260622T022811Z的all-databases.sql.gz、lisglosips.sql.gz、metadata.tsv校验通过,临时库恢复计数raw_cdrs=33、recordings=27、customer_recharges=34、users=2后已删除;Redis 备份/data/backups/redis/20260622T022812Z的dump.rdb、metadata.txt校验通过,隔离实例核对DBSIZE=51、stream:cdr_payload=54、cfg:active_version=s28-v1后已关闭并清理。灰度 Call-IDs28-1782095548702-luppqjd4@lisglosips-t收到 SIP100/200/BYE 200,B 生成 rawraw_7a6356cc76974652aa30a9d7a350041b、ratedrated_e0eb8cbe1b90454cb3cf2d9a3bbc9290、billSec=6、客户费用/供应商成本均0.012000、录音rec_5fdf599d679f493a83fc7a2d205a128a为READY且 55758 bytes。B preflight 通过,A/B/T 关键服务最终 active,S30 文档/脚本未检出待补充或明文 secret。 - 回滚方式:B 应用按
docs/S30_RELEASE_BACKUP_RUNBOOK.md使用infra/server-b/s30/lisglosips-release-rollback.sh /opt/lisglosips/releases/<previous-release>切回上一 release;A OpenSIPS 配置按infra/server-a/s30/lisglosips-opensips-config-restore.sh <backup-opensips.cfg>先语法检查再恢复;Redis 配置切回上一cfg:active_version,不删除 Stream;数据库优先向前修复迁移,只有完整备份和维护窗口下才恢复数据。S30 产生的测试 CDR、录音和备份作为验收证据保留。 - 未解决问题:本地开发环境不是阿里云生产环境;正式迁移前必须重新完成 VPC/安全组、正式 DNS/TLS、独立数据盘、备份恢复、故障注入和端到端呼叫验收。S29 遗留 Redis 恢复后 OpenSIPS 热路径可能需要重启
opensips的根因级修复仍需后续单独处理;真实多 A、多 B、高可用和完整生产路由不属于本地 S30 闭环。 - 下一任务:无;S00-S30 本地 V2 闭环完成,后续应按 S30 Runbook 进入阿里云迁移/生产化专项
2026-06-22 10:25 - S29 性能、故障与安全测试完成
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:完成本地 KVM 开发环境内的 S29 小规模性能、故障与安全测试。5 路并发和 12 路短突发呼叫均完成 SIP 100/200/BYE 200、CDR、计费、录音 READY 入库;Recording Worker 停止期间的录音恢复、CDR Worker 停止期间的 Redis pending 恢复均通过;Redis 停止期间 A 正确返回
503 Redis Unavailable,恢复后发现 OpenSIPS 热路径连接不自动恢复,已通过重启 Aopensips做运行时恢复并复测成功;MySQL 停止期间发现 CDR 被 deadletter 的可恢复性缺陷,已修复并部署到 B 当前 release,复测 MySQL 恢复后 pending CDR 自动入库且无新增 deadletter;完成 API 未认证、RBAC 越权、录音播放保护、充值幂等重放和 SIP 非法来源探针。S29 报告已写入docs/S29_PERFORMANCE_FAULT_SECURITY_REPORT.md。 - 修改文件:
IMPLEMENTATION_STATUS.md、docs/S29_PERFORMANCE_FAULT_SECURITY_REPORT.md、infra/server-t/s28/lisglosips-s28-sip.py、packages/redis/src/cdr-stream.ts、packages/redis/src/cdr-stream.spec.ts、apps/worker-cdr/src/main.ts、apps/worker-cdr/src/rating.ts;T 的/opt/lisglosips-s28/lisglosips-s28-sip.py;B 当前 release/opt/lisglosips/releases/s28-v2-20260621220924/apps/worker-cdr/与/opt/lisglosips/releases/s28-v2-20260621220924/packages/redis/ - 安装软件及固定版本:未安装新系统软件;继续使用 OpenSIPS
3.6.x、RTPEngine11.5.1.18-1ubuntu1.2、Node.jsv22.22.2、pnpm10.33.0、Prisma6.19.0 - 执行的关键命令:本地
corepack pnpm@10.33.0 exec vitest run packages/redis/src/cdr-stream.spec.ts apps/worker-cdr/src/rating.spec.ts、corepack pnpm@10.33.0 --filter @lisglosips/redis build、corepack pnpm@10.33.0 --filter @lisglosips/worker-cdr build;T 多路/opt/lisglosips-s28/lisglosips-s28-sip.py invite --timeout 10 --hold 1/3;B/A 使用systemctl stop/start/restart做受控故障注入和恢复;B 通过 API 与 Prisma 只读/小额写入探针核对 RBAC、录音播放保护和充值幂等。所有 sudo/数据库/API secret 均从.codex-private/或远端 env 读取并只在进程内使用,未写入文档。 - 验证结果:5 路并发生成 5 条 raw CDR、5 条 rated CDR、5 条 READY 录音;12 路短突发生成
raws=12、rated=12、recordingsReady=12、totalDuration=72、录音大小55758-56078bytes;Recording Worker 恢复后补搬运录音;CDR Worker 恢复后 Redispending=0、lag=0并入库;MySQL 故障修复后复测 Call-IDs28-1782093477852-a8bqqohb@lisglosips-t自动入库 raw/rated CDR,无新增 deadletter;API/api/v2/users未认证返回401 AUTH_REQUIRED,质检角色访问用户管理返回403 RBAC_FORBIDDEN,录音播放未认证返回401 AUTH_REQUIRED,伪造录音 ID 播放返回404 RECORDING_NOT_READY;充值重放两次返回同一充值 IDrch_da0bba1c7b1d44428ab92d77a55e503d,数据库customer_recharges.rows=1、idempotency_keys.rows=1,余额只增加0.000001一次;B 实际 Prometheus 服务lisglosips-prometheus.serviceactive 且 ready;A/B/T 关键服务复核 active。 - 回滚方式:T 可恢复 S29 前
/opt/lisglosips-s28/lisglosips-s28-sip.py或继续使用修复后的动态端口脚本;B 可用本次部署前保留在/tmp/s29-worker-cdr-backup-*与/tmp/s29-redis-backup-*的备份恢复 worker-cdr 与 packages/redis 后重启lisglosips@cdr-worker;A Redis 故障恢复只做过服务重启,无配置文件改动。S29 产生的测试 CDR、录音、充值、幂等键和测试用户作为审计证据保留,不直接删除;如需清理应单独说明影响与回滚点。 - 未解决问题:Redis 恢复后 A/OpenSIPS 热路径不会自动从
Redis Unavailable恢复,当前运行时修复是重启 Aopensips,根因级修复需继续排查 OpenSIPS Redis 连接池/重连策略;SIP 非法来源探针表现为无响应且未进入 OpenSIPS 日志,后续建议增加明确拒绝日志/指标;S29 期间 Tailscale 管理链路出现短时高延迟/丢包,记录为开发环境风险;真实多 A、多 B 和完整生产路由仍不在本任务范围。 - 下一任务:
S30 - 备份、Runbook、灰度与上线,尚未开始
2026-06-22 09:32 - S28 三机端到端联调完成
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:补齐 S28 剩余录音与质检闭环。T 呼叫脚本新增双向 PCMU RTP 发送,UAS 在 200 OK 后向 A/RTPEngine 回送 RTP,主叫侧在 ACK 后向应答 SDP 媒体地址发送 RTP;A 的 S28 OpenSIPS 路由启用
record-call=on,保持 INVITE/ACK/BYE 经原 A,仍绑定本机 RTPEngine;修正 A/dev/shm/voip_rec历史 root-owned 日期目录权限;修复 A recording finalizer 过早搬运问题,跳过 0 字节、仍被打开或未稳定的录音文件;B Recording Worker 新增按 RTPEngine 文件名反解 Call-ID 的 fallback 关联逻辑,使录音可自动关联raw_cdrs,从而让质检抽样按客户/线路组命中。新增 S28 质检测试用户usr_s28_quality、抽检规则qs_s28_e2e_20260622,并通过 API 保存最新录音质检记录。 - 修改文件:
infra/server-t/s28/lisglosips-s28-sip.py、infra/server-a/s28/opensips.cfg、infra/server-a/s19/scripts/lisglosips-recording-finalize、apps/worker-recording/src/transfer.ts、apps/worker-recording/src/transfer.spec.ts、IMPLEMENTATION_STATUS.md;A 的/etc/opensips/opensips.cfg、/usr/local/sbin/lisglosips-recording-finalize和/dev/shm/voip_rec目录属主/权限;T 的/opt/lisglosips-s28/lisglosips-s28-sip.py;B 当前 release/opt/lisglosips/releases/s28-v2-20260621220924/apps/worker-recording/的源码与 dist 产物 - 安装软件及固定版本:未安装新系统软件;继续使用 OpenSIPS
3.6.x、RTPEngine11.5.1.18-1ubuntu1.2、Node.jsv22.22.2、pnpm10.33.0、Prisma6.19.0 - 执行的关键命令:本地
corepack pnpm@10.33.0 exec vitest run apps/worker-recording/src/transfer.spec.ts、corepack pnpm@10.33.0 --filter @lisglosips/worker-recording build;Aopensips -C -f /tmp/opensips-s28.rendered.cfg后重启opensips,更新 finalizer 后保持 timer active;T/opt/lisglosips-s28/lisglosips-s28-sip.py invite --timeout 10 --hold 3;B 替换 worker-recording 产物并重启lisglosips@recording-worker,通过 MySQL 和 API 只读/写入测试核对。所有 sudo/数据库/API secret 均从.codex-private/或远端 env 读取并只在进程内使用,未输出或写入明文密码。 - 验证结果:最终验收 Call-ID
s28-1782091725991-ewqrpe7w@lisglosips-t;T 收到100 Giving it a try、200 OK、BYE200 OK;Braw_cdrs生成raw_aa6cbaf7758f41ea895a2889f558559d,duration_sec=6、rating_status=RATED;rated_cdrs对应客户费用和供应商成本均为0.012000、bill_sec=6;Brecordings生成rec_24b56d60dfd14937ae2e4c09802a91be,自动关联该 raw CDR,status=READY、bytes=55758、SHA-256 前缀e2cfaf8c7bbc6023;API/api/v2/health/ready返回 ok,GET /api/v2/recordings/rec_24b56d60dfd14937ae2e4c09802a91be返回 customer/lineGroup/callId/CDR 摘要,抽样规则qs_s28_e2e_20260622命中selected=true,PUT /api/v2/recordings/:id/review成功保存qrev_9672ee20119049a1a56082c35dc42e0f,GET /play返回200、content-type: audio/wav和内部x-accel-redirect。Aopensips、rtpengine-daemon、rtpengine-recording-daemon、lisglosips-recording-finalize.timeractive;Blisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker、mysql、redis-server、heplify-serveractive;Tlisglosips-s28-uas、opensipsactive。 - 回滚方式:T 回滚恢复或删除
/opt/lisglosips-s28/lisglosips-s28-sip.py,停止lisglosips-s28-uas.service;A 回滚可用/var/backups/lisglosips-s28/20260621T145920Z/opensips.cfg恢复/etc/opensips/opensips.cfg并语法检查后重启opensips,finalizer 可恢复 S24 备份/var/backups/lisglosips-s24/20260621T121130Z/lisglosips-recording-finalize,但旧脚本会重现提前搬运风险;B 回滚可将 worker-recording 产物恢复到本次替换前的 release 版本或把/opt/lisglosips/current指回前一 release 后重启相关服务。测试 CDR、余额流水、录音和质检记录作为验收证据保留,不直接删除;如必须清理,应另开维护步骤并先说明影响与回滚点。 - 未解决问题:A 当前仍是 S28 单一测试落地端最小路由,不是完整多落地网关生产路由;录音
duration_sec仍为 0,后续可在 Recording Worker 中读取 WAV 时长或由媒体侧补充;S28 调试期间存在一条 0 字节早期录音rec_f9b1457861444d4ebbddc11adb208d13作为问题证据保留;多 A 真实部署和多 B 高可用仍留给后续阶段。 - 下一任务:S29 - 性能、故障与安全测试
2026-06-21 23:06 - S28 三机端到端联调推进
- 状态:进行中
- 操作服务器:A、B、T、本地
- 完成内容:将 B 从 S05 placeholder 发布为完整 V2 release
/opt/lisglosips/releases/s28-v2-20260621220924,/opt/lisglosips/current已切换到该 release;执行业务库迁移,补齐20260621093000_auth_sessionsadditive 迁移记录,权限/角色种子已落库;lisglosips@api和lisglosips@cdr-workeractive,API/api/v2/health/live与/api/v2/health/ready返回 ok;补齐 B 录音拉取/etc/lisglosips/recording/ssh_config,lisglosips@recording-worker已恢复 active。新增 S28 测试业务数据:客户cus_s28_t、客户网关cgw_s28_t_ip、供应商ven_s28_t、落地网关vgw_s28_t_uas、线路组llg_s28_t和策略cgp_s28_t_default;Redis 热路径配置版本为s28-v1。在 T 部署专用落地模拟服务lisglosips-s28-uas.service,监听100.93.185.30:50620,只终结呼叫不回送 A,避免循环。A 部署 S28 最小真实路由:Lua 热路径 allow 后执行 RTPEngine offer/answer、转发到 T 专用落地端、写成功 CDR 到 Redis Stream,并为 T 源 IP 的 ACK/BYE 添加 fallback,确保回到原 A 后转给 T 落地端。 - 修改文件:
infra/server-t/s28/lisglosips-s28-sip.py、infra/server-a/s28/opensips.cfg、pnpm-lock.yaml、IMPLEMENTATION_STATUS.md;B 的/opt/lisglosips/releases/s28-v2-20260621220924、/etc/lisglosips/api.env、/etc/lisglosips/cdr-worker.env、/etc/lisglosips/recording-worker.env、/etc/lisglosips/recording/ssh_config、/etc/systemd/system/lisglosips@recording-worker.service.d/10-s28-recording.conf;T 的/opt/lisglosips-s28/lisglosips-s28-sip.py和/etc/systemd/system/lisglosips-s28-uas.service;A 的/etc/opensips/opensips.cfg - 安装软件及固定版本:未安装新系统软件;B 继续使用 Node.js
v22.22.2、Corepack0.34.6、pnpm10.33.0、Prisma6.19.0 - 执行的关键命令:本地
corepack pnpm@10.33.0 install --lockfile-only、corepack pnpm@10.33.0 build、corepack pnpm@10.33.0 prisma:validate、corepack pnpm@10.33.0 fetch;B 端离线pnpm install --frozen-lockfile --offline --ignore-scripts、MySQL 迁移/种子、systemd restart/enable;Aopensips -C -f /tmp/opensips-s28.rendered.cfg后重启opensips;T 启动lisglosips-s28-uas.service。凭据均从.codex-private/读取并通过 stdin/base64 参数传递,未写入文档或输出明文密码。 - 验证结果:最终候选呼叫 Call-ID
s28-1782054233152-f4eiz799@lisglosips-t;T 收到100 Giving it a try、200 OK,随后 BYE 返回200 OK;A 日志显示 hotpath allow、成功 CDR XADD1782054232944-0、ACK/BYE 均从 T 回到 A;RTPEngine session 最终回到 0;B MySQLraw_cdrs生成raw_2903404fa10b47a6a4b7968e3662cdd5,sip_code=200、hangup_reason=NORMAL_CLEARING、customer_id=cus_s28_t、vendor_gateway_id=vgw_s28_t_uas、duration_sec=6;rated_cdrs生成客户费用/供应商成本0.012000、计费秒数6;客户cus_s28_t余额扣减到99.928000(包含本次和前面 S28 调试成功 CDR);HOMERhep_proto_1_call可查到该 Call-ID 的INVITE、ACK、BYE;B API ready ok,lisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker均 active;T S28 UAS active。 - 回滚方式:B 回滚先停止
lisglosips@cdr-worker和lisglosips@recording-worker,将/opt/lisglosips/current指回备份记录中的 S05 placeholder release,并重启lisglosips@api;B 迁移前备份位于/var/backups/lisglosips-s28/20260621T140940Z,如需撤回数据库写入应走备份恢复或受控反向账务处理,不直接删除 CDR/余额流水。A 回滚使用/var/backups/lisglosips-s28/20260621T145920Z/opensips.cfg恢复/etc/opensips/opensips.cfg,语法检查后重启opensips。T 回滚停止并删除lisglosips-s28-uas.service和/opt/lisglosips-s28。Redis 配置可将cfg:active_version切回cfg:previous_version或清理s28-v1测试 keys,清理前需确认没有后续测试依赖。 - 未解决问题:本次 T 脚本只做 SIP/SDP,不发送 RTP 音频包,因此 RTPEngine 未产生真实录音文件;Recording Worker 虽已 active,但尚未完成真实通话
.ready文件搬运、recordings入库和质检抽样/保存核对。S28 仍需补“RTP 音频生成 -> A 录音 ready -> B Recording Worker 入库 -> 质检接口核对”后才能标记已完成。当前 A 配置中的 S28 路由是单一测试落地端 fallback,不是完整多落地网关生产路由。 - 下一任务:继续
S28 - 三机端到端联调,补齐录音和质检闭环;不得进入 S29。
2026-06-21 21:47 - S28 三机端到端联调
- 状态:已阻塞
- 操作服务器:A、B、T;仅执行一次 S28 测试 INVITE 和只读核对,未修改服务器配置,未重启服务,未执行数据库迁移、数据删除或防火墙变更
- 完成内容:按 S28 目标先做三机链路探针。T 使用 S21 脚本向 A
100.90.90.90:15060发起 IP 认证 INVITE,Call-ID 为s21-invite-1782049439554-088rul75@lisglosips-t;A 返回SIP/2.0 503 Config Missing;A OpenSIPS 日志确认热路径拒绝原因为CONFIG_MISSING,并成功向 B Redis Stream 写入失败 CDR;B Redis Streamstream:cdr_payload可按 Call-ID 查到该事件,字段包含node_id=a1、opensips_instance=opensips-a1、ingress_a_ip=100.90.90.90、rtpengine_node=a1、sip_code=503、hangup_reason=CONFIG_MISSING;B HOMERhep_proto_1_call最近记录的sid与data_header.callid均为该 Call-ID,data_header.method=INVITE,说明 HEP 信令镜像入库正常。 - 修改文件:
IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件
- 执行的关键命令:T
/opt/lisglosips-s21/s21-call-a-ip;Ajournalctl -u opensips --since '10 minutes ago'按 Call-ID 查询;Bredis-cli XREVRANGE stream:cdr_payload按 Call-ID 查询;B PostgreSQLhomer_data.hep_proto_1_call只读查询;B 本机 API/数据库只读探针。所有凭据均从.codex-private/读取并通过 stdin 使用,未输出明文密码。 - 验证结果:T->A SIP 输入、A->B Redis Stream 失败 CDR、A->B HOMER HEP 信令三段可按 Call-ID 串联;但未进入真实路由、媒体、录音、计费或质检闭环。B 当前
/api/v2/health/live返回 HTTP 404,lisglosips业务库表数为0,说明 current release 仍不是完整 V2 API/Worker;A 当前/etc/opensips/opensips.cfg仍为 S20/S22 占位逻辑,配置缺失时返回Config Missing,即便配置 allow 也会返回Routing Not Ready,没有真实落地网关选择、RTPEngine offer/answer、t_relay 和对话内 ACK/BYE/re-INVITE 路由。 - 回滚方式:本次未修改服务器配置和数据库结构,无需服务回滚;产生的一条测试 INVITE、HOMER 记录和 Redis Stream 失败 CDR 作为联调证据保留,不做直接删除。若后续需要清理测试数据,应先单独说明影响和回滚点。
- 未解决问题:S28 阻塞在两个前置缺口:一是 B 需要受控发布完整 V2 API、Web 和 CDR/Recording Worker,并在空业务库上执行迁移/种子和健康检查;二是 A 需要新增真实呼叫路由配置,至少包含从 Redis 配置读取线路组/落地网关、供应商目标地址、RTPEngine offer/answer、t_relay、onreply/failure/dialog 内路由和成功/失败 CDR 事件。当前不能进入 S29。
- 下一任务:继续
S28 - 三机端到端联调,先解除上述阻塞;不得提前执行 S29。
2026-06-21 21:32 - S27 React Demo 接入真实 API
- 状态:已完成
- 操作服务器:本地;未连接 A/B/T,未执行服务器写操作、服务重启、数据库迁移或数据删除
- 完成内容:新增 Web API Client,默认使用
/api/v2,开发环境通过 Vite proxy 转发到本地 API127.0.0.1:3000,可选从浏览器localStorage读取lisglosips.accessToken作为 Bearer Token,不写入或输出任何明文凭据;Dashboard 接入dashboard/summary和dashboard/trends;客户、供应商、充值记录、用户、角色、操作日志接入真实列表 API;客户/供应商新增、编辑、充值调用真实 API 并在成功后重新拉取数据;页面新增统一加载、错误和空状态,未登录或无权限时显示鉴权提示;顶部状态从“纯前端 Mock”改为真实 API 状态。CDR、计费、SIP 运维、监控告警、系统设置仍保留为未完成业务页面/静态运维原型,等待对应后端查询接口或后续延期页面设计,不在本任务伪造真实数据。 - 修改文件:
apps/web/src/api.js、apps/web/src/App.jsx、apps/web/src/styles.css、apps/web/vite.config.js、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用 React
18.2.0、Vite5.4.21、pnpm10.33.0 - 执行的关键命令:
corepack pnpm@10.33.0 --filter @lisglosips/web build、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build、本地启动corepack pnpm@10.33.0 --filter @lisglosips/web dev - 验证结果:Web 单包 build 通过;全 workspace
typecheck、lint、build均通过;本地 Vite 页面http://127.0.0.1:5173/返回 HTTP 200。API 服务若未启动或未登录,前端会展示错误态而不是回退到静态运营数据。 - 回滚方式:删除
apps/web/src/api.js,恢复apps/web/src/App.jsx中本次 API 数据加载、真实接口操作、空状态和顶部 API 状态改动,恢复apps/web/src/styles.css的空状态样式和apps/web/vite.config.jsproxy 配置,重新执行corepack pnpm@10.33.0 --filter @lisglosips/web build;S27 未修改远端服务或数据库,无远端数据需回滚。 - 未解决问题:B 当前
/opt/lisglosips/current仍是 S05 placeholder release,本次未部署到 B 当前 Web/API 服务;前端尚无登录页,真实接口需要已有 Bearer Token 或同源 Cookie 才能返回业务数据;CDR 查询、计费页面、自研 SIP 运维、监控告警、系统设置仍缺少对应产品化后端页面接口或属于延期页面,S27 未提前实现。 - 下一任务:
S28 - 三机端到端联调
2026-06-21 21:20 - S26 Dashboard 聚合
- 状态:已完成
- 操作服务器:本地;未连接 A/B/T,未执行服务器写操作、服务重启、数据库迁移或数据删除
- 完成内容:新增
DashboardModule,实现GET /api/v2/dashboard/summary和GET /api/v2/dashboard/trends;接口统一要求dashboard.view权限;summary 按Asia/Shanghai今日窗口聚合 raw CDR、rated CDR、启用客户/网关、待质检数量、失败响应码和失败落地网关 Top 10;trends 支持hours=1..168、bucketMinutes=5|15|60,按固定桶返回通话、接通率、时长、客户费用、供应商成本和毛利;实时在线通话和注册数当前返回0并标记source: "not_configured",等待后续接入 Redis/OpenSIPS 指标。未新增预聚合表,未做worker-metrics,未进入 S27 前端接入。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/dashboard/、docs/DASHBOARD_AGGREGATION_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用既有 NestJS/Fastify、Prisma
6.19.0、Vitest4.0.14、TypeScript5.9.3 - 执行的关键命令:
corepack pnpm@10.33.0 exec vitest run apps/api/src/modules/dashboard/dashboard.service.spec.ts、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build - 验证结果:S26 聚焦测试 1 个文件 4 条通过,覆盖上海自然日边界、接通率、趋势桶和参数校验;全 workspace
typecheck、lint、build均通过 - 回滚方式:从
AppModule移除DashboardModule,删除apps/api/src/modules/dashboard/和docs/DASHBOARD_AGGREGATION_RUNBOOK.md,重新执行corepack pnpm@10.33.0 build;S26 未对远端数据库写入,无远端数据需清理 - 未解决问题:B 当前
/opt/lisglosips/current仍是 S05 placeholder release,本次未部署到 B 当前 API 服务;Dashboard 设计要求的真正实时在线通话/注册数仍需后续接入 Redis/OpenSIPS 指标;24 小时趋势最终应由worker-metrics或 CDR Worker 增量写入预聚合表,本次只做时间窗口受限查询,避免扫描全量 CDR。 - 下一任务:
S27 - React Demo 接入真实 API
2026-06-21 21:05 - S25 质检后端
- 状态:已完成
- 操作服务器:本地;未连接 A/B/T,未执行服务器写操作、服务重启、数据库迁移或数据删除
- 完成内容:新增
QualityModule,实现GET/POST/PATCH/DELETE /api/v2/quality/rules及规则启停,支持按客户、线路组、比例、生效时间和过期时间配置抽检规则;新增稳定抽样算法sha256(rule_id:recording_id),同一规则与录音重复查询结果稳定;扩展RecordingsModule,实现GET /api/v2/recordings、GET /api/v2/recordings/:id、PUT /api/v2/recordings/:id/review,返回录音 CDR 摘要、最新质检、历史质检、上下条导航和抽样命中信息;保存质检结果写入quality_reviews,不覆盖历史;规则变更和质检保存均接入AuditAction元数据;权限使用quality.view、quality.manage、recordings.play。未做 Dashboard 聚合,未扩展 S26 内容。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/quality/、apps/api/src/modules/recordings/recordings.controller.ts、apps/api/src/modules/recordings/recordings.repository.ts、apps/api/src/modules/recordings/recordings.service.ts、apps/api/src/modules/recordings/recordings.module.ts、apps/api/src/modules/recordings/recordings.service.spec.ts、docs/QUALITY_BACKEND_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新软件;继续使用既有 NestJS/Fastify、Prisma
6.19.0、Vitest4.0.14、TypeScript5.9.3 - 执行的关键命令:
corepack pnpm@10.33.0 exec vitest run apps/api/src/modules/quality/sampling.spec.ts apps/api/src/modules/recordings/recordings.service.spec.ts、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build - 验证结果:S25 聚焦测试 2 个文件 6 条通过,覆盖稳定抽样可重复、比例边界、录音列表抽样状态、保存质检输入校验和 S24 内部播放路径安全;全 workspace
typecheck、lint、build均通过 - 回滚方式:删除或恢复
apps/api/src/modules/quality/,将apps/api/src/modules/recordings/恢复到 S24 版本,从AppModule移除QualityModule,重新执行corepack pnpm@10.33.0 build;S25 未对远端数据库写入,无远端数据需清理;若后续已运行 API 并产生quality_reviews,不直接删除历史记录,应通过审计可见的更正记录或受控维护窗口处理 - 未解决问题:B 当前
/opt/lisglosips/current仍是 S05 placeholder release,本次未部署到 B 当前 API 服务;真实录音和 raw CDR 关联仍依赖 S28 端到端闭环产生;Dashboard 的待质检数量、趋势和聚合指标留给 S26。 - 下一任务:
S26 - Dashboard 聚合
2026-06-21 20:20 - S24 录音搬运与播放
- 状态:已完成
- 操作服务器:A、B、本地;T 未操作
- 完成内容:新增 Recording Worker 搬运服务,支持扫描 A
/dev/shm/voip_rec/ready/**/*.ready、SSH 私网拉取、文件大小校验、SHA-256、B 端.part-*临时文件原子改名、写入recordings表、校验成功后才删除 A 源.ready;新增GET /api/v2/recordings/:id/play,要求recordings.play权限,状态必须为READY,通过X-Accel-Redirect转发到 Nginx 内部/_recordings/,不暴露真实/data/recordings路径;A 端 finalizer ready 目录改为0770,使专用拉取用户可在校验后删除源文件;B 建立专用拉取 key 和 known_hosts;A 建立专用低权限用户lisglo-rec-pull并加入rtpengine组。未调整 SSH 管理端口,未禁止 SSH key 登录,未重启 OpenSIPS、RTPEngine、Nginx 或 API。 - 修改文件:
apps/worker-recording/src/main.ts、apps/worker-recording/src/transfer.ts、apps/worker-recording/src/transfer.spec.ts、apps/worker-recording/package.json、apps/worker-recording/tsconfig.json、apps/api/src/modules/app.module.ts、apps/api/src/modules/recordings/、pnpm-lock.yaml、infra/server-a/s19/scripts/lisglosips-recording-finalize、docs/RECORDING_TRANSFER_PLAYBACK_RUNBOOK.md、IMPLEMENTATION_STATUS.md;A 的/usr/local/sbin/lisglosips-recording-finalize、/var/lib/lisglo-rec-pull/.ssh/authorized_keys和/dev/shm/voip_rec/ready权限;B 的/etc/lisglosips/recording/和/data/recordings/s24-smoke/s24-smoke.wav - 安装软件及固定版本:未安装新软件;继续使用既有 OpenSSH、Node.js
v22.22.2、pnpm10.33.0、Nginx1.24.0-2ubuntu7.12。新增系统用户lisglo-rec-pull,未新增系统包。 - 执行的关键命令:本地
corepack pnpm@10.33.0 exec vitest run apps/worker-recording/src/transfer.spec.ts apps/api/src/modules/recordings/recordings.service.spec.ts、corepack pnpm@10.33.0 --filter @lisglosips/worker-recording build、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;Abash -n /usr/local/sbin/lisglosips-recording-finalize、创建 S24 测试 incoming 文件并运行 finalizer;B 使用lisglo-recorder通过专用 key 从 A 拉取、校验、落盘、删除测试源文件。sudo 凭据通过.codex-private/读取并经 stdin 使用,未回显。 - 验证结果:本地 2 个测试文件 5 条测试通过,覆盖路径安全、
.ready转 storage key、复制/哈希/落盘/源删除、大小不匹配不删除源文件、API 内部路径穿越防护;worker-recording单包 build、全 workspacetypecheck、lint、build均通过;A 测试文件从incoming进入ready,B 私网拉取到/data/recordings/s24-smoke/s24-smoke.wav,大小31,属主lisglo-recorder:lisglosips,SHA-256 为c72dd3909a317ad55817435b7ed5a0db6947455ffc66e09a2dde787fe4a8c348,校验后 A 源.ready已删除;外部直接访问https://127.0.0.1/_recordings/s24-smoke/s24-smoke.wav返回404,Nginx 内部录音 location 未暴露。 - 回滚方式:A 备份目录
/var/backups/lisglosips-s24/20260621T121130Z,可恢复lisglosips-recording-finalize、删除lisglo-rec-pull用户并将/dev/shm/voip_rec/ready恢复0750;B 备份目录/var/backups/lisglosips-s24/20260621T121100Z,可删除/etc/lisglosips/recording和/data/recordings/s24-smoke;代码回滚恢复本次新增/修改文件后重新 build。详见docs/RECORDING_TRANSFER_PLAYBACK_RUNBOOK.md。 - 未解决问题:B 当前
/opt/lisglosips/current仍是 S05 placeholder release,尚未切换完整 monorepo API/Worker release,因此本次未启用正式lisglosips@recording-worker.service,API 播放入口完成代码与构建但未在 B 当前 release 对外提供;真实通话录音字段仍依赖后续 S28 端到端路由/媒体闭环产生。 - 下一任务:
S25 - 质检后端
2026-06-21 20:02 - B sudo 凭据复验
- 状态:已完成
- 操作服务器:B、本地;仅执行只读 SSH 与 sudo 校验,未修改服务器配置、未重启服务、未执行数据库写入或数据删除
- 完成内容:读取
.codex-private/中 DPAPI 保存的 B 凭据且未回显密码;复验 B SSH Key 登录正常;确认hector属于sudo组;使用保存的 sudo 凭据执行sudo -v成功;使用 sudo 执行只读权限检查成功 - 修改文件:
IMPLEMENTATION_STATUS.md - 安装软件及固定版本:无
- 执行的关键命令:
ssh -F .codex-private\ssh\config lisglosips-b ...;PowerShellImport-Clixml .codex-private\SERVER_CREDENTIALS.clixml后通过 stdin 向远端sudo -S提供凭据,命令与输出均未包含明文密码 - 验证结果:B 返回主机名
YanZi,用户hector在sudo组;sudo -v退出码为 0;只读 sudo 检查退出码为 0 - 回滚方式:无服务器变更无需回滚;如需撤销本地状态记录,恢复本文件本节和顶部阻塞说明
- 未解决问题:S13-S17 历史任务仍未补做 B 临时库冒烟;若后续补验会涉及数据库写入,应在执行前单独说明影响与回滚点
- 下一任务:
S24 - 录音搬运与播放
2026-06-21 19:55 - S23 最小计费与余额扣减
- 状态:已完成
- 操作服务器:本地;未连接 A/B/T,未执行服务器写操作、服务重启、数据库迁移或数据删除
- 完成内容:在
worker-cdr中接入最小计费服务,Redis Stream CDR 消费后写入raw_cdrs、成功通话写入rated_cdrs、按落地网关周期秒数和周期费率计算费用、扣减客户余额,并用负数customer_recharges记录不可变扣费流水;同一event_id或同一raw_cdr_id不重复扣费;失败 CDR、零时长 CDR、缺少客户或落地网关 ID 的 CDR 标记SKIPPED且不扣费。当前尚无客户侧费率表,S23 采用落地网关费率临时镜像为客户费用,供应商成本相同,毛利为 0,后续真实计费版本再扩展客户费率和利润计算。 - 修改文件:
apps/worker-cdr/src/main.ts、apps/worker-cdr/src/billing.ts、apps/worker-cdr/src/rating.ts、apps/worker-cdr/src/rating.spec.ts、apps/worker-cdr/package.json、apps/worker-cdr/tsconfig.json、apps/worker-cdr/dist/、docs/CDR_MINIMAL_BILLING_RUNBOOK.md、IMPLEMENTATION_STATUS.md;本地因pnpm install受 registry DNS/metadata 影响,手工补齐apps/worker-cdr/node_modules/@lisglosips/database到packages/database的开发 junction - 安装软件及固定版本:未安装新软件;继续使用既有 Node.js/pnpm、Prisma
6.19.0、Vitest4.0.14、TypeScript5.9.3 - 执行的关键命令:
corepack pnpm@10.33.0 exec vitest run apps/worker-cdr/src/rating.spec.ts、corepack pnpm@10.33.0 --filter @lisglosips/worker-cdr build、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;尝试corepack pnpm@10.33.0 install与install --offline时被 npm registry DNS/本地 metadata 问题阻塞,未作为验收依据 - 验证结果:
apps/worker-cdr/src/rating.spec.ts3 条测试通过,覆盖周期计费、成功 CDR 扣费幂等、失败/零时长 CDR 不扣费;worker-cdr单包 build 通过;全 workspacetypecheck、lint、build均通过 - 回滚方式:恢复
apps/worker-cdr/src/main.ts到 S22 只记录 CDR 的版本,删除或恢复 S23 新增的billing.ts、rating.ts、rating.spec.ts,恢复apps/worker-cdr/package.json和tsconfig.json后重新执行corepack pnpm@10.33.0 build;S23 未执行真实数据库写入验证,无远端数据需要清理;若后续已运行 worker 并产生扣费,必须先停止 worker,再通过受控反向流水或备份恢复处理,不直接删除余额流水 - 未解决问题:B sudo 凭据仍不可用,本次未部署 B systemd worker、未对真实 B MySQL 执行写入式冒烟;当前最小计费暂时镜像落地网关费率为客户费用,客户费率表、利润规则、低余额拦截和 Dashboard 聚合留给后续任务;
pnpm install当前受 registry DNS/metadata 影响,后续若新增依赖需优先修复本地包管理器联网或缓存 - 下一任务:
S24 - 录音搬运与播放
2026-06-21 19:34 - S22 CDR Redis Stream
- 状态:已完成
- 操作服务器:A、T、本地;B 仅通过 A 的 Redis AUTH proxy 写入 Redis Stream Consumer Group 元数据,未使用 B sudo,未改 B 服务
- 完成内容:新增 CDR Stream schema v1 和可靠消费库,支持
stream:cdr_payload、stream:cdr_deadletter、billing-workersConsumer Group、XREADGROUP、XACK、XAUTOCLAIMPending 重领、死信写入和lock:cdr:{event_id}幂等占位;worker-cdr从骨架改为 Redis Stream Worker,启动时确保 Consumer Group,循环先处理 Pending 再读取新消息,当前 handler 只接收/记录原始 CDR,不做计费和余额扣减;按多 A 后续演进预留node_id、opensips_instance、ingress_a_ip、rtpengine_node字段,当前单 A 值为a1、opensips-a1、100.90.90.90、a1;升级 Server A OpenSIPSroute[S20_CDR_XADD],从 S20 冒烟 XADD 改为 S22 正式事件字段;在 Redis 创建billing-workersConsumer Group。未调整 SSH 管理端口或密钥登录策略,未写入明文密码。 - 修改文件:
packages/redis/src/index.ts、packages/redis/src/cdr-stream.ts、packages/redis/src/cdr-stream.spec.ts、apps/worker-cdr/src/main.ts、packages/redis/dist/、apps/worker-cdr/dist/、infra/server-a/s22/opensips/opensips.cfg、docs/CDR_REDIS_STREAM_RUNBOOK.md、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md;A 的/etc/opensips/opensips.cfg;Redisstream:cdr_payloadConsumer Groupbilling-workers - 安装软件及固定版本:未安装新系统软件;继续使用既有 Node.js/pnpm、OpenSIPS
3.6.7-1、Redis7.0.15。新增代码复用既有ioredis 5.8.2。 - 执行的关键命令:本地
corepack pnpm@10.33.0 exec vitest run packages/redis/src/cdr-stream.spec.ts、corepack pnpm@10.33.0 lint/typecheck/build;Aopensips -C -f /etc/opensips/opensips.cfg、systemctl restart opensips;A 经本地 Redis proxy 执行XGROUP CREATE stream:cdr_payload billing-workers 0 MKSTREAM、XINFO GROUPS、XREVRANGE;T 执行/opt/lisglosips-s21/lisglosips-s21-sip.py invite --timeout 8。sudo 凭据通过.codex-private/读取且未回显。 - 验证结果:Redis Stream 单元测试 6 条通过,覆盖发布/解析、Consumer Group 幂等创建、
XREADGROUP/XACK、重复event_id幂等、死信、XAUTOCLAIMPending 重领;lint、typecheck、全 workspacebuild均通过;A OpenSIPS 配置语法检查通过并重启后 active;RedisXINFO GROUPS stream:cdr_payload显示billing-workers,pending 为 0;T 发 INVITE 后 A 返回SIP/2.0 503 Config Missing,A 日志显示 CDR XADD 成功,Redis 最新事件包含schema_version=1、idempotency_key、node_id=a1、opensips_instance=opensips-a1、ingress_a_ip=100.90.90.90、rtpengine_node=a1、sip_code=503、hangup_reason=CONFIG_MISSING。一次使用 S21 默认 3 秒超时的探针未收到响应,但 A 已写入 CDR;随后使用 8 秒超时复测收到预期503 Config Missing。 - 回滚方式:A 备份目录为
/var/backups/lisglosips-s22/20260621T113157Z,恢复其中opensips.cfg后执行opensips -C -f /etc/opensips/opensips.cfg并重启opensips;代码回滚恢复本次新增/修改的 Redis/worker 文件后重新 build;Redis Consumer Group 可保留,停止 worker 即可,不删除stream:cdr_payload数据。详见docs/CDR_REDIS_STREAM_RUNBOOK.md。 - 未解决问题:S22 只完成原始 CDR Stream 契约和可靠消费基础,不写 MySQL raw/rated CDR、不计费、不扣余额,这些留给 S23;A 端当前仍未发布真实路由配置,因此测试 CDR 为
CONFIG_MISSING失败事件;B sudo 凭据仍不可用,本次未执行 B 端服务部署或 systemd worker 验收;多 A 仅完成字段预留和 A1 默认值,真实 3A 部署留到单 A 闭环之后。 - 下一任务:
S23 - 最小计费与余额扣减
2026-06-21 15:28 - S21 Server T 客户模拟配置
- 状态:已完成
- 操作服务器:T、本地;A 仅接收 T 发起的 SIP 探针,未写入;B 未操作
- 完成内容:在 Server T 建立 S21 客户模拟环境;新增测试域
s21.lisglosips.test;新增两套 SIP 注册测试账号s21-reg-1001、s21-reg-1002,OpenSIPS 改为 HA1 鉴权,subscriber.password为空且不保存 SIP 明文密码;新增两套 IP 认证测试条目s21-ip-1001、s21-ip-1002指向 T100.93.185.30/32;部署/opt/lisglosips-s21自包含 SIP 脚本,支持注册成功、错误鉴权、向 A 发起 IP/SIP 模拟 INVITE、本地成功/忙/拒绝/超时场景和突发 CPS 探针;收敛 T 的 MI FIFO 权限为0660,TLS 私钥权限为0600。未调整 SSH 管理端口或密钥登录策略,未写入明文密码。 - 修改文件:T 的
/etc/opensips/opensips.cfg、OpenSIPSopensips数据库domain/subscriber/address/location测试数据、/opt/lisglosips-s21/、/etc/lisglosips-s21/sip-accounts.env;本地infra/server-t/s21/、docs/SERVER_T_CUSTOMER_SIMULATION_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 T 既有 OpenSIPS
3.6.6、RTPEngine11.5.1.18、MariaDB10.11.14和系统 Python 3。新增 SIP 测试脚本无第三方依赖。 - 执行的关键命令:
mysqldump --single-transaction --routines --events opensips、opensips -C -f /etc/opensips/opensips.cfg、systemctl restart opensips、mysql --protocol=socket -uroot、python3 -m py_compile、/opt/lisglosips-s21/s21-register-*、s21-local-*、s21-call-a-*、s21-over-cps-a。sudo 凭据通过.codex-private/读取且未回显。 - 验证结果:备份目录
/var/backups/lisglosips-s21/20260621T072316Z已生成并含 OpenSIPS 配置和数据库 dump;OpenSIPS 配置语法检查通过,opensips.serviceactive;s21-register-primary和s21-register-secondary均先收到401 Unauthorizedchallenge 后最终200 OK,s21-register-wrong-ha1最终401 Unauthorized;location表已有s21-reg-1001、s21-reg-1002注册位置;subscriber.password长度为 0,ha1长度为 32;本地脚本分别返回200 OK、486 Busy Here、603 Decline和NO RESPONSE;从 T 到 A 的s21-call-a-ip、s21-call-a-sip与 5 次s21-over-cps-a均稳定输出 Call-ID 并收到 A 当前预期的SIP/2.0 503 Config Missing;MI FIFO 为660 opensips:opensips,TLS 私钥文件为600 root:root。 - 回滚方式:停止 T 的
opensips,从/var/backups/lisglosips-s21/20260621T072316Z/opensips恢复/etc/opensips,用该目录opensips.sql恢复 OpenSIPS 数据库,删除/opt/lisglosips-s21和/etc/lisglosips-s21,执行opensips -C -f /etc/opensips/opensips.cfg后启动opensips。详见docs/SERVER_T_CUSTOMER_SIMULATION_RUNBOOK.md。 - 未解决问题:A 端尚未发布真实客户/路由配置,因此 T 到 A 的 INVITE 当前按 S20 逻辑返回
503 Config Missing,真实路由、双向媒体、正式 CDR 和扣费留给 S22/S28;T 的 RTPEngine 仍无内核加速,防火墙默认放行、Apache/rpcbind 暴露和克隆身份问题仍为历史风险,未在 S21 扩大处理。 - 下一任务:
S22 - CDR Redis Stream
2026-06-21 15:05 - S20 Server A Redis 热路径与 HEP/指标
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:在 Server A 部署 Redis 热路径 Lua 原子检查,基于
cfg:active_version和版本化cfg:v:{version}:*配置读取客户、客户网关、IP 认证、策略与线路组;OpenSIPSINVITE接入 Lua 热路径,配置缺失返回503 Config Missing,Redis 不可用返回503 Redis Unavailable,策略允许但路由未进入 S21/S28 前返回503 Routing Not Ready;新增 CDR 冒烟 XADD 到stream:cdr_payload;新增本地 Redis AUTH 代理,凭据通过 systemd encrypted credential 加载,OpenSIPS 只连接127.0.0.1:6380;修正 HEP socket 为100.90.90.90:9061并发送至 B100.90.90.91:9060;新增 OpenSIPS Prometheusscript:指标和 Node Exporter textfile 指标;新增 S20 nftables 表阻断非本地 Redis 代理和非既有连接访问 HEP socket;未调整 SSH 管理端口或密钥登录策略,未写入明文密码。 - 修改文件:A 的
/etc/opensips/opensips.cfg、/etc/opensips/lisglosips_hotpath.lua、/usr/local/sbin/lisglosips-redis-auth-proxy、/usr/local/sbin/lisglosips-redis-load-hotpath、/usr/local/sbin/lisglosips-opensips-preprocess、/usr/local/sbin/lisglosips-s20-metrics、/etc/systemd/system/lisglosips-redis-auth-proxy.service、/etc/systemd/system/lisglosips-redis-hotpath-load.service、/etc/systemd/system/opensips.service.d/20-lisglosips-s20.conf、/etc/systemd/system/lisglosips-s20-metrics.service、/etc/systemd/system/lisglosips-s20-metrics.timer、/etc/default/lisglosips-node-exporter、/etc/nftables.d/lisglosips-s20-observability.nft、/etc/systemd/system/lisglosips-a-firewall.service;本地infra/server-a/s20/、docs/SERVER_A_REDIS_HEP_METRICS_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 OpenSIPS
3.6.7-1、Redis Server B 既有7.0.15、Node Exporter1.7.0、RTPEngine11.5.1.18-1ubuntu1.2。新增 systemd encrypted credential 文件/etc/credstore.encrypted/lisglosips-redis-password.cred,未写明文凭据。 - 执行的关键命令:
opensips -C -f /etc/opensips/opensips.cfg、systemctl daemon-reload/enable/restart/start/stop、nft -c/-f、curl http://127.0.0.1:8888/metrics、curl http://100.90.90.90:9100/metrics、从 T 发送 SIPINVITE,A/B Redis RESP 冒烟,Atcpdump观察 HEP,Bjournalctl -u heplify-server查询 HEP 统计。所有凭据均通过.codex-private/读取并已避免回显。 - 验证结果:A
systemctl is-system-running为running;opensips、lisglosips-redis-auth-proxy、lisglosips-s20-metrics.timer、lisglosips-node-exporter、lisglosips-a-firewall、rtpengine-daemon、rtpengine-recording-daemon均 active,S20 新增服务均 enabled;lisglosips-redis-hotpath-load.serviceoneshot 结果为success;监听端口为100.90.90.90:15060/udp、100.90.90.90:9061/udp、127.0.0.1:6380/tcp、127.0.0.1:8888/tcp、100.90.90.90:9100/tcp;S20 nftables 表存在;A 到 B Redis PING/AUTH 通过;无配置版本时 T 发 INVITE 返回SIP/2.0 503 Config Missing,OpenSIPS 记录热路径拒绝且 CDR XADD 成功;临时 Redis 冒烟配置下 T 发 INVITE 返回SIP/2.0 503 Routing Not Ready,返回字段含配置版本、客户网关、策略和线路组,测试 Redis keys 与 CDR 事件已清理;短暂停止 Redis 代理后 T 发 INVITE 返回SIP/2.0 503 Redis Unavailable,日志明确记录 Redis 热路径不可用和 CDR XADD 失败,代理与 Lua loader 随后恢复;OpenSIPS Prometheus 暴露lisglosips_opensips_s20_*,B 可抓取 A Node Exporter textfile 指标lisglosips_redis_proxy_up、录音 tmpfs、ready/part 文件数和 RTPEngine sessions;HEP 绑定修正后 A 侧抓包看到发往 B9060/udp的 HEP,B heplify 统计显示 HEP 收包且 Error 为 0,修正后 A 无新增 HEP 发送错误;S20 本地产物敏感扫描未发现明文凭据,命中的仅为 credential 名称或历史文档中的安全术语。 - 回滚方式:A 备份目录为
/var/backups/lisglosips-s20/20260621T061900Z,验证阶段的 OpenSIPS 配置副本位于该目录post-verify/下;回滚时停止 S20 metrics timer 和 Redis AUTH proxy,从备份恢复/etc/opensips/opensips.cfg、S20 systemd/drop-in、Node Exporter 默认文件和 nftables 文件,执行opensips -C -f /etc/opensips/opensips.cfg后重启opensips、lisglosips-node-exporter、lisglosips-a-firewall,不删除 Redis Stream 历史数据。详细步骤见docs/SERVER_A_REDIS_HEP_METRICS_RUNBOOK.md。 - 未解决问题:S20 只完成热路径检查、HEP 和指标,不实现真实落地路由、SIP 注册账号、双向媒体、正式 CDR 契约和计费;这些留给 S21/S22/S28。B sudo 凭据仍无法通过校验,本次 B 侧仅使用免 sudo 的只读 heplify 日志和已有服务凭据做验证;HOMER 数据库未稳定查到本次 OpenSIPS HEP Call-ID 行,但 heplify 已确认收包,后续 S28 端到端联调需继续核对 HOMER UI/库内 Call-ID。
- 下一任务:
S21 - Server T 客户模拟配置
2026-06-21 14:00 - S19 Server A RTPEngine 与录音 tmpfs
- 状态:已完成
- 操作服务器:A、T、本地
- 完成内容:在 Server A 安装并固定 RTPEngine
11.5.1.18-1ubuntu1.2及 kernel DKMS/recording 组件;加载xt_RTPENGINE内核模块,DKMS 已为6.17.0-22-generic与6.17.0-35-generic构建;配置 RTPEngine 媒体地址100.90.90.90、RTP30000-40000/udp、本地 NG/CLI/HTTP 控制口127.0.0.1:2223/2224/2225;创建/dev/shm/voip_rec3 GiB tmpfs 与spool/incoming/ready/failed/tmp目录;部署录音 finalizer,按incoming -> ready/*.part -> ready/*.ready原子规则生成 ready 文件;新增lisglosips-a-firewall.service保障 S18/S19 nftables 表重启后自动加载;新增 OpenSIPS systemd drop-in 等待 Tailscale 地址后启动;覆盖rtpengine-recording-daemon.service,移除包默认 NFS/rpcbind 依赖;停止并 mask S19 不使用的rpcbind.service/socket。未调整 SSH 管理端口或密钥登录策略。 - 修改文件:A 的
/etc/rtpengine/rtpengine.conf、/etc/rtpengine/rtpengine-recording.conf、/etc/default/rtpengine-daemon、/etc/default/rtpengine-recording-daemon、/etc/fstab、/etc/nftables.d/lisglosips-s19-rtpengine.nft、/etc/systemd/system/lisglosips-a-firewall.service、/etc/systemd/system/opensips.service.d/10-lisglosips-wait-tailscale.conf、/etc/systemd/system/rtpengine-recording-daemon.service、/etc/systemd/system/lisglosips-recording-finalize.service、/etc/systemd/system/lisglosips-recording-finalize.timer、/etc/tmpfiles.d/lisglosips-voip-rec.conf、/usr/local/sbin/lisglosips-recording-finalize;本地infra/server-a/s19/、docs/SERVER_A_RTPENGINE_RECORDING_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:
rtpengine、rtpengine-daemon、rtpengine-recording-daemon、rtpengine-iptables、rtpengine-kernel-dkms、rtpengine-utils均为11.5.1.18-1ubuntu1.2并已apt-mark hold;安装匹配内核 headers。未新增明文凭据。 - 执行的关键命令:
apt-get install、apt-mark hold、modprobe xt_RTPENGINE、dkms status、nft -c/-f、systemctl daemon-reload/enable/restart/reboot、systemd-tmpfiles --create、rtpengine-ctl -ip 127.0.0.1 -port 2224 list numsessions/interfaces、tcpdump、从 T 发送 RTP UDP 与 SIPOPTIONS/REGISTER测试包。 - 验证结果:A 二次重启后
systemctl is-system-running为running且 0 failed unit;opensips、rtpengine-daemon、rtpengine-recording-daemon、lisglosips-recording-finalize.timer、lisglosips-a-firewall.service、lisglosips-node-exporter.service全部 active/enabled;/dev/shm/voip_rec为 3.0 GiB tmpfs,子目录均为rtpengine:rtpengine 0750;xt_RTPENGINE已加载,DKMS 状态 installed;rtpengine-ctl显示接口100.90.90.90和端口段30000 - 40000;nftables S18/S19 表重启后存在;从 T 到 A30000/udp、40000/udp的 RTP 探针在 Atailscale0抓包可见;从 T 发 SIPOPTIONS返回SIP/2.0 200 Keepalive,REGISTER返回SIP/2.0 401 Authentication Required和WWW-Authenticate;录音 finalizer 冒烟将测试.wav生成ready/...wav.ready,权限0640,无.part残留,测试文件已删除;本地 S19 产物敏感信息扫描未命中。 - 回滚方式:A 备份目录为
/var/backups/lisglosips-s19/20260621T054645Z;回滚时停止rtpengine-recording-daemon、lisglosips-recording-finalize.timer、rtpengine-daemon,从备份恢复/etc/rtpengine/、/etc/default/、/etc/fstab等配置,删除inet lisglosips_s19_rtpenginenftables 表,停用并删除 S19 systemd/tmpfiles/finalizer 文件,必要时apt-mark unhold后卸载 RTPEngine 包;详细步骤见docs/SERVER_A_RTPENGINE_RECORDING_RUNBOOK.md。 - 未解决问题:S19 尚未把 OpenSIPS 呼叫路由接入 RTPEngine,真实双向音频、NAT 媒体锚定和完整通话录音需在 S20/S21/S28 完成;Tailscale APT 源在 A 上仍有 DNS 解析失败历史但当前 Tailscale 网络和服务运行正常;B sudo 凭据阻塞仍未解决,不影响本次 A 端 S19。
- 下一任务:
S20 - Server A Redis 热路径与 HEP/指标
2026-06-21 13:29 - S18 Server A OpenSIPS 基线
- 状态:已完成
- 操作服务器:A、本地
- 完成内容:在 Server A 添加 OpenSIPS 官方 APT 源并安装固定 OpenSIPS
3.6.7-1;安装并 holdopensips、opensips-cli、opensips-auth-modules、opensips-redis-module、opensips-http-modules、opensips-json-module、opensips-restclient-module、opensips-prometheus-module、opensips-tlsmgm-module、opensips-tls-module、opensips-tls-openssl-module;部署/etc/opensips/opensips.cfg,OpenSIPS 仅监听100.90.90.90:15060/udp,HEP transport 占位监听127.0.0.1:9061/udp,MI HTTP 仅监听127.0.0.1:8888/tcp;加载auth、pike、ratelimit、cachedb_redis、proto_hep、tracer、mi_http等模块;实现 S18 保守路由基线:OPTIONS返回200 Keepalive,REGISTER返回带 Digest challenge 的401 Authentication Required,INVITE在 S20/S21 前返回503 Routing Not Ready,避免开放中继;新增inet lisglosips_s18_opensipsnftables 表,只允许 T100.93.185.30访问 A 的 UDP15060,丢弃其他 15060 来源,并阻断非本地 MI HTTP;未调整 SSH 管理端口或密钥登录策略。 - 修改文件:A 的
/etc/apt/sources.list.d/opensips.list、/etc/apt/sources.list.d/opensips-cli.list、/etc/opensips/opensips.cfg、/etc/default/opensips、/etc/nftables.d/lisglosips-s18-opensips.nft;本地infra/server-a/s18/、docs/SERVER_A_OPENSIPS_BASELINE_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:OpenSIPS
3.6.7-1;OpenSIPS CLI0.4.0~20260522~570a9a9-1;所有 S18 OpenSIPS 包已apt-mark hold。未新增明文凭据。 - 执行的关键命令:
apt-get update、apt-get install、apt-mark hold、opensips -C -f /etc/opensips/opensips.cfg、systemctl enable --now/restart opensips、nft -c -f /etc/nftables.d/lisglosips-s18-opensips.nft、nft -f /etc/nftables.d/lisglosips-s18-opensips.nft、curl -X POST http://127.0.0.1:8888/mi、从 T 发送 SIPOPTIONS/REGISTER测试包。 - 验证结果:A 系统状态
running;opensips.serviceactive/enabled;OpenSIPS 配置语法检查通过;监听端口为100.90.90.90:15060/udp、127.0.0.1:9061/udp、127.0.0.1:8888/tcp,S06 的100.90.90.90:9100/tcp保持;MI HTTP JSON-RPCversion返回OpenSIPS (3.6.7 (x86_64/linux));从 T100.93.185.30发往 A15060/udp的 SIPOPTIONS返回SIP/2.0 200 Keepalive;SIPREGISTER返回SIP/2.0 401 Authentication Required且包含WWW-Authenticate;nftables S18 表加载成功;本地 S18 产物敏感信息扫描未命中。 - 回滚方式:A 备份目录为
/var/backups/lisglosips-s18/20260621T051403Z;回滚时执行systemctl stop opensips,从备份恢复/etc/opensips/和/etc/default/opensips,删除inet lisglosips_s18_opensipsnftables 表,必要时apt-mark unhold并卸载 S18 OpenSIPS 包;详细步骤见docs/SERVER_A_OPENSIPS_BASELINE_RUNBOOK.md。 - 未解决问题:Tailscale APT 源在 A 上一度 DNS 解析失败,但 Tailscale 服务和当前连接未受影响;A 仍存在 S01 记录的本地 ens18 双地址/路由历史风险,S18 的 OpenSIPS 绑定 Tailscale 地址规避了 SIP 监听歧义;当前 HEP trace 已有目标配置但未查询 B 的 HOMER 入库,完整 HEP/Redis 热路径和指标留给 S20;RTPEngine、RTP 端口和录音 tmpfs 留给 S19。
- 下一任务:
S19 - Server A RTPEngine 与录音 tmpfs
2026-06-21 13:06 - S17 落地线路组 API
- 状态:已完成
- 操作服务器:本地;B 未执行写操作
- 完成内容:新增 LandingLineGroupsModule,提供
GET /landing-line-groups、GET /landing-line-groups/:id、POST /landing-line-groups、PATCH /landing-line-groups/:id、POST /landing-line-groups/:id/enable、POST /landing-line-groups/:id/disable、DELETE /landing-line-groups/:id、POST /landing-line-groups/:id/items、PATCH /landing-line-groups/items/:itemId、DELETE /landing-line-groups/:id/items/:gatewayId、POST /landing-line-groups/:id/items/reorder;支持线路组启停、软删除、成员增删改、成员优先级重排、权重、成员并发上限和成员状态;返回itemCount、enabledItemCount、concurrencyCapSum、gatewayConcurrencyLimitSum和policyCount,用于组内并发汇总与引用可见性;新增引用校验,成员只能引用未删除落地网关,软删除线路组前阻止仍被未删除客户网关策略引用的线路组删除;读操作绑定line_groups.view,写操作绑定line_groups.manage和审计动作;线路组与成员变更写入line_group_configOutbox,Config Publisher 扩展线路组快照并发布cfg:v:{version}:line_group:*与line_group:*:items。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/landing-line-groups/、apps/worker-config-publisher/src/publisher.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;未新增 npm 包;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test -- landing-line-groups、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build。 - 验证结果:typecheck 通过;S17 相关 Vitest 14 个测试文件 44 条测试通过,其中
landing-line-groups.e2e.spec.ts覆盖线路组列表并发汇总、写权限拒绝、创建、成员新增/更新/删除、成员参数校验、优先级重排、启停、软删除和审计记录;全量 Vitest 14 个测试文件 44 条测试通过;lint 通过;workspace build 通过,包含apps/worker-config-publisher构建通过。 - 回滚方式:移除
LandingLineGroupsModule导入并删除apps/api/src/modules/landing-line-groups/;恢复apps/worker-config-publisher/src/publisher.ts中 S17 新增的line_group_configOutbox 处理和线路组快照发布逻辑。S17 未新增数据库迁移,B 端未创建临时库、未改服务、未写 Redis 或 MySQL。 - 未解决问题:B 写入式临时库冒烟未执行,因为当前 B sudo 凭据仍无法通过校验,迁移账号也无权创建临时库;S17 不实现 OpenSIPS 呼叫路由或 Server A 配置,这些从 S18 开始。
- 下一任务:
S18 - Server A OpenSIPS 基线
2026-06-21 11:57 - S16 落地网关 API
- 状态:已完成
- 操作服务器:本地;B 未执行写操作
- 完成内容:新增 VendorGatewaysModule,提供
GET /vendor-gateways、GET /vendor-gateways/:id、POST /vendor-gateways、PATCH /vendor-gateways/:id、POST /vendor-gateways/:id/enable、POST /vendor-gateways/:id/disable;支持供应商落地网关IP、SIP_DIGEST、MIXED三种认证模式,创建和更新时校验 host/port/transport、SIP 用户名与密码强度,并以username:host:password生成 HA1,API 响应只返回hasSipCredential不返回明文密码或 HA1;支持 CPS、并发上限、禁呼时段、编码优先级、主叫/被叫号码前缀转换、计费周期秒数和周期费率,响应补充按分钟折算的minuteRate;写操作绑定vendor_gateways.manage和审计动作,读操作绑定vendor_gateways.view;落地网关创建、更新、启停写入vendor_gateway_configOutbox,Config Publisher 扩展供应商落地网关快照并发布到 Redis 版本化配置。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/vendor-gateways/、apps/worker-config-publisher/src/publisher.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;未新增 npm 包;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test -- vendor-gateways、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build。 - 验证结果:typecheck 通过;S16 相关 Vitest 13 个测试文件 40 条测试通过,其中
vendor-gateways.e2e.spec.ts覆盖列表脱敏、写权限拒绝、完整 SIP Digest 网关创建、审计敏感字段脱敏、切换 IP 模式清空 SIP 凭据、重复编码优先级校验和启停;全量 Vitest 13 个测试文件 40 条测试通过;lint 通过;workspace build 通过,包含apps/worker-config-publisher构建通过。 - 回滚方式:移除
VendorGatewaysModule导入并删除apps/api/src/modules/vendor-gateways/;恢复apps/worker-config-publisher/src/publisher.ts中 S16 新增的vendor_gateway_configOutbox 处理和落地网关快照发布逻辑。S16 未新增数据库迁移,B 端未创建临时库、未改服务、未写 Redis 或 MySQL。 - 未解决问题:B 写入式临时库冒烟未执行,因为当前 B sudo 凭据仍无法通过校验,迁移账号也无权创建临时库;S16 不实现落地线路组成员、优先级重排或并发汇总,这些留给 S17。
- 下一任务:
S17 - 落地线路组 API
2026-06-21 11:45 - S15 供应商管理 API
- 状态:已完成
- 操作服务器:本地;B 未执行写操作
- 完成内容:新增 VendorsModule,提供
GET /vendors、GET /vendors/:id、POST /vendors、PATCH /vendors/:id、POST /vendors/:id/enable、POST /vendors/:id/disable、DELETE /vendors/:id;供应商返回余额、授信额度、可用余额和落地网关数量;余额保持只读,供应商充值仍通过 S12 的不可变充值流水;支持联系人、电话、邮箱、结算方式、备注、授信额度和启停状态维护;软删除前阻止仍有关联未删除落地网关的供应商删除;读操作绑定vendors.view,写操作绑定vendors.manage和审计动作。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/vendors/、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;未新增 npm 包;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test -- vendors、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build。 - 验证结果:typecheck 通过;S15 相关 Vitest 12 个测试文件 36 条测试通过;全量 Vitest 12 个测试文件 36 条测试通过,覆盖供应商余额/授信/网关数量读取、写权限拒绝、新增、编辑、启停、软删除和审计记录;lint 通过;workspace build 通过。
- 回滚方式:移除
VendorsModule导入并删除apps/api/src/modules/vendors/;S15 未新增数据库迁移。B 端未创建临时库、未改服务、未写 Redis 或 MySQL。 - 未解决问题:B 写入式临时库冒烟未执行,因为当前 B sudo 凭据仍无法通过校验,迁移账号也无权创建临时库;S15 不实现落地网关、费率、CPS/并发或号码转换,这些留给 S16。
- 下一任务:
S16 - 落地网关 API
2026-06-21 11:39 - S14 客户网关策略与 Redis 配置发布
- 状态:已完成
- 操作服务器:本地;B 未执行写操作
- 完成内容:新增 CustomerGatewayPoliciesModule,提供
GET /customer-gateways/:id/policies、POST /customer-gateways/:id/policies、PATCH /customer-gateway-policies/:id、DELETE /customer-gateway-policies/:id、POST /customer-gateways/:id/policies/reorder;支持主叫/被叫ANY、EQUALS、PREFIX匹配,校验非ANY模式必须提供匹配值;支持显式优先级和自动追加优先级,重排时要求一次提交全部有效策略 ID 并用两阶段负优先级避免唯一索引冲突;客户网关创建、更新、启停以及策略创建、更新、删除、重排均在同一事务内写入outbox_events,不直接双写 Redis;Config Publisher 从骨架升级为处理customer_gateway_configOutbox 的 worker,生成完整 Redis 配置快照,写入cfg:v:{version}:customer:*、customer_gateway:*、auth:ip:*、auth:sip:*和策略列表,原子切换cfg:active_version,并维护cfg:previous_version作为回滚点;新增rollbackActiveConfig函数用于切回上一版本。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/customer-gateways/customer-gateways.repository.ts、apps/api/src/modules/customer-gateway-policies/、apps/worker-config-publisher/src/main.ts、apps/worker-config-publisher/src/publisher.ts、apps/worker-config-publisher/package.json、apps/worker-config-publisher/tsconfig.json、packages/redis/src/index.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;未新增需要联网下载的 npm 包;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0、ioredis5.8.2(由@lisglosips/redis继续提供)。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test -- customer-gateway、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;曾尝试corepack pnpm@10.33.0 install,因本机 DNS 无法解析registry.npmjs.org失败,随后移除新增联网依赖,最终无需更新锁文件。 - 验证结果:typecheck 通过;S14 相关 Vitest 11 个测试文件 33 条测试通过;全量 Vitest 11 个测试文件 33 条测试通过,覆盖策略列表、权限拒绝、策略创建/更新/重排/删除、审计记录和匹配值校验;lint 通过;workspace build 通过,包含
apps/worker-config-publisher构建通过。 - 回滚方式:移除
CustomerGatewayPoliciesModule导入并删除apps/api/src/modules/customer-gateway-policies/;恢复apps/api/src/modules/customer-gateways/customer-gateways.repository.ts中的 Outbox 写入前版本;恢复apps/worker-config-publisher/src/main.ts为骨架并删除publisher.ts;恢复packages/redis/src/index.ts的 S14 新增 Redis 常量和 helper。S14 未新增数据库迁移,B 端未创建临时库、未改服务、未写 Redis。 - 未解决问题:B 写入式临时库/Redis 冒烟未执行,因为当前 B sudo 凭据仍无法通过校验,迁移账号也无权创建临时库;Config Publisher 当前完成客户/客户网关/策略快照发布,落地网关、线路组成员和供应商侧热路径会随 S15-S17 数据模型 API 补齐后扩展到快照。
- 下一任务:
S15 - 供应商管理 API
2026-06-21 11:28 - S13 客户网关 API
- 状态:已完成
- 操作服务器:本地;B 仅读取 SSH/Node/pnpm 状态并尝试临时库验收,未修改正式库或服务
- 完成内容:新增 CustomerGatewaysModule,提供
GET /customer-gateways、GET /customer-gateways/:id、POST /customer-gateways、PATCH /customer-gateways/:id、POST /customer-gateways/:id/enable、POST /customer-gateways/:id/disable;支持IP、SIP_DIGEST、MIXED三种认证模式;创建和更新时校验来源 IP、SIP 用户名、SIP 域和密码强度;SIP 注册密码仅用于生成 OpenSIPS Digest HA1,API 响应只返回hasSipCredential,不返回sipPassword或sipHa1;SIP 用户名或域名变更时要求同时提交新密码,避免旧 HA1 与新身份不匹配;所有写操作绑定customer_gateways.manage和审计动作,读操作绑定customer_gateways.view;日志脱敏路径新增sipHa1。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/customer-gateways/、packages/observability/src/index.ts、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;B 端执行只读hostname && node --version && pnpm --version;尝试通过 SSH 隧道连接 B MySQL 并用MYSQL_MIGRATE创建 S13 临时库,因账号无mysql系统库访问权限返回P1010;B sudo 凭据校验失败,未继续执行需要 sudo 的 MySQL 创建/清理命令。 - 验证结果:本地 typecheck 通过;Vitest 10 个测试文件 29 条测试通过,新增覆盖客户网关只读权限、写权限拒绝、IP 网关返回脱敏、SIP Digest 网关创建、审计敏感字段脱敏、SIP 身份变更强制新密码、切换 IP 模式清空 SIP 凭据、启停接口;lint 通过;workspace build 通过;B 的 Node
v22.22.2和 pnpm10.33.0只读确认通过;B 临时库冒烟未完成,未创建临时库,SSH 隧道、本地 S13 临时 SQL 和远端/tmp/lisglosips-s13-create-*.sql已清理。 - 回滚方式:移除
CustomerGatewaysModule导入并删除apps/api/src/modules/customer-gateways/;从packages/observability/src/index.ts移除*.sipHa1脱敏项;S13 未新增数据库迁移。B 端未创建临时库、未修改服务,仅使用短时 SSH 隧道和/tmp临时 SQL 文件,已清理。 - 未解决问题:S13 未执行 B 临时库冒烟,因为当前
.codex-private中的 B sudo 凭据无法通过校验,MYSQL_MIGRATE也无权在mysql系统库创建临时库;后续需要修复 B sudo 凭据或提供受控临时库创建账号。S13 不实现客户网关策略、Outbox 或 Redis 发布,这些留给 S14。 - 下一任务:
S14 - 客户网关策略与 Redis 配置发布
2026-06-21 11:14 - S12 充值与不可变余额流水
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:新增 RechargesModule,提供
POST /customers/:id/recharges、POST /vendors/:id/recharges和GET /recharges;客户和供应商充值在同一数据库事务内完成账户行锁定、余额读取、不可变充值流水写入、余额更新和幂等结果保存;重复相同幂等键返回原充值结果,不重复加余额;同一幂等键不同请求返回冲突;充值写操作绑定recharges.manage和审计动作,查询绑定recharges.view;并发写冲突和唯一键竞争只对 PrismaP2034/P2002做短退避重试。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/recharges/、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;B 端通过 SSH 隧道连接临时 MySQL 库执行prisma migrate deploy、db:seed、prisma migrate status,并使用编译后的充值仓库执行客户/供应商充值、幂等重放、幂等冲突和并发充值冒烟。 - 验证结果:本地 typecheck 通过;Vitest 9 个测试文件 24 条测试通过,覆盖充值权限拒绝、客户充值、供应商充值、幂等重放、幂等冲突、流水查询和审计记录;lint 通过;workspace build 通过;B 临时空库
lisglosips_s12_verify_20260621111322成功应用 S08/S09 共 2 个迁移,seed 执行成功,migrate status显示 schema up to date;B 临时库验证客户充值前后余额、供应商充值前后余额、重复幂等不重复入账、幂等冲突拒绝、5 路并发客户充值后余额15.000000、客户流水 6 条、供应商流水 1 条、成功幂等记录 7 条;B 残留检查确认无 S12 临时库、临时用户和/tmp/lisglosips-s12-*文件。 - 回滚方式:移除
RechargesModule导入并删除apps/api/src/modules/recharges/;S12 未新增数据库迁移。B 端仅创建临时库、临时用户和 SSH 隧道,已删除或关闭。 - 未解决问题:S12 只实现手工充值入账;CDR 扣费、余额扣减和扣费幂等留给 S22/S23;供应商管理 CRUD 仍按任务顺序留给 S15。
- 下一任务:
S13 - 客户网关 API
2026-06-21 10:57 - S11 客户管理 API
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:新增 CustomersModule,提供客户列表、详情、新增、编辑、启用、停用和软删除 API;返回余额、授信额度、最低余额、可用余额和启用网关数量;客户余额只读,避免绕过 S12 充值流水;所有写操作均绑定
customers.manage和审计动作,读操作绑定customers.view;软删除前阻止仍有关联启用网关的客户删除。 - 修改文件:
apps/api/src/modules/app.module.ts、apps/api/src/modules/customers/、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;B 端通过 SSH 隧道连接临时 MySQL 库执行prisma migrate deploy、db:seed、prisma migrate status和客户 CRUD 冒烟脚本。 - 验证结果:本地 typecheck 通过;Vitest 8 个测试文件 21 条测试通过,覆盖客户读权限、写权限拒绝、新增、启停、软删除、余额/授信字段和审计记录;lint 通过;workspace build 通过;B 临时空库
lisglosips_s11_verify_20260621105537成功应用 S08/S09 共 2 个迁移,seed 执行成功,migrate status显示 schema up to date;B 临时库客户创建、状态更新、Decimal 精度、网关计数和软删除冒烟通过;B 残留检查确认无 S11 临时库、临时用户和/tmp/lisglosips-s11-*文件。 - 回滚方式:移除
CustomersModule导入并删除apps/api/src/modules/customers/;S11 未新增数据库迁移。B 端仅创建临时库、临时用户和 SSH 隧道,已删除或关闭。 - 未解决问题:S11 不实现充值入账和余额变更事务,余额仍只能通过后续 S12 不可变流水更新;客户网关数量当前依赖后续 S13 的网关数据。
- 下一任务:
S12 - 充值与不可变余额流水
2026-06-21 10:46 - S10 用户、角色、权限与审计
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:实现全局 Access Token 认证 Guard、RBAC Guard、
@Public、@RequirePermissions与当前用户注入;新增用户、角色、权限和审计日志查询 API;实现用户创建、资料更新、重置密码和角色权限维护;内置角色禁止改名、停用和变更权限;重置密码仅保存 Argon2id 哈希并要求下次改密;新增审计拦截器,按接口元数据记录 request_id、操作者、角色、IP、User-Agent、模块、动作、对象、成功/失败、错误码和脱敏后的 before/after 摘要。 - 修改文件:
packages/auth/src/index.ts、packages/database/src/index.ts、apps/api/src/modules/app.module.ts、apps/api/src/modules/auth/auth.controller.ts、apps/api/src/modules/health/health.controller.ts、apps/api/src/modules/security/、apps/api/src/modules/audit/、apps/api/src/modules/audit-logs/、apps/api/src/modules/users/、apps/api/src/modules/roles/、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 Node.js
v22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 build;B 端通过 SSH 隧道连接临时 MySQL 库执行prisma migrate deploy、db:seed、prisma migrate status;B 端检查并清理lisglosips_s10_verify_*、s10v_*和/tmp/lisglosips-s10-*临时残留。 - 验证结果:本地 typecheck 通过;Vitest 7 个测试文件 18 条测试通过,覆盖未认证拒绝、越权拒绝、重置密码审计脱敏、内置角色保护和审计日志查询;lint 通过;workspace build 通过;B 临时空库
lisglosips_s10_verify_20260621104300成功应用 S08/S09 共 2 个迁移,seed 执行成功,migrate status显示 schema up to date;B 残留检查确认无 S10 临时库、临时用户和/tmp/lisglosips-s10-*文件。 - 回滚方式:恢复上述本地文件到 S10 前版本;S10 未新增数据库迁移,若需回滚只需移除新增模块导入和相关接口代码。B 端仅创建临时库、临时用户和
/tmp临时文件,已删除。 - 未解决问题:S10 只完成后端安全与审计基础;后续业务 API 必须继续显式标注权限和审计动作,客户、充值、网关等业务模块从 S11 开始实现。
- 下一任务:
S11 - 客户管理 API
2026-06-21 10:15 - S09 登录认证基础
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:实现登录、刷新和退出接口;新增 Argon2id 密码哈希与校验,优先使用 Node 原生实现并在 Node 22 上回退到 vendored WASM;实现 HMAC access token、HttpOnly refresh cookie、refresh token SHA-256 摘要入库、会话撤销与轮换、失败计数和临时锁定;新增 Prisma
user_sessions模型和迁移;补充 Auth API、服务层、密码/Token 原语和 schema 合同测试。 - 修改文件:
packages/auth/src/index.ts、packages/auth/src/index.spec.ts、packages/auth/vendor/argon2id/、apps/api/src/modules/auth/、apps/api/src/modules/database/、apps/api/src/modules/app.module.ts、apps/api/src/shared/config.ts、apps/api/tsconfig.json、apps/api/package.json、.env.example、eslint.config.mjs、prisma/schema.prisma、prisma/migrations/20260621093000_auth_sessions/migration.sql、packages/database/src/schema-contract.spec.ts、pnpm-lock.yaml、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;vendored npm 包
argon2id1.0.1,MIT 许可证,WASM 哈希记录于packages/auth/vendor/argon2id/UPSTREAM.md;继续使用 Node.jsv22.22.2、Prisma6.19.0、pnpm10.33.0。 - 执行的关键命令:
corepack pnpm@10.33.0 prisma:validate、prisma:generate、typecheck、test、lint、build;B 端通过临时/tmp包验证 Node 22 下 Argon2id WASM 哈希与校验;通过 SSH 隧道连接 B 的临时 MySQL 库执行prisma migrate deploy和prisma migrate status。 - 验证结果:本地 Prisma schema validate 通过;Prisma Client 生成通过;typecheck 通过;Vitest 6 个测试文件 15 条测试通过,覆盖 Argon2id、Token、登录成功、失败锁定、refresh 轮换、旧 refresh 拒绝、logout 后拒绝和健康检查;lint 通过;workspace build 通过;B Node
v22.22.2上 Argon2id WASM 校验结果为正确值通过、随机值拒绝;B 临时空库lisglosips_s09_verify_20260621101249成功应用 S08 与 S09 共 2 个迁移,migrate status显示 schema up to date;B 残留检查确认临时库、临时用户和/tmp/lisglosips-s09-*文件已清理。 - 回滚方式:恢复上述本地文件到 S09 前版本;删除
prisma/migrations/20260621093000_auth_sessions/并从prisma/schema.prisma移除UserSession关系和模型。B 端仅创建临时库、临时用户和/tmp临时文件,已删除。 - 未解决问题:生产 access token secret 必须由受控环境变量注入;当前 S09 只实现认证基础,RBAC Guard、内置角色保护、重置密码和审计拦截器留给 S10。
- 下一任务:
S10 - 用户、角色、权限与审计
2026-06-21 09:28 - S08 数据库 Schema 与迁移
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:实现 V2 第 10 章核心数据模型,包括客户、客户网关、策略、供应商、落地网关、线路组、充值、CDR、录音、质检、用户、角色、权限、审计、Outbox 和幂等键;生成初始化迁移;新增内置权限/角色 seed;补充数据库 Schema Runbook 和 schema 合同测试;迁移 Prisma seed 配置到
prisma.config.ts。 - 修改文件:
prisma/schema.prisma、prisma/migrations/20260621090000_init_v2_schema/migration.sql、prisma/seed.ts、prisma.config.ts、package.json、packages/database/src/schema-contract.spec.ts、docs/DATABASE_SCHEMA_RUNBOOK.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未安装新系统软件;继续使用 Prisma
6.19.0、MySQL8.0.46、pnpm10.33.0。 - 执行的关键命令:
prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script、corepack pnpm@10.33.0 prisma:generate、prisma:validate、typecheck、test、lint、build;通过 SSH 隧道连接 B 的临时 MySQL 库执行prisma migrate deploy、prisma db seed、重复 seed、重复 migrate deploy、prisma migrate status;使用mysqldump --no-tablespaces做临时库备份恢复验证。 - 验证结果:本地 Prisma schema validate 通过;Prisma Client 生成通过;lint 通过;typecheck 通过;Vitest 3 个测试文件 6 条测试通过;workspace build 通过;B 上临时空库
lisglosips_s08_verify_20260621091537成功应用 1 个迁移;seed 重复执行安全;重复migrate deploy显示无待迁移;migrate status显示 schema up to date;备份恢复后计数为permissions=22、roles=5、role_permissions=53、_prisma_migrations=1。 - 回滚方式:恢复上述本地文件到 S08 前版本;删除
prisma/migrations/20260621090000_init_v2_schema/。B 端仅创建临时库和临时用户,已删除lisglosips_s08_verify_*、s08v_*、/tmp/lisglosips-s08-*.sh和临时 dump 文件。 - 未解决问题:生产/准生产迁移仍需在正式发布窗口前做全量备份和恢复演练;本次只验证临时空库初始化、seed 幂等和 dump/restore。
- 下一任务:
S09 - 登录认证基础
2026-06-21 08:55 - S07 V2 后端 Monorepo 骨架完成
- 状态:已完成
- 操作服务器:本地、B
- 完成内容:恢复 B 开机后完成 S07 的 B 端构建验收;补充
scripts/pnpm-run.mjs,让根脚本复用当前 pnpm 运行器,兼容本机 Corepack 调用和 B 上已安装的/usr/bin/pnpm;在package.json中声明允许固定依赖的 pnpm build scripts,并加入 Linux x64 glibc optional dependency 支持,便于 B 端验证 Rollup/esbuild/Prisma。 - 修改文件:
package.json、pnpm-lock.yaml、scripts/pnpm-run.mjs、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:未在 B 安装新系统软件;B 使用既有 Node.js
v22.22.2、Corepack0.34.6、pnpm10.33.0。本地和 B 的项目依赖仍按pnpm-lock.yaml固定。 - 执行的关键命令:本地
corepack pnpm@10.33.0 lint/typecheck/test/prisma:validate/build;B/tmp/lisglosips-s07-20260621081708中执行pnpm install --frozen-lockfile、离线 store 安装、pnpm lint、pnpm typecheck、pnpm test、pnpm prisma:validate、pnpm build、临时 APInode apps/api/dist/main.js健康检查。 - 验证结果:本地 lint、typecheck、Vitest 2 个测试文件 3 条测试、Prisma schema validate、workspace build 全部通过;B 端 lint、typecheck、Vitest 2 个测试文件 3 条测试、Prisma schema validate、workspace build 全部通过;B 本机临时 API
/api/v2/health/live返回ok、/api/v2/health/ready返回ok、/api/v2/openapi.json返回 HTTP 200。 - 回滚方式:恢复本次新增/修改的 monorepo 文件;如需恢复旧前端结构,将
apps/web/src移回根目录src、apps/web/index.html移回根目录,并恢复旧package.json/package-lock.json。B 端仅使用/tmp临时目录和临时 pnpm store,已删除/tmp/lisglosips-s07-*、/tmp/lisglosips-s07-src-*.zip、/tmp/lisglosips-s07-pnpm-store-v10.tar.gz、/tmp/rollup-linux-x64-gnu-4.62.2.tgz及 API 冒烟临时文件。 - 未解决问题:B 到 npm registry 的 TLS 连接不稳定,在线安装多次
ECONNRESET;本次通过本地 pnpm store 临时上传完成 B 端构建验收。后续正式部署建议使用内网 npm 缓存、制品仓库或在 CI 产出完整 release artifact。 - 下一任务:
S08 - 数据库 Schema 与迁移
2026-06-21 08:10 - S07 V2 后端 Monorepo 骨架
- 状态:已阻塞
- 操作服务器:本地;Server B 仅尝试临时源码包上传和只读 SSH 连通性检查,未确认远端执行
- 完成内容:将现有 React Demo 纳入 pnpm monorepo 的
apps/web;创建apps/apiNestJS + Fastify 后端骨架;创建apps/worker-cdr、apps/worker-recording、apps/worker-config-publisherworker 入口;创建packages/contracts、domain、database、redis、auth、observability公共包;建立 Prisma MySQL schema 占位、结构化日志脱敏、配置校验、OpenAPI JSON/Swagger 入口、健康检查、Vitest/Supertest 测试和 lint/typecheck/build/ci 命令。 - 修改文件:
package.json、pnpm-workspace.yaml、pnpm-lock.yaml、tsconfig.base.json、tsconfig.build.json、eslint.config.mjs、vitest.config.ts、.env.example、.gitignore、apps/、packages/、prisma/schema.prisma;原src/和index.html已移动到apps/web/。 - 安装软件及固定版本:本地通过 Corepack 使用 pnpm
10.33.0;关键依赖固定包括 NestJS11.1.9、Fastify5.6.2、Prisma6.19.0、ioredis5.8.2、pino9.14.0、Vitest4.0.14、TypeScript5.9.3、Vite5.4.21。 - 执行的关键命令:
corepack pnpm@10.33.0 install、corepack pnpm@10.33.0 prisma:generate、corepack pnpm@10.33.0 lint、corepack pnpm@10.33.0 typecheck、corepack pnpm@10.33.0 test、corepack pnpm@10.33.0 prisma:validate、corepack pnpm@10.33.0 build、本地 APInode apps/api/dist/main.js冒烟检查。 - 验证结果:本地
lint通过;typecheck通过;Vitest 2 个测试文件 3 条测试通过;Prisma schema validate 通过;全 workspace build 通过;本地 API 冒烟/api/v2/health/live返回ok、/api/v2/health/ready返回ok、/api/v2/openapi.json返回 HTTP 200;测试和脚本只使用无密码占位连接串。 - 回滚方式:恢复本次新增/修改的 monorepo 文件;如需恢复旧前端结构,将
apps/web/src移回根目录src、apps/web/index.html移回根目录,并恢复旧package.json/package-lock.json。 - 未解决问题:未完成 B 端构建验收。本机到
100.90.90.91:22SSH 超时;本机无tailscaleCLI;scp曾尝试上传/tmp/lisglosips-s07-src-20260621080628.zip,返回码为 0 但随后提示连接关闭,因 SSH 不通无法确认远端是否存在该临时文件,恢复连通后应检查并删除。 - 下一任务:恢复 Server B SSH/Tailscale 可达性后继续完成
S07 - V2 后端 Monorepo 骨架的 B 端构建验收;不要进入 S08。
2026-06-21 00:20 - SSH 策略文档修订
- 状态:已完成
- 操作服务器:本地
- 完成内容:将 SSH 加固原则调整为“只调整 SSH 管理端口和允许来源,不禁止 SSH 密钥登录”;端口号统一使用
<SSH_ADMIN_PORT>占位,待用户确认具体端口后再实施。 - 修改文件:
SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md、docs/SSH_ACCESS_RUNBOOK.md、docs/LISGLOSIPS_DEPLOYMENT_GUIDE.md、docs/infra-check.md、docs/SERVER_B_BASELINE_RUNBOOK.md - 安装软件及固定版本:无。
- 执行的关键命令:
rg文档复查。 - 验证结果:需求/部署文档中已无“仅 Key 登录”“禁用密码登录”作为强制策略的表述;历史盘点文件保留当时实际
TCP 22快照,docs/infra-check.md中历史验收项已标注后续迁移为<SSH_ADMIN_PORT>。 - 回滚方式:恢复上述文档至本次修改前版本。
- 未解决问题:尚未确认
<SSH_ADMIN_PORT>的具体端口号,未执行服务器侧 SSH 端口迁移。 - 下一任务:
S07 - V2 后端 Monorepo 骨架
2026-06-20 23:20 - S06 Server B HOMER 与基础监控
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:在 B 部署 PostgreSQL 16 HOMER 数据库、Heplify HEP 接收、Homer App、Prometheus、Grafana、MySQL/PostgreSQL/Redis/Node/Heplify exporters 和 Nginx 监控入口;在 A 部署 Node Exporter,并用独立 nftables 表限制仅 B 可抓取;重新签发开发证书,增加
grafana.lisglosips.local与homer.lisglosips.local;补充 S06 Runbook 和可重复部署脚本。 - 修改文件:B 的
/etc/homer/、/etc/lisglosips/secrets/、/etc/prometheus/、/etc/grafana/、/etc/nginx/sites-available/monitoring.conf、/etc/systemd/system/S06 服务、/data/homer/postgresql;A 的/etc/nftables.d/lisglosips-s06-exporter.nft、/etc/systemd/system/lisglosips-exporter-firewall.service、/etc/systemd/system/lisglosips-node-exporter.service;本地infra/server-b/s06/、infra/server-a/s06/、docs/SERVER_B_HOMER_MONITORING_RUNBOOK.md、docs/LISGLOSIPS_DEPLOYMENT_GUIDE.md、.codex-private/SERVICE_CREDENTIALS.clixml。 - 安装软件及固定版本:PostgreSQL
16.14、Prometheus2.45.3、Grafana13.0.2、Node Exporter1.7.0、MySQL Exporter0.15.0、PostgreSQL Exporter0.15.0、Redis Exporter1.86.0;Heplify Serverv1.60.2-0.20260512101233-c74dc3d216ac;Homer Appv0.0.0-20251021161517-9b1336352aa0;Docker29.1.3与 Compose2.40.3已安装但禁用,不作为 S06 运行时。 - 执行的关键命令:
apt-get install、Goinstall/build、psql、mysql、redis-cli、promtool check config/rules、nginx -t、systemctl daemon-reload/enable/restart/reboot、curl、nft -c/-f、HEPv3 UDP 测试脚本。 - 验证结果:B 重启后系统
running且 0 failed unit;A 重启后系统running且 0 failed unit;B 的 PostgreSQL、MySQL、Redis、Nginx、Heplify、Homer、Grafana、Prometheus 和 4 个 exporter 全部 active;Prometheus 7 个 target 全部up;A Node Exporter 仅监听100.90.90.90:9100,B 抓取返回 200,T 访问超时;HEPv3INVITE从 A 发往 B 后写入homer_data.hep_proto_1_call,方法和源/目标 IP 正确;Grafana HTTPS health 通过,已加载Prometheus、HOMER PostgreSQLdatasource 和LisgloSIPS Infrastructure Overviewdashboard;Homer HTTPS 返回 200;Homer/Grafana/MySQL Exporter 近 10 分钟无错误日志。 - 回滚方式:B 备份位于
/var/backups/lisglosips-s06/20260620T132440Z,A 备份位于/var/backups/lisglosips-s06/20260620225517;停止 S06 服务、恢复配置、保留并单独备份/data/homer/postgresql后再处理数据目录;A 回滚只删除inet lisglosips_s06表和两个 S06 服务,不触碰 OpenSIPS/RTP。 - 未解决问题:外部对象存储下载被代理链路阻断,未继续采用 HOMER10 Docker 架构;当前 HOMER Web 是最小状态页,HOMER API、HEP 入库和 Grafana 可用,完整 HOMER UI 静态包待网络条件稳定后补;A 的 9100 保护只是 S06 局部规则,完整 A 防火墙留到 S18/S20。
- 下一任务:
S07 - V2 后端 Monorepo 骨架
2026-06-20 20:44 - S05 Server B Node.js、Nginx 与 TLS 基线
- 状态:已完成
- 操作服务器:B、T、本地
- 完成内容:固定 Node.js 22 LTS、Corepack、pnpm 和 Nginx 版本;建立
/opt/lisglosips/releases与原子current发布模型;部署受限lisglosips@.service模板和无依赖 API 健康占位服务;配置 Nginx 静态站点、API 代理、登录/API 限流、安全响应头、内部录音 Range 入口;签发仅供开发使用的私有 CA 证书;另建全项目独立部署指南。 - 修改文件:B 的
/etc/lisglosips/web-runtime-versions.env、/etc/lisglosips/api.env、/etc/lisglosips/pki/、/etc/nginx/LisgloSIPS 配置、/etc/systemd/system/lisglosips@.service、/usr/local/sbin/lisglosips-issue-dev-cert和/opt/lisglosips/releases/s05-placeholder-20260620;本地infra/server-b/s05/、docs/SERVER_B_WEB_RUNTIME_RUNBOOK.md、docs/LISGLOSIPS_DEPLOYMENT_GUIDE.md、.codex-private/tls/lisglosips-dev-ca.crt。 - 安装软件及固定版本:Node.js
v22.22.2(包22.22.2-1nodesource1)、Corepack0.34.6、pnpm10.33.0、Nginx1.24.0-2ubuntu7.12;Node/Nginx 包已 hold,升级前必须复核安全公告与兼容性。 - 执行的关键命令:
apt-get install、apt-mark hold、openssl、nginx -t、systemctl daemon-reload/enable/restart/reboot、curl、systemd-analyze security、sha256sum。 - 验证结果:内部和管理端 HTTPS 健康检查通过;证书链与
100.90.90.91SAN 校验通过;仅 TLS 1.2/1.3;首页 HSTS/CSP/防嵌套等安全头完整;登录限流测试出现预期 429;/_recordings/外部访问为 404;应用只监听127.0.0.1:3000,本地管理机和 T 均不能直连 3000,T 不能访问 443;systemd 安全评级为 OK;重启后 Nginx、API、MySQL、Redis 和备份 timer 正常,0 个 failed unit;11 个仓库配置与服务器文件哈希一致;文档敏感信息扫描通过。 - 回滚方式:切换
/opt/lisglosips/current可回滚应用;S05 变更前备份位于/var/backups/lisglosips-s05/20260620T123432Z,配置/证书/服务恢复步骤见docs/SERVER_B_WEB_RUNTIME_RUNBOOK.md。 - 未解决问题:当前是开发私有 CA,迁移阿里云必须替换正式域名证书;80 已监听但仍由 nftables 拒绝,生产是否开放取决于 ACME 方案;NodeSource 在本地代理链路上偶发 TLS 握手中断,现有固定包和运行不受影响;当前 API 仅为 S05 健康占位服务,S07 替换。
- 下一任务:
S06 - Server B HOMER 与基础监控
2026-06-20 20:22 - S04 Server B MySQL 与 Redis
- 状态:已完成
- 操作服务器:B、A、T、本地
- 完成内容:在 B 安装并固定 MySQL 8.0.46 与 Redis 7.0.15;建立
/data/mysql、/data/redisbind mount;完成 MySQL binlog、UTC、utf8mb4、慢查询和最小权限账号配置;完成 Redis Tailscale 私网绑定、ACL、AOF/RDB 和noeviction配置;建立 Tailscale 地址就绪依赖,消除 Redis 开机绑定竞态;配置每日 MySQL/Redis 自动备份及 14 天保留。 - 修改文件:B 的
/etc/mysql/mysql.conf.d/99-lisglosips.cnf、/etc/redis/redis-lisglosips.conf、/etc/redis/users.acl、/etc/fstab、相关 systemd unit/drop-in、备份脚本及/etc/lisglosips/secrets/;本地docs/SERVER_B_DATA_SERVICES_RUNBOOK.md、infra/server-b/s04/、.codex-private/SERVICE_CREDENTIALS.clixml。 - 安装软件及固定版本:MySQL
8.0.46-0ubuntu0.24.04.2;Redis5:7.0.15-1ubuntu0.24.04.4;相关 MySQL/Redis 包已apt-mark hold,升级前须先评估安全公告与兼容性。 - 执行的关键命令:
apt-get install、apt-mark hold、mysql、mysqlcheck、redis-cli、redis-check-rdb、systemctl、findmnt、sha256sum、systemctl reboot。 - 验证结果:MySQL 仅监听本机,应用账号 DML 通过且 DDL 被拒;Redis 未认证访问被拒,应用账号读写通过且危险命令被拒,A 可通过 Tailscale 认证访问,T 到 3306/6379 被防火墙拒绝;MySQL 与 Redis 备份哈希通过,并分别完成隔离恢复和数据一致性验证;重启后 mount、服务、数据和 timer 均正常,Redis
NRestarts=0,0 个 failed unit;11 个仓库配置与服务器文件 SHA-256 完全一致;仓库敏感信息、私密文件忽略规则和 ACL 检查通过。 - 回滚方式:变更前及迁移前数据保存在 B 的
/var/backups/lisglosips-s04/20260620195847;配置、数据目录和服务恢复步骤见docs/SERVER_B_DATA_SERVICES_RUNBOOK.md。 - 未解决问题:开发阶段
/data仍位于根盘,正式上云需改为独立数据盘;固定版本需要纳入安全补丁复核;Tailscale 首次直连协商偶尔产生短时延迟,稳态访问正常。 - 下一任务:
S05 - Server B Node.js、Nginx 与 TLS 基线
2026-06-20 19:01 - S03 Server B 基础系统初始化
- 状态:已完成
- 操作服务器:B、本地
- 完成内容:升级 101 个系统包并启用 6.17.0-35 内核;安装基础工具;建立 chrony、nftables、fail2ban、sysstat;创建三个 LisgloSIPS 系统用户和
/data目录基线;修复 NFS 为 Tailscale 就绪后的 automount;完成重启验收。 - 修改文件:B 的
/etc/fstab、/etc/nftables.conf、/etc/nftables.d/lisglosips.nft、/etc/chrony/chrony.conf、/etc/chrony/sources.d/lisglosips.sources、/etc/fail2ban/jail.d/lisglosips.local、/etc/tmpfiles.d/lisglosips.conf;本地docs/SERVER_B_BASELINE_RUNBOOK.md、infra/server-b/s03/。 - 安装软件及固定版本:chrony 4.5-1ubuntu4.2、nftables 1.0.9-1ubuntu0.1、fail2ban 1.0.2-3ubuntu0.1;其余基础工具版本见 Runbook 和远端 post-change 包清单。
- 执行的关键命令:
apt-get dist-upgrade/install、chronyc、nft、fail2ban-client、systemd-tmpfiles、findmnt、systemctl reboot。 - 验证结果:新内核启动;0 个 failed unit;无待升级包、无 reboot-required;chrony Stratum 3/Leap Normal;NFS automount 重启后可访问;fail2ban sshd jail active;nftables 重启后默认拒绝,开发机与 A 允许路径通过,T 到 B 的 22/443/6379/9060 被拒绝;远端配置与仓库副本哈希一致。
- 回滚方式:变更前备份
/var/backups/lisglosips-s03/20260620184404;防火墙可执行其中rollback-firewall.sh;其余恢复方式见 Runbook。 - 未解决问题:开发阶段
/data仍位于根盘;桌面/CUPS/Avahi/rpcbind 尚未精简但已被防火墙隔离;ens18 双地址/双默认路由和克隆 machine-id 仍待单独维护窗口处理。 - 下一任务:
S04 - Server B MySQL 与 Redis
2026-06-20 18:37 - S02 网络与安全组验收
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:确认本地开发服务器和 Tailscale 专网边界;采集六个方向稳态 RTT;验证 SIP、RTP、Web、HEP、Redis、监控与 SSH 网络路径;形成开发最小暴露矩阵和未来阿里云安全组清单。
- 修改文件:
docs/infra-check.md、docs/inventory-A.md、docs/inventory-B.md、docs/inventory-summary.md、SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:无。
- 执行的关键命令:
tailscale status/ping/netcheck、ping、Python 短时 TCP/UDP 监听与 ACK、SIP OPTIONS、ss、OpenSIPS/MariaDB 只读核验。 - 验证结果:A/B/T 六方向 0% 丢包,预热后平均 RTT 0.434-0.648 ms;1200 字节 DF 报文通过;T->A 15060/UDP 与 RTP 30000/40000 双向通过;A->T SIP 5060 返回合法 484;开发机->B 443、A->B 9060/6379、B->A 9100/22 通过;所有临时监听器均已退出。
- 回滚方式:未修改远端持久配置;短时监听器已自动退出,无需回滚。
- 未解决问题:主机防火墙尚未落地;Tailscale direct 路径曾短暂重新协商导致 SSH 超时;ens18 双地址/双默认路由仍存在;T 的权限与 RTPEngine 内核模块问题留待对应任务处理。
- 测试数据:T 的
sip_trace新增 1 条 S02 OPTIONS 记录,acc和missed_calls未增加。 - 下一任务:
S03 - Server B 基础系统初始化
2026-06-20 18:00 - S01 三机只读资产盘点
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:只读采集三机 OS、内核、CPU、内存、磁盘、网络、路由、时间、端口、服务、软件和防火墙;检查 T 的 OpenSIPS、RTPEngine、MariaDB、录音目录、配置结构与运行统计。
- 修改文件:
docs/inventory-A.md、docs/inventory-B.md、docs/inventory-T.md、docs/inventory-summary.md、IMPLEMENTATION_STATUS.md - 安装软件及固定版本:无。
- 执行的关键命令:
hostnamectl、lscpu、lsblk、ip、ss、systemctl、ufw status、nft list ruleset、opensips -C、opensips-cli、rtpengine-ctl、MariaDB 只读查询。 - 验证结果:A/B/T SSH 复验通过;T 的 OpenSIPS、RTPEngine、录音守护进程、MariaDB、Apache 均 active;OpenSIPS 主配置哈希与盘点时一致;报告完整性及敏感信息扫描通过。
- 回滚方式:S01 未修改远端配置、数据或服务,无需回滚。
- 未解决问题:三机克隆身份相同;均有双 IPv4/双默认路由;UFW 未启用;B 的 NFS mount 失败;T 的 TLS 私钥为 0644、MI FIFO 为 0666、RTPEngine 无内核模块;当前 T 无注册账号和路由数据。
- 下一任务:
S02 - 网络与安全组验收
2026-06-20 15:08 - S00 SSH 安全接入与凭据迁移
- 状态:已完成
- 操作服务器:A、B、T、本地
- 完成内容:生成项目专用 Ed25519 Key;三机安装公钥;发现并修复克隆服务器共享 SSH host key;轮换
hector密码;建立严格 known_hosts 和 SSH 别名。后续 SSH 变更按最新要求只调整管理端口,不禁止 SSH 密钥登录。 - 修改文件:
.codex-private/ssh/lisglosips_codex_ed25519、.codex-private/ssh/config、.codex-private/ssh/known_hosts、.codex-private/SERVER_CREDENTIALS.clixml、docs/SSH_ACCESS_RUNBOOK.md - 安装软件及固定版本:本地私密目录安装 Paramiko 5.0.0,仅用于首次安全接入自动化。
- 验证结果:A/B/T 均通过 Key 新会话;
sshd -t通过;SSH 密钥登录保持开启;新 sudo 密码逐台验证通过。 - 回滚方式:见
docs/SSH_ACCESS_RUNBOOK.md,三台服务器/var/backups/lisglosips-s00/保留轮换前和加固前备份。 - 未解决问题:尚未盘点三机软件、磁盘、服务和网络;容量参数与 RTP 端口段仍未确认。
- 下一任务:
S01 - 三机只读资产盘点
2026-06-20 - 计划初始化
- 状态:已完成
- 操作服务器:本地
- 完成内容:建立 Codex 原子任务列表、服务器索引和跨会话交接规范。
- 修改文件:
SOFTSWITCH_PLATFORM_DESIGN_V2.md、IMPLEMENTATION_STATUS.md、.gitignore、.codex-private/SERVER_ACCESS.md - 验证结果:等待开始 S00。
- 未解决问题:尚未连接服务器;尚未确认容量参数和 RTP 端口段。
- 下一任务:
S00 - SSH 安全接入与凭据迁移