# LisgloSIPS V2 项目设计与实施文档 > 中文名:聆界SIP管理平台 > 文档版本:V2.0 > 编制日期:2026-06-20 > 文档状态:实施基线草案 > 依据:服务器架构图、`SOFTSWITCH_PLATFORM_DESIGN_V1.md`、当前 React Web Demo、`OPENSIPS_INSTALL_NOTES.md` ## 1. 文档目的 V2 的目标是把当前纯前端原型转化为可部署、可联调、可上线的软交换运营平台。本文不仅描述产品功能,还给出服务器部署、软件安装、后端代码、OpenSIPS/RTPEngine 对接、数据库、Redis、录音迁移、测试和上线方案。 本文作为下一阶段执行基线,后续数据库 DDL、OpenAPI、OpenSIPS 脚本和部署脚本应从本文拆分并版本化管理。 ## 2. V2 范围 ### 2.1 本期实施页面 以下菜单已在 Web Demo 中完成交互设计,纳入 V2 后端和联调范围: | 菜单 | V2 实施内容 | | --- | --- | | 概览 Dashboard | 通话、接通率、消费、成本、毛利、注册、节点、异常网关、质检指标 | | 客户管理 | 客户新增、编辑、启停、余额、授信、充值、网关数量 | | 客户网关管理 | IP/SIP 注册认证、启停、多个策略、优先级、主被叫匹配、线路组绑定 | | 充值记录 | 客户充值、供应商充值、余额前后值、操作人、备注 | | 供应商管理 | 供应商账户、余额、授信、充值 | | 落地网关管理 | 认证、并发、CPS、禁呼时段、编码、号码转换、计费周期、费率、启停 | | 落地线路组 | 线路组、组内网关、优先级、并发汇总 | | 话单中心 | 话单查询、挂断原因、费用、录音、信令入口、详情 | | 质检中心 | 抽检规则、录音列表、连续播放、自动播放、问题标注、评分 | | 用户管理 | 用户新增、编辑、启停、重置密码、角色绑定 | | 角色与权限 | 角色、权限矩阵、角色用户数、内置角色保护 | | 操作日志 | 条件查询、结果、详情、导出 | ### 2.2 延期页面 以下菜单在 Web Demo 中标记为“待设计”,本期不开发其业务页面: - 费率与计费 - SIP 运维 - 监控告警 - 系统设置 说明:页面延期不代表底层能力完全取消。V2 仍需实现: - 最小计费引擎,用于生成话单客户费用、成本费用并更新余额。 - Prometheus/Grafana 基础设施监控,但不接入自研“监控告警”页面。 - OpenSIPS/RTPEngine 配置文件和环境变量管理,但不开放自研“系统设置”页面。 - HOMER 信令查询入口,但不开发自研“SIP 运维”页面。 ### 2.3 明确不做 - 客户自助门户。 - 多 OpenSIPS 节点和通话级高可用。 - 完整账单、发票、对账和复杂费率管理页面。 - 自研可视化 SIP Trace、监控告警和系统配置页面。 - Kubernetes。双机阶段使用 systemd、Nginx 和 Docker Compose 的有限组合。 ## 3. 总体架构 ### 3.1 服务器规划 环境分为两个阶段: - 当前开发阶段:A、B、T 均为本地 KVM 虚拟机,通过 Tailscale 专网联调。 - 开发完成后:将 A、B 部署到阿里云,按下表建立 VPC、公网入口和安全组;T 保留为外部 SIP 测试端。 下表为最终阿里云生产目标,不代表当前本地开发机已经位于阿里云: | 服务器 | 规格 | 定位 | 公网暴露 | | --- | --- | --- | --- | | Server A | 阿里云计算型 4C8G | 核心通信网关 | SIP `15060/UDP`、RTP UDP 端口段 | | Server B | 阿里云通用型 4C16G | 业务、缓存、数据库、录音、监控中心 | `443/TCP`,可选 `80/TCP` 跳转 HTTPS | 两台服务器必须位于同一 VPC、同一可用区或低时延可用区,使用私网 IP 通信。目标私网 RTT 小于 1 ms。 本地开发阶段的实测网络、端口矩阵和未来阿里云安全组清单见 `docs/infra-check.md`。 ### 3.2 逻辑拓扑 ```mermaid flowchart LR UA["客户 SIP / 上游线路"] -->|"SIP 15060/UDP"| OS["Server A: OpenSIPS"] UA <-->|"RTP UDP 端口段"| RTP["Server A: RTPEngine"] OS <-->|"call-time Redis 查询"| REDIS["Server B: Redis"] OS -->|"Redis Stream: CDR"| REDIS OS -->|"HEP/9060 私网"| HEP["Server B: Heplify-server + HOMER"] RTP --> TMP["Server A: /dev/shm/voip_rec"] WORKER["Server B: Recording Worker"] -->|"私网拉取 ready 文件"| TMP WORKER --> SSD["Server B: 录音数据盘"] API["Server B: LisgloSIPS API"] --> MYSQL["Server B: MySQL"] API --> REDIS CDRW["Server B: CDR/Billing Worker"] --> REDIS CDRW --> MYSQL WEB["React Web + Nginx"] --> API EXPORTER["Server A: Exporters"] --> PROM["Server B: Prometheus + Grafana"] ``` ### 3.3 Server A 职责 - OpenSIPS:SIP 鉴权、客户识别、策略匹配、路由、Dialog/CDR 事件。 - RTPEngine:媒体锚定、NAT 穿透、双向录音、内核态转发。 - `/dev/shm/voip_rec`:3 GiB tmpfs 录音缓冲区。 - HEP 发送端:向 Server B 发送信令镜像。 - Node Exporter 和自定义 textfile 指标。 - Fail2ban、nftables、OpenSIPS `pike`/`ratelimit` 防护。 “无状态”定义为不在 Server A 持久化业务主数据。OpenSIPS 运行时仍会维护事务和 Dialog 内存状态,服务重启会影响现有通话,V2 单节点接受此限制。 ### 3.4 Server B 职责 - Nginx、React 静态资源、HTTPS 和 API 反向代理。 - LisgloSIPS API、CDR Worker、Recording Worker、Config Publisher。 - Redis:呼叫热数据、配置版本、锁、Redis Streams。 - MySQL:业务主数据、充值流水、话单、质检、权限、审计。 - 录音数据盘:保存迁移后的录音文件。 - Heplify-server、HOMER UI 及其独立存储。 - Prometheus、Grafana 及必要 exporters。 ## 4. 架构图必须修正的两点 ### 4.1 RTP 端口不能只开放 SIP 端口 架构图写明 Server A 公网仅暴露 `15060/UDP`,这不足以完成 RTP 媒体通信。若 RTPEngine 负责公网媒体锚定,安全组和主机防火墙还必须开放 RTPEngine 配置的 UDP 端口范围,例如: ```text SIP: 15060/UDP RTP: 30000-40000/UDP ``` 端口范围应根据并发测算缩小,并尽量限制来源运营商或客户 IP 段。若上游 RTP 来源不可预知,需要保留公网 UDP 范围并强化速率限制和监控。 ### 4.2 CDR 使用 Redis Stream 架构图中的 `queue:cdr_payload` 不应实现为无法确认消费的普通 Redis List。V2 使用 Redis Stream: ```text stream:cdr_payload consumer group: billing-workers ``` 要求支持: - 消息 ID 和业务幂等键。 - `XREADGROUP` 消费。 - MySQL 提交成功后 `XACK`。 - Pending 消息重领。 - 死信 Stream。 - Redis 暂时不可用时 Server A 本地受限缓冲或告警降级。 ## 5. 技术栈与下载清单 ### 5.1 版本原则 - 服务器统一 Ubuntu 24.04 LTS。 - OpenSIPS 使用项目已验证的 3.6.x 分支。 - 其他软件选择受支持稳定版,禁止部署时无条件升级“最新版”。 - 所有版本写入 `versions.env`、部署脚本或容器镜像标签。 - 先在预生产执行安装、通话和性能验证,再用于生产。 ### 5.2 Server A 软件 | 软件 | 用途 | 安装来源 | | --- | --- | --- | | OpenSIPS 3.6.x | SIP 核心 | https://apt.opensips.org/ | | opensips-cli | 管理与检查 | OpenSIPS APT | | OpenSIPS Redis/HTTP/JSON 模块 | Redis 热路径、MI、事件 | OpenSIPS APT | | RTPEngine daemon + kernel module | RTP 转发与录音 | https://github.com/sipwise/rtpengine | | Heplify client 或 OpenSIPS tracer/HEP | 信令镜像 | https://github.com/sipcapture/heplify | | Node Exporter | 主机指标 | https://prometheus.io/docs/guides/node-exporter/ | | Fail2ban、nftables | 主机防护 | Ubuntu APT | | chrony | 时间同步 | Ubuntu APT | 建议 OpenSIPS 包: ```text opensips opensips-cli opensips-redis-module opensips-http-modules opensips-json-module opensips-tls-module opensips-auth-modules opensips-mysql-dbschema ``` 实际包名以 OpenSIPS 3.6 Ubuntu 24.04 仓库为准,安装脚本执行前用 `apt-cache search opensips` 校验。 ### 5.3 Server B 软件 | 软件 | 用途 | 安装来源 | | --- | --- | --- | | Node.js LTS | API 和 Worker | https://nodejs.org/en/about/previous-releases | | pnpm | Node.js 包管理 | Corepack | | Nginx | Web、HTTPS、反向代理 | https://nginx.org/en/linux_packages.html | | Redis | 热数据与 Streams | https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/ | | MySQL | 业务持久化 | https://dev.mysql.com/downloads/repo/apt/ | | Heplify-server + HOMER UI | 信令接收和排障 | https://github.com/sipcapture/heplify-server | | Prometheus | 指标存储 | https://prometheus.io/docs/prometheus/latest/installation/ | | Grafana | 监控大盘 | https://grafana.com/docs/grafana/latest/setup-grafana/installation/debian/ | | mysqld_exporter、redis_exporter | 数据库与缓存指标 | Prometheus Community | | Docker Engine/Compose | 仅用于 HOMER 组合服务 | https://docs.docker.com/engine/install/ubuntu/ | | ffmpeg | 录音格式校验与转换 | Ubuntu APT | HOMER 数据与业务 MySQL 必须逻辑隔离。优先使用 HOMER 官方 Compose 所要求的数据库,不把 HEP 海量信令写入业务库。 ## 6. 网络、安全组和端口 ### 6.1 Server A 入站 | 端口 | 来源 | 说明 | | --- | --- | --- | | `15060/UDP` | 客户/供应商允许列表 | SIP | | `30000-40000/UDP` | RTP 对端或允许网段 | RTPEngine 媒体,最终按容量调整 | | `/TCP` | 堡垒机或管理 VPN | SSH 管理端口,不对全网开放;保留密钥登录 | ### 6.2 Server B 入站 | 端口 | 来源 | 说明 | | --- | --- | --- | | `443/TCP` | 管理端用户 | LisgloSIPS HTTPS | | `80/TCP` | 可选 | 跳转 HTTPS,不承载登录 | | `9060/UDP` | Server A 私网 IP | HEP | | `/TCP` | 堡垒机或管理 VPN | SSH 管理端口;保留密钥登录 | Redis `6379`、MySQL `3306`、Prometheus `9090`、Grafana `3000`、应用端口、HOMER 内部端口不得直接暴露公网。Redis 和 MySQL至少绑定私网 IP,并通过安全组仅允许明确来源;同机服务优先使用 `127.0.0.1`。 ### 6.3 安全要求 - 全部服务器启用 chrony,CDR、HOMER 和审计日志时间必须一致。 - Web 强制 HTTPS、HSTS、SameSite Cookie 和 CSRF 防护。 - 管理员密码使用 Argon2id;不记录明文密码。 - SIP 注册密码按 OpenSIPS Digest 需要保存 HA1 或受控密文,不直接返回前端。 - MI HTTP 只绑定 Server A `127.0.0.1`。 - 所有生产 Secret 放入权限为 `0600` 的环境文件或云 Secret 服务。 - 操作日志不得记录密码、完整 Authorization、Cookie、录音签名 URL。 ## 7. 基础环境部署 ### 7.1 阿里云资源 1. 创建 VPC 和私有子网。 2. 创建 Server A 4C8G 计算型实例和 Server B 4C16G 通用型实例。 3. Server B 挂载独立 ESSD 数据盘,业务数据库、录音和监控数据使用独立目录或分区。 4. 配置安全组、EIP、DNS 和快照策略。 5. 建议先创建同规格预生产环境,至少在上线前保留 3 天完整演练时间。 ### 7.2 两台服务器通用初始化 ```bash sudo apt update && sudo apt full-upgrade -y sudo apt install -y curl wget ca-certificates gnupg jq git unzip chrony \ nftables fail2ban vim htop iotop sysstat lsof net-tools tcpdump sudo timedatectl set-timezone Asia/Shanghai sudo systemctl enable --now chrony nftables fail2ban ``` 生产环境推荐系统内部统一存 UTC,前端按 `Asia/Shanghai` 展示;若操作系统使用上海时区,数据库字段仍需明确 UTC 语义。 ### 7.3 Server A tmpfs ```bash sudo mkdir -p /dev/shm/voip_rec sudo chown rtpengine:rtpengine /dev/shm/voip_rec sudo chmod 0750 /dev/shm/voip_rec ``` `/etc/fstab`: ```fstab tmpfs /dev/shm/voip_rec tmpfs rw,nosuid,nodev,noexec,size=3G,mode=0750 0 0 ``` 3 GiB 缓冲不是长期存储。按双向 G.711 原始音频约 16 KiB/s 粗略估算,只能容纳约 53 个“并发小时”,未计算容器和文件头开销。100 路持续录音时缓冲可能不到 35 分钟。Recording Worker 建议 5-15 秒扫描一次,使用量超过 70% 告警,超过 85% 时停止接受新的非强制录音或触发紧急迁移。 ### 7.4 Server B 数据盘目录 ```text /data/mysql /data/redis /data/recordings/YYYY/MM/DD /data/prometheus /data/grafana /data/homer /data/backups ``` 数据盘挂载参数、文件系统和 IOPS 规格需根据压测结果确认。录音目录和 MySQL 不建议共享同一个容易打满的根分区。 ## 8. OpenSIPS 与 RTPEngine 实施 ### 8.1 OpenSIPS 呼叫路径 ```text INVITE -> 基础合法性与防扫描检查 -> IP 或 SIP Digest 认证 -> Redis 查询客户、客户网关、状态、余额门槛 -> Redis 查询客户网关策略(主叫条件 AND 被叫条件,按优先级) -> 获得落地线路组 -> 按组内优先级、启用状态、并发/CPS 选择落地网关 -> 执行主被叫前缀转换、时段和编码限制 -> RTPEngine offer/answer -> 转发到供应商 -> BYE/失败时生成标准化 CDR 事件 ``` ### 8.2 呼叫前原子检查 Redis Lua 脚本一次完成: - 客户是否启用。 - 客户网关是否启用。 - IP/账号是否匹配。 - 余额加授信是否大于最小通话门槛。 - 网关 CPS 和并发是否超限。 - 黑名单是否命中。 - 返回配置版本、线路组和路由策略。 金额禁止使用 Redis 浮点数,统一使用整数“微元”或其他固定精度最小单位。 ### 8.3 RTPEngine - 使用内核转发模块并在启动时校验。 - 控制接口只在本机或私网监听。 - RTP 端口段与安全组保持一致。 - 录音先写 `.part`,完成后原子重命名为 `.ready`。 - 文件名包含日期、Call-ID 哈希和方向,禁止直接拼接未清洗 SIP Header。 ### 8.4 CDR 标准事件 ```json { "event_id": "uuid", "idempotency_key": "call_id + ended_at 或 event_id", "call_id": "sip-call-id", "node_id": "a1", "opensips_instance": "opensips-a1", "ingress_a_ip": "100.90.90.90", "rtpengine_node": "a1", "customer_id": "C1001", "customer_gateway_id": "CGW-001", "source_ip": "10.10.1.11", "caller": "02160010001", "callee": "13800138000", "vendor_id": "V1001", "vendor_gateway_id": "VGW-001", "line_group_id": "LG-001", "started_at": "2026-06-20T02:14:22.123Z", "answered_at": "2026-06-20T02:14:27.010Z", "ended_at": "2026-06-20T02:17:30.445Z", "duration_sec": 183, "sip_code": 200, "hangup_reason": "NORMAL_CLEARING", "recording_key": "2026/06/20/xxx.ready", "config_version": 1042 } ``` `event_id` 和 `call_id + ended_at` 作为幂等依据。任何重试不得重复扣费或重复写充值/余额流水。 ## 9. Redis 设计 ### 9.1 Key 规范 ```text cfg:active_version cfg:customer:{customer_id} cfg:customer_gateway:{gateway_id} cfg:customer_gateway:{gateway_id}:policies cfg:vendor_gateway:{gateway_id} cfg:line_group:{group_id}:items auth:ip:{ip} auth:sip:{username} balance:customer:{customer_id} blacklist:caller blacklist:callee stream:cdr_payload stream:cdr_deadletter stream:recording_jobs lock:cdr:{event_id} ``` ### 9.2 配置发布 1. API 在 MySQL 事务中写业务表和 `outbox_events`。 2. Config Publisher 读取 outbox,生成完整配置快照。 3. 使用临时版本 Key 写入 Redis。 4. 校验数量和哈希后原子切换 `cfg:active_version`。 5. 发布成功后更新 outbox 状态。 6. 保留上一个版本,故障时一键回滚。 禁止 API 先改 Redis、后改 MySQL。MySQL 是唯一业务事实源,Redis 是呼叫热路径副本。 ### 9.3 Redis 持久化 - 启用 AOF `appendfsync everysec`。 - 配置 RDB 快照。 - 限制 `maxmemory`,配置前评估淘汰策略;核心配置和队列不得被随机淘汰。 - 监控 Stream 长度、Pending 数、消费延迟、内存和连接数。 ## 10. MySQL 数据模型 ### 10.1 业务核心表 ```text customers customer_gateways customer_gateway_policies customer_recharges vendors vendor_recharges vendor_gateways vendor_gateway_forbidden_periods vendor_gateway_codecs vendor_gateway_prefix_rules landing_line_groups landing_line_group_items raw_cdrs rated_cdrs recordings quality_sampling_rules quality_reviews users roles permissions user_roles role_permissions audit_logs outbox_events idempotency_keys ``` ### 10.2 关键约束 - ID 使用业务前缀 + 雪花 ID/UUID,禁止依赖前端生成。 - 金额使用 `DECIMAL(20,6)`,应用层不得使用 JavaScript 浮点直接计算金额。 - 充值使用不可变流水,余额变化必须与流水在同一事务提交。 - 网关策略唯一索引至少包含 `gateway_id + priority`。 - 线路组成员唯一索引包含 `line_group_id + vendor_gateway_id`。 - CDR 使用 `event_id` 唯一索引。 - 所有管理表包含 `created_at`、`updated_at`、`created_by`、`updated_by` 和乐观锁版本号。 - 时间存 UTC;API 返回 ISO 8601。 - 删除优先软删除;充值、CDR、审计日志不允许物理删除。 ### 10.3 余额事务 充值事务: ```text 锁定客户/供应商账户行 -> 读取充值前余额 -> 插入充值流水 -> 更新余额 -> 插入审计日志/Outbox -> 提交 ``` CDR 扣费事务: ```text 检查 event_id 幂等 -> 写 raw_cdr -> 计算最小计费结果 -> 写 rated_cdr -> 锁定客户余额 -> 扣费并写余额流水 -> ACK Redis Stream ``` ## 11. 后端工程设计 ### 11.1 技术选型 - TypeScript + Node.js LTS。 - NestJS + Fastify Adapter。 - Prisma 管理 MySQL Schema 和迁移。 - ioredis 访问 Redis 和 Streams。 - OpenAPI/Swagger 生成 API 契约。 - Pino 输出结构化 JSON 日志。 - Vitest/Jest 做单元测试,Supertest 做 API 集成测试。 - systemd 管理 API 和 Worker;HOMER 可使用 Docker Compose。 ### 11.2 推荐目录 ```text lisglosips/ ├── apps/ │ ├── web/ │ ├── api/ │ ├── worker-cdr/ │ ├── worker-recording/ │ └── worker-config-publisher/ ├── packages/ │ ├── contracts/ │ ├── domain/ │ ├── database/ │ ├── redis/ │ ├── auth/ │ └── observability/ ├── prisma/ │ ├── schema.prisma │ └── migrations/ ├── deploy/ │ ├── server-a/ │ ├── server-b/ │ ├── nginx/ │ ├── systemd/ │ └── homer/ ├── opensips/ │ ├── opensips.cfg │ ├── routes/ │ └── scripts/ ├── docs/ │ ├── openapi.yaml │ ├── redis-keys.md │ └── runbooks/ └── tests/ ``` ### 11.3 后端模块 ```text AuthModule DashboardModule CustomersModule CustomerGatewaysModule CustomerGatewayPoliciesModule RechargesModule VendorsModule VendorGatewaysModule LandingLineGroupsModule CdrModule RecordingsModule QualityModule UsersModule RolesModule AuditModule ConfigPublisherModule HealthModule ``` ### 11.4 API 清单 ```text POST /api/v2/auth/login POST /api/v2/auth/refresh POST /api/v2/auth/logout GET /api/v2/dashboard/summary GET /api/v2/dashboard/trends GET /api/v2/customers POST /api/v2/customers PATCH /api/v2/customers/:id POST /api/v2/customers/:id/recharges GET /api/v2/customer-gateways POST /api/v2/customer-gateways PATCH /api/v2/customer-gateways/:id POST /api/v2/customer-gateways/:id/enable POST /api/v2/customer-gateways/:id/disable GET /api/v2/customer-gateways/:id/policies POST /api/v2/customer-gateways/:id/policies PATCH /api/v2/customer-gateway-policies/:id DELETE /api/v2/customer-gateway-policies/:id POST /api/v2/customer-gateways/:id/policies/reorder GET /api/v2/vendors POST /api/v2/vendors PATCH /api/v2/vendors/:id POST /api/v2/vendors/:id/recharges GET /api/v2/recharges GET /api/v2/vendor-gateways POST /api/v2/vendor-gateways PATCH /api/v2/vendor-gateways/:id POST /api/v2/vendor-gateways/:id/enable POST /api/v2/vendor-gateways/:id/disable GET /api/v2/landing-line-groups POST /api/v2/landing-line-groups PATCH /api/v2/landing-line-groups/:id POST /api/v2/landing-line-groups/:id/items DELETE /api/v2/landing-line-groups/:id/items/:gatewayId POST /api/v2/landing-line-groups/:id/items/reorder GET /api/v2/cdrs GET /api/v2/cdrs/:id GET /api/v2/cdrs/:id/recording GET /api/v2/cdrs/:id/signaling-link GET /api/v2/quality/rules POST /api/v2/quality/rules PATCH /api/v2/quality/rules/:id DELETE /api/v2/quality/rules/:id POST /api/v2/quality/rules/:id/enable POST /api/v2/quality/rules/:id/disable GET /api/v2/recordings GET /api/v2/recordings/:id PUT /api/v2/recordings/:id/review GET /api/v2/users POST /api/v2/users PATCH /api/v2/users/:id POST /api/v2/users/:id/reset-password GET /api/v2/roles POST /api/v2/roles PATCH /api/v2/roles/:id PUT /api/v2/roles/:id/permissions GET /api/v2/audit-logs GET /api/v2/audit-logs/:id ``` ### 11.5 API 规范 - 列表统一 `page`、`page_size`、`sort`、筛选参数。 - 返回 `{ data, meta, request_id }`。 - 错误返回稳定业务码,不把数据库错误直接返回前端。 - 新增、充值、重置密码、策略重排支持 `Idempotency-Key`。 - 写请求校验 `If-Match` 或版本字段,防止覆盖其他管理员修改。 - 导出采用异步任务和短期下载地址,避免长请求占用 5 Mbps 公网带宽。 ## 12. 录音迁移和质检 ### 12.1 文件生命周期 ```text RTPEngine 写入 .part -> 录音完成原子改名 .ready -> Recording Worker 私网扫描 -> 拉取到 Server B 临时目录 -> 校验大小、SHA-256、音频时长 -> 原子移动到 /data/recordings/YYYY/MM/DD -> 写 recordings 表 -> 删除 Server A 源文件 ``` 任何一步失败都不得先删除 Server A 文件。Worker 使用文件锁或 Redis 锁防止重复搬运,并对 `.part`、过期 `.ready`、孤儿文件分别处理。 ### 12.2 播放 - API 先校验 `recording.play` 权限。 - Nginx `X-Accel-Redirect` 发送文件,Node.js 不直接读取整段音频到内存。 - 支持 HTTP Range,便于拖动播放。 - 操作日志记录播放人、录音 ID、时间和 IP。 - Server B 公网只有 5 Mbps 时需限制并发播放;后续可迁移至阿里云 OSS,使用短期签名 URL。 ### 12.3 抽检规则 V2 字段与 Web Demo 保持一致: - 规则名称。 - 客户。 - 抽检比例。 - 指定线路。 - 生效时间。 - 状态。 Worker 在录音入库后根据客户、线路和稳定哈希进行抽样,避免重启导致随机结果变化。 ## 13. 权限与审计 ### 13.1 权限模型 权限粒度采用 `resource.action`: ```text customers.view customers.manage vendors.view vendors.manage gateways.view gateways.manage routes.view routes.manage recharge.manage cdr.view cdr.export recording.play quality.manage users.manage roles.manage logs.view ``` 超级管理员、运营管理员、财务、质检和技术运维为内置角色。内置角色名称和关键权限禁止普通管理员修改。 ### 13.2 审计要求 审计日志至少记录: - `request_id`、操作人、用户账号、角色。 - 时间、IP、User-Agent。 - 模块、动作、对象类型、对象 ID。 - 修改前/后摘要,敏感字段脱敏。 - 成功/失败和业务错误码。 审计日志由服务端生成,前端传入的“操作人”不可信。 ## 14. Dashboard 数据策略 - 实时在线通话和注册:Redis 热数据或 OpenSIPS 指标。 - 今日通话、费用、成本、毛利:MySQL 聚合表。 - 24 小时趋势:按 5 分钟或 1 小时预聚合,禁止首页扫描全量 CDR。 - 失败响应码:CDR 聚合。 - 异常网关:健康状态缓存。 - 质检待处理:recordings/quality_reviews 聚合。 创建定时聚合任务 `worker-metrics` 或复用 CDR Worker 增量写统计表。 ## 15. 最小计费实现 虽然“费率与计费”页面延期,V2 仍需最小计费能力支持话单费用和余额。 V2 首期仅支持落地网关上的: - 计费周期,单位秒,最大 60。 - 周期费率。 - 分钟费率展示值:`周期费率 * 60 / 计费周期`。 计算使用 Decimal: ```text 计费周期数 = ceil(通话秒数 / 计费周期) 费用 = 计费周期数 * 周期费率 ``` 客户侧费率在完整页面设计前可由受控配置表或迁移脚本维护,不对普通运营角色开放。所有费率变更必须留审计记录和生效时间。 ## 16. 监控、日志和备份 ### 16.1 基础监控 即使自研“监控告警”页面延期,也必须部署: - Server A/B CPU、内存、磁盘、网络、负载。 - OpenSIPS 进程、SIP 请求量、响应码、活动 Dialog。 - RTPEngine 会话、丢包、端口使用率。 - tmpfs 使用率和最老录音文件年龄。 - Redis 内存、连接、Stream 长度、Pending。 - MySQL 连接、慢查询、锁等待、磁盘。 - API P95/P99、5xx、Worker 延迟。 ### 16.2 日志 - 应用日志输出 JSON,包含 `request_id`、`call_id`、`event_id`。 - journald 保留短期日志;可选 Loki/Promtail 后续集中化。 - 禁止在普通日志输出 SIP 密码、Web Token 和录音内容。 ### 16.3 备份 - MySQL:每日全量备份 + binlog,定期恢复演练。 - Redis:AOF + RDB;Redis 不是业务主数据唯一副本。 - 录音:数据盘快照,后续转 OSS 生命周期管理。 - OpenSIPS、Nginx、systemd、Prometheus 配置进入 Git。 - 备份密钥与业务数据分开保存。 ## 17. 开发与部署阶段 ### 阶段 0:确认技术决策(2-3 天) - 确认 SIP 公网端口、RTP 端口段和运营商 IP 范围。 - 确认预估并发、CPS、日通话量、日录音小时数。 - 确认客户余额实时控制规则和最低余额门槛。 - 确认录音保存周期和是否接 OSS。 - 确认 MySQL 还是 MariaDB。本文默认 MySQL;若沿用现有 MariaDB,需先验证 Prisma 和 SQL 兼容性。 ### 阶段 1:服务器和网络(3-4 天) - 创建 VPC、Server A/B、EIP、数据盘和安全组。 - 安装基础工具、时间同步、防火墙。 - 验证私网 RTT、带宽、磁盘吞吐和 DNS。 - 输出 `infra-check.md` 和端口验收结果。 ### 阶段 2:Server A 通信栈(5-7 天) - 安装 OpenSIPS 3.6.x、RTPEngine 和内核模块。 - 配置 SIP 15060、RTP 端口范围和 MI 本地端口。 - 配置 Redis 连接、HEP、录音 tmpfs、指标和安全限制。 - 完成 IP 认证、SIP 注册认证和基础呼叫。 - 完成单通、双通、挂断、失败响应和录音测试。 ### 阶段 3:Server B 基础服务(4-5 天) - 安装 Nginx、Node.js、Redis、MySQL。 - 部署 HOMER、Prometheus、Grafana 和 exporters。 - 初始化数据盘、备份目录和 TLS。 - 创建应用系统用户、systemd 服务账户和最小权限目录。 ### 阶段 4:后端骨架(5 天) - 初始化 monorepo、NestJS、Prisma、OpenAPI、日志和健康检查。 - 完成认证、JWT 刷新、RBAC、审计拦截器。 - 建立数据库迁移、种子数据和 CI。 - 完成 Redis 客户端、Stream 基础库和 outbox publisher。 ### 阶段 5:运营业务 API(10-12 天) - 客户、客户网关、策略、充值。 - 供应商、落地网关、落地线路组。 - 用户、角色、权限、操作日志。 - 接入当前 React Demo,移除 Mock 数据。 ### 阶段 6:通话、CDR 和录音闭环(8-10 天) - Redis 配置发布和回滚。 - OpenSIPS 呼叫时查询和路由。 - CDR Stream、幂等消费、最小计费、余额扣减。 - Recording Worker、文件校验、Range 播放。 - HOMER Call-ID 跳转。 ### 阶段 7:Dashboard 和质检(5-7 天) - Dashboard 聚合指标。 - 抽检规则、录音列表、连续播放、质检结果。 - 权限校验和播放审计。 ### 阶段 8:测试和上线(7-10 天) - 功能、集成、故障、性能、安全测试。 - 全量上线演练和回滚演练。 - 小流量客户灰度。 - 观察 24-72 小时后扩大流量。 预计总周期 8-10 周,建议至少配置:后端 1 人、SIP/媒体 1 人、前端 1 人、运维 0.5 人、测试 0.5 人。单人实施需要相应延长周期并优先保证呼叫、CDR、余额和录音闭环。 ## 18. 后端代码完成标准 - 所有本期页面均使用真实 API,不再依赖硬编码 Mock。 - OpenAPI 可生成前端类型或 API Client。 - 数据库迁移可在空库一键执行,也能回滚最近一次变更。 - 关键服务有 `/health/live` 和 `/health/ready`。 - CDR 重复投递不会重复扣费。 - 充值重复请求不会重复加余额。 - 配置发布失败不会留下半套 Redis 配置。 - 录音搬运失败不会删除源文件。 - RBAC 在服务端生效,隐藏按钮不能替代权限校验。 - 敏感操作全部进入审计日志。 - 单元测试覆盖金额、优先级、主被叫条件、幂等和权限。 ## 19. 测试与验收 ### 19.1 功能验收 - IP 和 SIP 注册两种客户网关认证成功/失败路径。 - 主叫等于、主叫前缀、被叫等于、被叫前缀及组合条件。 - 多策略优先级和多落地网关优先级。 - 网关启停、禁呼时段、编码、号码前缀转换。 - 充值、余额前后值、授信和审计。 - CDR 字段、挂断原因、客户费用、成本费用。 - 抽检规则、录音播放、上下条、自动播放、保存质检结果。 - 用户、角色、权限和重置密码。 ### 19.2 性能验收 压测目标必须在阶段 0 根据业务量确认。最低应覆盖: - 目标 CPS 的 1.5 倍持续 30 分钟。 - 目标并发的 1.2 倍持续 2 小时。 - Redis 呼叫时查询 P99。 - CDR Worker 堆积恢复速度。 - 100% 录音下 tmpfs 增长和迁移速度。 - 多用户同时查询 CDR 和播放录音时 Server B 带宽。 ### 19.3 故障验收 - Redis 短暂不可用。 - MySQL 重启和事务回滚。 - CDR Worker 异常退出后重领 Pending。 - Recording Worker 重复执行。 - Server B 数据盘接近满。 - Server A tmpfs 达 70%/85%。 - RTPEngine 重启。 - 配置版本回滚。 ### 19.4 安全验收 - SIP 扫描、暴力注册、非法来源 IP、超 CPS。 - 越权访问其他模块 API。 - 重放充值请求。 - SQL 注入、XSS、CSRF、弱密码和 Session 失效。 - 录音 URL 过期、Range 越权和路径穿越。 ## 20. 上线和回滚 ### 20.1 上线顺序 1. 备份 MySQL、Redis、OpenSIPS 和 Nginx 配置。 2. 部署 Server B 数据库迁移和后端,但暂不切流。 3. 发布 Redis 配置快照并校验。 4. 部署 Server A OpenSIPS/RTPEngine 配置。 5. 使用测试账号完成真实呼叫、CDR、扣费和录音。 6. 灰度一个低风险客户或一条线路。 7. 观察指标、CDR 差异、余额和录音完整性。 8. 逐步扩大流量。 ### 20.2 回滚 - Web/API:切回上一个发布目录或镜像。 - 数据库:使用向前兼容迁移;上线窗口不执行不可逆删列。 - Redis 配置:切回上一个 `cfg:active_version`。 - OpenSIPS:恢复上一版配置并执行语法检查后重载。 - RTPEngine:恢复上一版 systemd 参数。 - CDR:停止 Consumer,不删除 Stream;修复后重放。 - 录音:停止删除 Server A 源文件,保留 B 端临时文件等待人工处理。 ## 21. 主要风险 | 风险 | 影响 | 控制措施 | | --- | --- | --- | | Server A 单点 | 重启影响通话 | V2 接受;配置快速恢复;后续增加双节点 | | 仅开放 15060 | 无 RTP 或单通 | 必须开放并验证 RTP UDP 端口段 | | 3 GiB tmpfs 打满 | 录音丢失、进程异常 | 高频搬运、阈值告警、降级策略 | | Server B 5 Mbps | 多录音播放卡顿 | Range、限并发、后续 OSS/CDN | | Redis 成为呼叫热路径单点 | 新呼叫失败 | AOF/RDB、监控、后续主从/Sentinel | | CDR 重复消费 | 重复扣费 | Stream ACK、唯一索引、幂等事务 | | 配置半发布 | 路由不一致 | MySQL Outbox + Redis 版本原子切换 | | HOMER 数据膨胀 | 数据盘占满 | 独立存储、保留周期、容量告警 | | 金额浮点误差 | 对账差异 | Decimal/整数最小单位,禁止 JS Number 计费 | ## 22. 下一步执行清单 ### 立即执行 - [ ] 确认预计 CPS、并发、日话单、录音小时数。 - [ ] 确认 Server A RTP 端口段和安全组来源范围。 - [ ] 确认 Server B 数据盘容量、IOPS 和录音保留期。 - [ ] 确认 MySQL/MariaDB 最终选择。 - [ ] 申请域名和 TLS 证书。 - [ ] 创建预生产 VPC 和两台服务器。 ### 服务器部署完成条件 - [ ] Server A OpenSIPS、RTPEngine、Redis 私网连接、HEP、tmpfs、exporter 正常。 - [ ] Server B Nginx、Node.js、Redis、MySQL、HOMER、Prometheus、Grafana 正常。 - [ ] 安全组端口和主机防火墙验收。 - [ ] 备份、日志轮转、时间同步和系统用户验收。 - [ ] 完成端到端测试呼叫并在 HOMER 查询到信令。 - [ ] 完成录音从 A 内存盘到 B 数据盘的搬运测试。 ### 后端开发启动条件 - [ ] 创建 V2 monorepo 和 CI。 - [ ] 提交 Prisma Schema V1 和初始化迁移。 - [ ] 提交 OpenAPI V2 基线。 - [ ] 提交 Redis Key 和 Stream 契约。 - [ ] 完成 Auth/RBAC/Audit 骨架。 - [ ] 先实现客户、网关、线路组和配置发布,再实现 CDR/录音。 ## 23. V2 交付物 ```text 1. 阿里云双机部署记录和安全组清单 2. Server A/Server B 幂等安装脚本 3. OpenSIPS 和 RTPEngine 配置仓库 4. MySQL DDL/Prisma migrations 5. Redis Key/Stream 契约 6. OpenAPI 文档 7. LisgloSIPS API 和 Workers 源码 8. React Web 与真实 API 集成版本 9. Prometheus/Grafana/HOMER 部署配置 10. 自动化测试、压测报告和安全检查报告 11. 上线、回滚、备份恢复和故障处置 Runbook ``` ## 24. 最终实施原则 - 先打通“配置发布 -> 呼叫 -> CDR -> 计费 -> 余额 -> 录音 -> 质检”闭环,再扩展页面。 - MySQL 是业务事实源,Redis 是呼叫热路径和队列,不反向替代主库。 - 金额、CDR、充值和录音全部按可重试、可审计、幂等设计。 - Server A 保持精简,Server B 承担业务复杂度。 - 待设计菜单不阻塞底层必需能力,但不得在 V2 临时扩张为未确认的产品页面。 - 所有部署和配置必须脚本化、版本化、可回滚。 ## 25. Codex 全流程分步实施规范 ### 25.1 目的 本项目计划主要由 Codex 持续实施。为避免单次任务过大、上下文丢失、误操作和新会话重复工作,整个 V2 被拆分为 31 个原子任务 `S00-S30`。每个会话默认只完成一个任务,除非用户明确授权合并。 详细实时状态记录在 `IMPLEMENTATION_STATUS.md`,服务器密码只保存在被 `.gitignore` 排除的 `.codex-private/SERVER_ACCESS.md`。 ### 25.2 服务器固定代号 | 代号 | IP | SSH 用户 | 职责 | | --- | --- | --- | --- | | Server A | `100.90.90.90` | `hector` | 核心通信网关 | | Server B | `100.90.90.91` | `hector` | 业务、缓存与监控中心 | | Server T | `100.93.185.30` | `hector` | 已安装 OpenSIPS,模拟 SIP 客户 | 主文档不保存明文密码。新会话只有在用户授权当前任务连接服务器后,才读取私密访问文件,并禁止在回复或日志中回显密码。 ### 25.3 单会话执行纪律 1. 先读取 `SOFTSWITCH_PLATFORM_DESIGN_V2.md` 和 `IMPLEMENTATION_STATUS.md`。 2. 只执行状态文件中的“当前任务”。 3. 服务器任务先做只读检查,再做写操作。 4. 修改配置前备份,记录绝对路径、时间戳和回滚命令。 5. 服务重启、防火墙、SSH、数据库迁移、数据删除前说明影响。 6. 不能因安装困难随意更换技术栈或扩大公网端口。 7. 安装软件必须记录来源、版本和校验结果。 8. 完成后执行该任务的验收,不以“命令无报错”代替功能验证。 9. 更新状态文件后再结束会话。 10. 出现阻塞时停止在安全状态,不绕过安全控制继续后续任务。 ### 25.4 原子任务顺序 #### S00-S02:安全接入和资产盘点 **S00 SSH 安全接入与凭据迁移** - 用户明确授权后连接 A/B/T。 - 在本机生成项目专用 Ed25519 Key,不覆盖现有 Key。 - 安装公钥并验证三个独立新会话。 - 备份 `sshd_config`,确认不会锁死后只调整 SSH 管理端口,不禁止 SSH 密钥登录。 - 轮换当前密码,私密文件同步更新或改为只记录 Key。 - 验收:新端口 Key 登录成功;sudo 可用;现有会话保留到新会话验证完成。 **S01 三机只读资产盘点** - 只读取 OS、内核、CPU、内存、磁盘、网卡、路由、时间、端口、服务、包版本。 - 盘点 T 上现有 OpenSIPS 配置和测试能力,不修改。 - 输出 `docs/inventory-A.md`、`inventory-B.md`、`inventory-T.md`。 - 验收:报告包含差异、风险和安装前备份清单。 **S02 网络与安全组验收** - 验证 A/B/T 双向可达性和实际私网 RTT。 - 确认 A 的 SIP 15060/UDP、RTP 端口段;B 的 443/TCP、私网 HEP 9060/UDP。 - 不通过 SSH 直接修改云安全组;生成阿里云控制台操作清单供用户确认。 - 验收:形成端口矩阵和最小暴露方案。 #### S03-S06:Server B 基础设施 **S03 Server B 基础系统初始化** - 更新系统、安装基础包、chrony、nftables、fail2ban。 - 检查并初始化数据盘目录,不格式化未知数据盘。 - 创建应用、录音和监控系统用户与目录权限。 - 验收:重启后时间、挂载、权限和防火墙正常。 **S04 MySQL 与 Redis** - 固定版本安装并限制监听地址。 - 创建业务库/用户,启用 MySQL binlog、Redis AOF/RDB。 - 配置备份脚本和健康检查。 - 验收:重启恢复、权限隔离、备份和恢复样例通过。 **S05 Node.js、Nginx 与 TLS 基线** - 安装固定 Node.js LTS 和 pnpm。 - 建立 `/opt/lisglosips/releases`、`current` 和 systemd 模板。 - 配置 Nginx 静态站点、API 代理、Range、限流和 HTTPS 预案。 - 验收:占位健康页通过 Nginx 访问,应用端口不暴露公网。 **S06 HOMER 与基础监控** - 部署 Heplify-server/HOMER、Prometheus、Grafana、exporters。 - HOMER 数据和业务 MySQL 分离。 - 验收:B 私网 9060 接收测试 HEP;A/B 主机指标可见。 #### S07-S10:后端基础 **S07 后端 Monorepo 骨架** - 创建 NestJS/Fastify、Prisma、结构化日志、配置校验、健康检查和测试框架。 - 建立 CI 命令:lint、typecheck、test、build。 - 验收:本地和 B 均可构建;健康检查可用。 **S08 数据库 Schema 与迁移** - 实现本文第 10 章表结构、索引、Decimal、审计列和 Outbox。 - 提供空库迁移、种子和回滚/恢复说明。 - 验收:空库一键初始化;重复执行安全;Schema 测试通过。 **S09 登录认证基础** - 登录、刷新、退出、Argon2id、失败锁定、Token/Cookie 安全。 - 验收:成功、失败、过期、刷新、注销和暴力尝试路径通过。 **S10 用户、角色、权限与审计** - 实现用户、角色、权限矩阵、内置角色保护和重置密码。 - 服务端 RBAC 和审计拦截器必须先于业务 API 完成。 - 验收:越权被拒绝;敏感字段脱敏;审计详情可查询。 #### S11-S17:运营业务 API **S11 客户管理**:客户 CRUD、启停、余额/授信读取。 **S12 充值流水**:客户/供应商充值、事务、幂等、并发。 **S13 客户网关**:IP/SIP 注册认证、启停、密码安全。 **S14 网关策略与配置发布**:主被叫条件、优先级、Outbox、Redis 版本。 **S15 供应商管理**:供应商 CRUD、余额、授信。 **S16 落地网关**:认证、CPS、并发、禁呼、编码、号码转换、周期费率。 **S17 落地线路组**:成员增删、优先级、并发汇总、引用校验。 每个任务必须同时完成:数据库迁移、领域校验、API、权限、审计、单元测试、集成测试和 OpenAPI,不允许先堆积未测试接口后统一补测。 #### S18-S21:通信栈和测试客户 **S18 Server A OpenSIPS 基线** - 先备份现状,再安装/固定 OpenSIPS 3.6.x。 - 配置 15060、认证、防扫描、MI 本地绑定和模块基线。 - 每次改动先执行语法检查,验证失败立即回滚。 **S19 RTPEngine 与录音 tmpfs** - 配置内核转发、RTP 端口段、3 GiB tmpfs 和 `.part -> .ready` 生命周期。 - 验收:双向音频、NAT、录音、重启和 tmpfs 阈值。 **S20 Redis 热路径、HEP 与指标** - 实现 Lua 原子检查、版本化配置读取、CDR XADD、HEP 发送和指标采集。 - 验收:Redis 超时、配置缺失、HEP 中断均有明确降级和日志。 **S21 Server T 客户模拟** - 在不破坏 T 现有 OpenSIPS 的前提下配置测试域、IP 认证和 SIP 注册账号。 - 编写成功呼叫、忙、拒绝、超时、错误密码、超 CPS 测试脚本。 - 验收:所有脚本可重复执行并输出 Call-ID。 #### S22-S26:异步闭环 **S22 CDR Redis Stream**:事件契约、Consumer Group、ACK、Pending、死信、幂等。 **S23 最小计费**:Decimal 周期计费、客户费用、成本、余额扣减。 **S24 录音搬运与播放**:私网拉取、哈希、原子移动、Range、权限和源文件删除保护。 **S25 质检后端**:规则、稳定抽样、连续录音查询、质检保存和审计。 **S26 Dashboard 聚合**:实时指标和预聚合趋势,避免扫描全量 CDR。 #### S27-S30:联调和上线 **S27 React 接入真实 API** - 按菜单逐页替换 Mock;每次只接入一个领域。 - 增加加载、空状态、错误、权限隐藏和并发更新提示。 - 待设计菜单继续保留标识,不接临时 API。 **S28 三机端到端联调** - T 发起 SIP -> A 鉴权/路由/媒体/录音 -> B CDR/费用/信令/质检。 - 按 Call-ID 核对 OpenSIPS、Redis Stream、MySQL、HOMER 和录音文件。 **S29 性能、故障与安全测试** - 执行 CPS、并发、录音、Redis/MySQL/Worker 故障和 Web/SIP 安全测试。 - 所有失败必须形成缺陷、修复和复测记录。 **S30 备份、Runbook、灰度与上线** - 完成恢复演练、上线/回滚脚本和灰度。 - 上线前冻结版本和数据库迁移,输出最终验收报告。 ### 25.5 Codex 新会话启动模板 ```text 请先读取 SOFTSWITCH_PLATFORM_DESIGN_V2.md 和 IMPLEMENTATION_STATUS.md, 只执行当前任务 Sxx,不提前执行下一任务。 如任务需要服务器访问,读取 .codex-private/SERVER_ACCESS.md,但不要在回复、日志或文档中回显密码。 所有服务器写操作先备份并给出回滚点;涉及重启、SSH、防火墙、数据库迁移或删除时先说明影响。 完成后执行验收并更新 IMPLEMENTATION_STATUS.md,包括状态、产物、验证、回滚、遗留问题和下一任务。 ```