1164 lines
42 KiB
Markdown
1164 lines
42 KiB
Markdown
# 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 媒体,最终按容量调整 |
|
||
| `<SSH_ADMIN_PORT>/TCP` | 堡垒机或管理 VPN | SSH 管理端口,不对全网开放;保留密钥登录 |
|
||
|
||
### 6.2 Server B 入站
|
||
|
||
| 端口 | 来源 | 说明 |
|
||
| --- | --- | --- |
|
||
| `443/TCP` | 管理端用户 | LisgloSIPS HTTPS |
|
||
| `80/TCP` | 可选 | 跳转 HTTPS,不承载登录 |
|
||
| `9060/UDP` | Server A 私网 IP | HEP |
|
||
| `<SSH_ADMIN_PORT>/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,包括状态、产物、验证、回滚、遗留问题和下一任务。
|
||
```
|