feat: improve application access and money precision

This commit is contained in:
hectorzhao
2026-07-16 17:54:05 +08:00
parent 9d5c507007
commit faa716b8d0
49 changed files with 1699 additions and 489 deletions
+31 -3
View File
@@ -125,7 +125,7 @@
11. 应用 `AppID` 是平台内部应用标识,用于页面展示、复制参数和工单定位,不作为 CMPP bind/login 认证参数。
12. 短信应用必须可配置客户侧 CMPP 最大连接数 `cmppMaxConnections`;客户侧提交窗口 `cmppWindowSize` 后端保留默认值,当前第一版不在运营端展示或要求运营配置,待 Gateway 入站侧按应用窗口真正限流后再开放为高级配置。
13. 短信应用必须恢复设计基线中的“短信接口”开关,字段为 `interfaceEnabled`,默认开通;关闭后客户端/API 发送链路、客户侧 CMPP Gateway bind/login 和 submit 都必须被真实后端拒绝,不允许只在前端隐藏入口。
14. 短信应用必须恢复设计基线中的“接口类型”配置,当前第一版仅允许 `CMPP2.0`,字段 `interfaceType=cmpp20`HTTP 接口在页面中展示为暂不可选,后端必须拒绝 `http`未实现类型
14. CMPP 协议类型当前第一版仅允许 `CMPP2.0`,字段保持 `interfaceType=cmpp20`HTTP 不写入该字段,而是通过独立的 `SmsApplicationHttpConfig` 总开关和子能力配置开通。前后端必须拒绝`interfaceType` 直接改成 `http`无效协议值
### 4.3 签名与引流信息
@@ -359,7 +359,7 @@
6. 最终失败、超时失败需要退费。
7. 三网通道成本只用于平台内部成本核算,不影响客户扣费金额。
8. 当前版本计费口径固定为提交 accepted 扣费、最终 failed receipt/timeout 退款。
9. 所有面向用户展示的金额、余额、充值金额和单价统一以人民币元展示并固定保留位小数;内部使用分或最小计费单位持久化,不以展示精度改变账务计算。
9. 所有面向用户展示的金额、余额、充值金额和单价统一以人民币元展示并固定保留位小数;内部使用 `0.0001 元`整数金额单位持久化,不以浮点数执行账务计算。
10. API 必须定时扫描提交成功但超过 72 小时仍未收到明确最终回执的短信,转为 timeout 并退还已扣金额;扫描需覆盖 `submitted``unknown`,且用条件更新避免多实例重复退款。
## 5. 功能需求
@@ -546,7 +546,7 @@
- 利润报表按发送日期汇总日发送条数、成功条数、消费金额、成本金额、利润和利润率,支持在“企业应用”和“通道”两个统计维度间切换。
- 企业应用维度的消费金额只统计仍为 `charged` 的客户账单,最终失败并退款的短信不再形成收入;成本金额统计该应用短信所有上游 `accepted` 提交的通道成本,包括补发产生的真实额外成本。
- 通道维度按实际上游 `accepted` 提交统计发送量和成本,按同一 Gateway 消息回执统计成功量;客户收入只归属最终有效提交,避免补发时重复计算收入。通道成本单价和成本金额必须在提交记录创建时快照,后续修改通道单价不得改写历史成本。
- 利润等于消费金额减成本金额;利润率等于利润除以消费金额,消费金额为 0 时利润率按 0 展示。所有金额继续使用整数分持久化并按位小数展示。
- 利润等于消费金额减成本金额;利润率等于利润除以消费金额,消费金额为 0 时利润率按 0 展示。所有金额使用 `0.0001 元`整数金额单位持久化并按位小数展示。
- 报表按北京时间 T+1 生成,不生成当天未完整数据;每日刷新时必须在同一事务内重新生成 T-4 至 T-1 四个完整自然日,使 72 小时内到达或变化的回执能够修正发送成功和利润结果。
- API 启动后自动补生成最近四个完整自然日,并按日执行滚动刷新;报表查询支持服务端日期、企业、应用、通道和维度过滤及分页。
- “报表对账”增加“发送质量报表”,包含企业应用、通道、签名、引流信息四个 Tab。每个 Tab 按发送日期和对应维度展示发送条数、成功条数、成功率、平均到达时长,并默认按发送条数从大到小排序。
@@ -1495,6 +1495,7 @@
### 管理端企业应用配置
- CMPP 与 HTTP 是两套可独立开通的接入能力,不再把 HTTP 作为 `interfaceType` 的互斥选项。运营端在企业应用“接口配置”中维护 HTTP 总开关,以及单条发送、短信状态查询、回执 Webhook、上行 Webhook、上行查询、客户端凭据自助管理等子能力。
- 企业应用新增/编辑页必须将 CMPP 与 HTTP 配置拆成两个视觉和语义独立的区域:CMPP 区只放协议、账号、扩展码、客户接入号、接口密码、连接数、CMPP 白名单及下游重试;HTTP 区只放 HTTP 子能力、HTTP 白名单、QPS、凭据限制、投递模式和 Webhook 策略。协议关闭时收起该协议参数,只保留独立开关和关闭说明,不得再把两套字段混排在同一表单网格中。
- HTTP 配置独立维护 IP/CIDR 白名单、应用级 QPS、签名时间容差、最多有效凭据数、上行保留/查询范围/分页上限、Webhook 超时和最多尝试次数、生产 HTTPS 约束、客户手工重投权限。
- 回执和上行分别配置 `cmpp/http/both/none` 投递模式。Gateway 产生的回执或上行必须先写入现有真实短信记录,再按模式投递;HTTP 回调不得取代或伪造 Gateway、回执匹配和上行认领链路。
- HTTP 访问密钥和 Webhook 签名密钥使用 `HTTP_API_MASTER_KEY` 派生的 AES-256-GCM 密钥加密保存。Secret 只在创建或轮换当次返回,后续运营端和客户端仅显示末四位;允许同时保留多个有效凭据以完成无停机轮换。
@@ -1515,3 +1516,30 @@
- Webhook 禁止重定向,并在保存和每次投递前解析域名,拒绝环回、私网、链路本地、共享地址和元数据地址。2xx 成功;网络错误、408、429、5xx 可按立即、1 分钟、5 分钟、15 分钟、1 小时、6 小时、24 小时重试;其他 4xx 直接终结。
- PostgreSQL 分别保存 Webhook 事件、投递状态和每次尝试摘要;客户和运营人员可查询,授权后可手工重投。首次投递与重试均由 BullMQ 执行,不得使用浏览器定时器或 localStorage 冒充。
- 客户端“短信基础配置”新增“接口对接”,包含接口概览、访问凭据、回调配置、接口文档、调用与回调记录五个页签;企业应用卡片显示 HTTP 开通状态并跳转。客户端上行列表改为真实服务端条件查询,不再先拉全量数据后仅在浏览器过滤。
## 2026-07-16 运营端与客户端移动端适配要求
1. 运营端和客户端在宽度不大于 780px 的小屏设备上统一使用顶部栏加左侧抽屉导航。抽屉默认关闭,由顶部菜单按钮打开,支持遮罩、关闭按钮、Esc 和选择菜单后关闭;菜单内容在抽屉内部独立滚动,业务内容不得被完整侧栏挤到页面下方。
2. 320px、360px、375px、390px 和 768px 常见视口不得出现页面级横向滚动。登录面板、筛选条件、表单、统计卡、操作区和弹窗必须限制在可用宽度内,桌面端既有可折叠侧栏行为保持不变。
3. 通用数据表格在小屏下改为带字段名称的纵向记录卡片,操作按钮允许换行;不得要求用户横向滚动才能看到状态、失败原因或操作。业务专用的签名、引流、通道报备列表也必须按同一原则重排。
4. 多列查询条件和报表筛选在小屏下收敛为单列;相关查询、重置和导出按钮保持可见并可换行。通道组配置、手机号段库、HTTP 接口凭据、企业签名报备目标等固定宽度区域必须取消页面级最小宽度。
5. 移动端顶部栏至少保留导航入口、平台标识、通知和用户菜单;交互控件应具备可读的无障碍名称,抽屉打开状态使用 `aria-expanded` 表达,并尊重系统“减少动态效果”设置。
6. 客户端彩信签名、彩信模板、彩信发送、彩信任务、彩信详情和上行彩信均未完成真实后端闭环,在功能完成前不得展示“彩信服务”菜单或其子菜单;保留内部路由不代表可向客户开放。
7. 运营端手机号段库使用平台通用 Breadcrumb、Button、Input、Tabs、Table、Tag、Pagination 和 Modal 实现。Tab 位于标题下方和筛选条件上方;当前 Tab 仅显示自身的真实总数。统计使用紧凑信息带,手机号段突出显示、运营商使用语义标签、删除使用克制的危险操作样式,不得另造一套组件或用大面积统计卡挤压表格。
## 2026-07-16 全平台金额精度要求
1. 企业应用客户单价、通道成本单价、账户余额、授信额度、充值、消费、返还、短信计费金额以及对账和利润报表中的全部金额,统一精确到人民币小数点后 4 位;输入最多允许 4 位小数,页面及导出文件统一展示 4 位小数。
2. 数据库和计费链路继续使用整数运算,最小金额单位统一为 `0.0001 元`,即 `1 元 = 10000 金额单位`。历史字段名中的 `Cents` 为兼容既有 API 暂不改名,但其数值语义同步调整为金额单位,不再表示人民币“分”。
3. PostgreSQL 金额列统一升级为 `BIGINT`。上线迁移时既有按分保存的数据乘以 100,应用换算除数由 100 改为 10000,确保迁移前后实际人民币金额完全一致。
4. 企业应用单价修改必须写入真实 `SmsApplication.customerUnitPrice`,例如 `0.0325 元/条` 保存为 `325`;后续预估、冻结、扣费、返还和利润统计均使用该整数值,不得在前端或后端再次四舍五入到分。
5. API 返回 `BIGINT` 金额时仅在 JavaScript 安全整数范围内转换为 JSON number;超过安全整数范围必须显式报错,避免静默丢失金额精度。
## 2026-07-16 企业应用接口参数复制与下游接入约束
1. 运营端企业应用列表同时提供 CMPP 参数和 HTTP 参数复制;客户端应用列表提供 CMPP 参数复制,客户端“接口对接”页提供 HTTP 参数复制。复制内容必须来自真实应用和 HTTP 配置 API,不得用静态数组、localStorage 或页面默认值冒充。
2. 客户端仅在应用已开通对应协议时允许复制参数。未开通 CMPP 时按钮不可操作,且客户端直接请求 CMPP 参数 API 必须返回 403;未开通 HTTP 时同样不得复制 HTTP 参数。
3. 客户侧 CMPP 网关地址和端口是平台对外公布的下游接入地址,分别由 `CMPP_PUBLIC_HOST``CMPP_PUBLIC_PORT` 配置,不得读取任一上游短信通道的网关地址。生产默认值为 `8.160.169.106:17890`
4. 参数复制必须兼容平台当前 HTTP 页面:优先使用 Clipboard API;浏览器因非安全上下文或权限拒绝时,使用受控 textarea 复制降级,并向用户明确反馈成功或失败,不得无提示失败。
5. `cmppMaxConnections` 必须在 Gateway 登录时按应用和活动 TCP 会话真实计数并限制,同时由 API 的连接事件校验兜底。连接关闭或异常断开后必须及时释放连接名额并回写断开事件。
6. CMPP IP/CIDR 白名单必须在登录和连接事件中校验;运营端修改白名单、关闭接口、停用应用或降低最大连接数后,Gateway 应在下一次心跳校验时关闭不再符合条件的存量连接,不能只限制后续 Submit。
+2
View File
@@ -50,6 +50,8 @@ REPORT_DAILY_REFRESH_ENABLED=true
REPORT_REFRESH_INTERVAL_MS=3600000
CMPP_DOWNSTREAM_ACK_TIMEOUT_SECONDS=30
GATEWAY_CMPP_ADDR=0.0.0.0:17890
CMPP_PUBLIC_HOST=8.160.169.106
CMPP_PUBLIC_PORT=17890
GATEWAY_STARTUP_RECONNECT_DELAY_MS=1000
OBJECT_STORAGE_DRIVER=minio
OBJECT_STORAGE_LOCAL_ROOT=/var/lib/cmpp-platform/object-storage
+32 -1
View File
@@ -639,7 +639,7 @@
- 企业选项来自真实企业 API,不使用静态数组、mock 或 localStorage。
- 请求体包含 tenantId、queuePriority、客户单价、IP 白名单、`interfaceEnabled``interfaceType=cmpp20``cmppAccount``cmppEnterpriseCode``passwordCipher``cmppMaxConnections` 和通道组绑定;企业应用表单不展示或提交客户侧 `cmppWindowSize`
- “短信接口”开关刷新后仍来自真实数据库;关闭后该应用不能通过客户端/API 发送,也不能通过 Gateway bind/login 或 submit。
- “接口类型”当前只能选择 CMPP2.0HTTP 接口展示为暂不可选,手工提交 `interfaceType=http` 时后端返回 400。
- CMPP 协议当前只能选择 CMPP2.0HTTP 使用独立配置区、总开关和子能力,不与 CMPP `interfaceType` 互斥;手工提交 `interfaceType=http` 时后端返回 400。
- `cmppAccount` 可显式填写 6 位数字;留空时由后端自动生成唯一账号;重复或非法格式保存失败并提示可读错误。
- `cmppEnterpriseCode` 来自应用自身配置,不透传上游通道企业代码;`passwordCipher` 为 16 位,编辑留空不覆盖原密码。
- 填充开关关闭时不渲染填充前缀输入框;开启后前缀才显示并可编辑。前后端只接受数字扩展码/前缀,客户侧接入号为二者拼接且不超过 21 位;关闭填充后前缀清空。
@@ -3378,6 +3378,20 @@ npm run verify:phase8
| TC-CMPP-STATUS-016 | 单连接限速 100,连接数 1 和 2 分别压测。 | 理论能力随在线连接数变化;实际 TPS 不超过限速;不重复发送。 |
| TC-CMPP-STATUS-017 | 新建/启用通道后 Gateway 未回写,连接状态停留 connecting 超过 30 秒。 | API 兜底任务将连接标记为 failedcurrentConnections=0lastError 为 `Gateway connection request timed out after 30 seconds`;连接日志包含 connect_timeout;发送路由不可选择该通道。 |
### 17.8.1 运营端与客户端移动端适配
| 用例编号 | 操作 | 预期结果 |
| --- | --- | --- |
| TC-RESP-001 | 分别以运营管理员和企业管理员登录,在 390×844 视口打开首页。 | 左侧栏默认不占据业务内容空间;顶部显示菜单、平台标识、通知和用户入口;页面 `scrollWidth` 不大于视口宽度。 |
| TC-RESP-002 | 点击顶部菜单,滚动长菜单,随后点击任一业务菜单;重复打开后点击遮罩、关闭按钮和按 Esc。 | 抽屉覆盖在业务内容上方并带遮罩,菜单内部可滚动;四种关闭方式均有效,路由切换后抽屉自动收起。 |
| TC-RESP-003 | 在 320、360、375、390 和 768px 宽度打开两端登录页。 | 登录面板、账号、密码、验证码和登录按钮完整处于视口内,不出现页面级横向滚动。 |
| TC-RESP-004 | 在小屏打开客户端签名与引流信息、账户账单、短信记录和模板列表。 | 通用表格或业务列表以带字段标签的纵向卡片呈现,长文本可换行,状态和操作无需横向滚动即可查看。 |
| TC-RESP-005 | 在小屏打开运营端发送质量、对账、利润报表、企业查询、审核日志和手机号段库。 | 多列筛选收敛为单列或紧凑按钮组;查询、重置、导出均可操作;页面无固定宽度导致的横向溢出。 |
| TC-RESP-006 | 在小屏打开通道报备详情、通道组新增/编辑、企业签名管理和 HTTP 接口凭据。 | 报备行按卡片重排,通道组表单与报备目标单列显示,凭据和密钥可换行,操作区可换行且不超出视口。 |
| TC-RESP-007 | 在 1280px 及以上桌面视口复核运营端和客户端。 | 保持原桌面侧栏及折叠按钮;筛选和表格仍按桌面布局展示,不因移动端规则产生回归。 |
| TC-RESP-008 | 登录客户端,在桌面侧栏和移动端抽屉中检查全部菜单。 | 不展示“彩信服务”分组,也不展示彩信签名、模板、发送、任务、详情或上行彩信入口;短信和账户等已完成功能菜单正常显示。 |
| TC-RESP-009 | 在桌面和 390px 小屏打开手机号段库,切换“手机号段/运营商区分规则”,执行关键词查询和重置。 | 页面使用系统通用控件;Tab 位于标题与筛选之间;仅展示当前 Tab 的真实总数;表格/移动卡片无横向溢出,查询和重置继续调用真实 API 查询状态。 |
### 17.9 自动化落地建议
| 层级 | 建议覆盖 |
@@ -3476,9 +3490,26 @@ npm run verify:phase8
| 用例编号 | 操作 | 预期结果 |
| --- | --- | --- |
| TC-HTTP-CONFIG-001 | 运营端编辑企业应用,独立开关 CMPP 与 HTTP,并配置发送/查询/回调子能力、独立 IP 白名单、QPS、投递模式和 Webhook 策略,保存后刷新。 | 配置写入 `SmsApplicationHttpConfig` 和 HTTP 白名单表;CMPP 原配置不丢失;刷新一致;关闭某项能力后对应 OpenAPI 返回稳定 403 业务码。 |
| TC-HTTP-CONFIG-002 | 打开运营端企业应用新增/编辑页,分别开关 CMPP 与 HTTP,并在桌面及 390px 小屏检查两块配置。 | CMPP 与 HTTP 以两个独立区块展示;CMPP 区不出现 HTTP 参数,HTTP 区不出现 CMPP 账号、密码或白名单;关闭某协议后仅收起该协议参数且不影响另一协议区域;小屏无页面级横向滚动。 |
| TC-HTTP-AUTH-001 | 使用正确 Access Key/Secret 按原始请求体签名,再分别修改 path、body、timestamp、nonce、来源 IP 和签名。 | 正确请求通过;篡改项返回 `application/problem+json`;过期时间、重复 nonce、白名单外 IP 和错误签名被拒绝;错误签名不得提前占用 nonce。 |
| TC-HTTP-AUTH-002 | 同一应用一秒内并发调用超过配置 QPS,再在下一秒继续调用。 | Redis 应用级额度不被多个凭据放大;超额返回 429,下一秒恢复;不依赖单进程内存计数。 |
| TC-HTTP-CREDENTIAL-001 | 客户创建第一把凭据、保存 Secret,再创建第二把完成切换并吊销第一把;刷新页面和查看数据库。 | Secret 仅创建当次可见,数据库为 AES-256-GCM 密文;列表只显示末四位;两把凭据轮换期可并存,吊销后旧凭据立即返回 401。 |
### 17.11 全平台金额四位小数精度
| 用例编号 | 操作 | 预期结果 |
| --- | --- | --- |
| TC-MONEY-001 | 运营端编辑企业应用,将客户单价填写为 `0.0325` 并保存,刷新列表后再次进入编辑页。 | 保存调用真实 NestJS API;数据库 `SmsApplication.customerUnitPrice=325`;列表和编辑页均展示 `0.0325`,不被舍入为 `0.03`。 |
| TC-MONEY-002 | 使用单价 `0.0325` 的应用发送 2 个计费条数。 | 预估、冻结及最终计费金额均为 `650` 金额单位,即 `0.0650 元`;返还时按相同精度原额冲回。 |
| TC-MONEY-003 | 分别设置余额、授信、充值、今日消费和今日返还为含 4 位小数的金额,查看运营端企业列表、详情、首页以及客户端首页和账单。 | 所有位置展示同一真实金额且固定为 4 位小数,不使用浮点累计或仅保留到分。 |
| TC-MONEY-004 | 打开短信详单、对账单、利润报表并导出 CSV。 | 消费金额、成本金额和利润均按 4 位小数显示;CSV 表头以“元”为单位,值固定 4 位小数,汇总结果与数据库整数金额单位一致。 |
| TC-MONEY-005 | 在迁移前备份数据库并记录各金额列汇总,执行四位精度迁移后复核字段类型及汇总。 | 金额列升级为 `BIGINT`;迁移后整数汇总等于迁移前的 100 倍,按新除数换算后的人民币金额完全相等。 |
| TC-MONEY-006 | 在单价、授信和充值输入中分别填写超过 4 位小数、非法字符和超出 JavaScript 安全整数范围的值。 | 前后端拒绝无效值并返回可读错误;API 不静默舍入或输出已失真的金额。 |
| TC-IF-PARAM-001 | 运营端分别打开已开通 CMPP、HTTP 的企业应用参数弹窗并一键复制;模拟 Clipboard API 在 HTTP 页面被拒绝。 | CMPP 内容展示平台公网地址和端口而非上游通道地址;HTTP 内容包含应用、能力、QPS、白名单、投递模式和文档地址;降级复制成功且有明确提示。 |
| TC-IF-PARAM-002 | 客户端查看未开通 CMPP 的应用并直接调用该应用 CMPP 参数 API。 | 页面复制按钮禁用;真实 API 返回 403,不泄露账号、密码、接入号等参数。 |
| TC-IF-PARAM-003 | 客户端在已开通 HTTP 的应用“接口对接”页复制 HTTP 参数,再关闭 HTTP 后复测。 | 开通时复制真实 HTTP 配置;关闭时按钮不可用且不生成参数文本。 |
| TC-CMPP-DOWNSTREAM-001 | 将应用最大连接数设为 2,依次建立 3 条 CMPP TCP 连接,断开其中一条后再次连接。 | 前 2 条 bind 成功,第 3 条被拒绝;断开后名额立即释放,新连接可成功;连接记录与真实 TCP 会话一致。 |
| TC-CMPP-DOWNSTREAM-002 | 已建立连接后修改应用 IP/CIDR 白名单为不包含当前来源 IP,或将最大连接数降至当前连接数以下,等待下一次心跳。 | API 拒绝连接事件,Gateway 主动关闭不符合配置的存量连接并回写断开原因;连接数不继续显示为正常。 |
| TC-HTTP-SEND-001 | 调用单条发送接口,使用真实已审核签名/模板、余额和通道路由。 | 返回 202 和平台 messageId;真实创建 API 来源批次与短信记录,执行风控、冻结/计费并进入 Redis/BullMQ/Gateway 链路;不得使用静态数组或直接伪造 delivered。 |
| TC-HTTP-IDEMPOTENCY-001 | 并发使用相同 `Idempotency-Key` 和相同 body 调用,再用相同 key 改变 body;另重复 clientMessageId。 | 只创建一条真实短信;完成后同内容重放原响应,处理中返回 409 processing;不同 body 返回 409 conflict;应用内重复 clientMessageId 被拒绝。 |
| TC-HTTP-QUERY-001 | 用本应用凭据按 messageId/clientMessageId 查询本应用和其他应用短信。 | 只返回当前应用短信状态、提交/回执时间和失败信息;其他应用记录统一 404,不泄露租户数据。 |
+35
View File
@@ -1969,3 +1969,38 @@ git diff --check
- 发布包本地与服务器 SHA-256 均为 `b49b51ef1acd2a0bb709ca91fbe0b98c16c80bc86f24b35b5dba141907cc7116`;生产原缺失的 `HTTP_API_MASTER_KEY` 已生成并以 `600` 权限保存,密钥内容未输出。migration `20260716110000_add_http_open_api` 已应用,52 条 migration 全部齐全且 schema 最新;生产运行提交为 `dcb6162dcf93a70e9886c978b07fdccee19dd657`
- `cmpp-api``cmpp-gateway`、MinIO、Nginx、PostgreSQL 和 Redis 正常,`12026/17890/8090/3000/9000` 监听,API/Gateway health、Redis PONG、PostgreSQL readiness 和外部首页、运营端、客户端、客户 Swagger 文档均通过。两个 Redis 通道权威 TPS key 均恢复为真实值 100`gateway.submit.commands` consumer group 为 `pending=0、lag=0`,数据库连接状态有 3 条 connected,部署后 10 分钟内 API/Gateway 无 error 级日志。
- 客户文档 JSON 仅包含约定的 4 个路径:单条发送、短信状态查询、上行列表和上行详情;未鉴权访问真实 HTTP 接口返回 401。Browser 插件不可用,使用现有 Playwright/Chromium 复核生产运营登录、客户端鉴权跳转和 Swagger 文档:页面非空、标题/表单/4 个接口正常,输入控件交互成功,无框架错误覆盖或 console error/warn;未重置账号、未绕过验证码、未发送真实短信。
## 2026-07-16 运营端与客户端 P1/P2 移动端适配(本地未提交)
- AppShell 在不大于 780px 的视口改为 56px 顶部栏与覆盖式左侧抽屉,抽屉默认关闭,支持遮罩、关闭按钮、Esc 和路由切换后自动收起;运营端长菜单和客户端菜单在抽屉内部独立滚动,不再作为 260px 高的页面顶部区域挤压业务内容。桌面折叠侧栏保持原行为。
- 修复两端登录面板宽度计算;运营端报表、企业查询和审核筛选、手机号段库、通道组、通道报备详情、企业签名报备目标、客户端 HTTP 凭据等多列/固定宽度区域在小屏下改为单列或可换行布局。
- 通用 `Table` 为每个单元格输出字段标签,小屏统一转为纵向记录卡片;客户端签名和引流信息列表、通道报备业务列表补专用卡片规则。状态、失败原因、密钥和操作按钮无需依赖横向滚动查看。
- 应用内浏览器使用本地真实 NestJS API/PostgreSQL 的既有客户端会话在 390×844 视口验证:关闭态 `document.scrollWidth=390`、侧栏完全移出视口;打开态抽屉约占 82% 宽度且菜单独立滚动;点击“签名与引流信息”后路由切换并自动关闭。签名列表由原 1050px 横向内容收敛为 343px 卡片,账户账单表格容器和表格均为 293px 且无内部横向滚动;客户端登录页正文和文档宽度均为 390px,登录面板边界为 16~374px。未注入 mock、未绕过认证、未写业务数据。
- 前端 TypeScript/Vite 生产构建、API build 和 `git diff --check` 均已通过;前端仅有既有 Vite chunk size warning。桌面规则位于 780px 媒体查询之外,既有侧栏折叠和桌面表格结构保持不变;按要求暂不提交、不 push、不部署。
- 客户端导航隐藏尚未完成真实后端闭环的整个“彩信服务”分组及六个彩信子菜单;内部占位路由暂时保留,避免把未完成能力暴露给客户。按本轮要求继续保持工作区未提交、未部署。
- 运营端手机号段库按确认设计重做:复用平台通用 Breadcrumb、Button、Input、Tabs、Table、Tag、Pagination 和 Modal;标题区增加用途说明,Tab 下使用当前数据类型的紧凑真实总数信息带,搜索工具栏与数据表形成单一工作区,手机号段使用等宽强调,运营商使用语义标签,删除改为克制的文本危险操作。未增加批量导入、额外筛选或虚构覆盖统计。
- 应用内浏览器使用真实本地运营管理员会话和 NestJS API 验收手机号段库:1536×1024 下页面与表格 `scrollWidth=clientWidth=1230`1280px 下均为 959px,无横向滚动;390×844 下文档、面板、Tab、统计、筛选和表格容器均未超出 390px。已验证 Tab 切换同步“新增号段/新增规则”和当前统计,新增规则 Modal 可打开/关闭,关键词 `138` 查询后输入值保留、重置后清空,console 无 error/warn。设计图中的示例总数、31 省覆盖说明和示例行未照搬,页面只展示本地 PostgreSQL 的真实 0 条数据;分页和标题采用平台通用组件样式。
## 2026-07-16 企业应用 CMPP/HTTP 配置区域拆分(本地未提交)
- 运营端企业应用新增/编辑页将原来混排的“接口配置”拆成独立 CMPP 接入配置和 HTTP 接口配置区块。CMPP 区集中账号、扩展码、接入号、密码、连接数、CMPP 白名单和下游重试;HTTP 区集中能力、HTTP 白名单、QPS、凭据限制、投递方式及 Webhook 安全重试,保存仍沿用现有真实 NestJS API 字段。
- 两个协议使用独立开关和视觉标识;关闭时只收起本协议参数并展示关闭说明,不改变另一协议的开关或表单状态。HTTP 能力静态定义移出组件渲染,避免每次渲染重复创建配置数组。
- Browser 插件不可用,使用已有 Playwright/Chromium、真实本地 NestJS API、PostgreSQL、Redis 和临时平台管理员完成 1440×1000 验收:HTTP 从关闭切换为开启后只展开 HTTP 参数;CMPP 关闭后账号等字段消失但 HTTP QPS 保持可见;页面标题、DOM、控制台均正常。390×844 复核 `body.scrollWidth=viewportWidth=390`,无页面级横向滚动。临时管理员、登录操作日志和会话均已清理,未创建企业应用、未发送短信。
- 使用 Node.js 24 执行前端 TypeScript/Vite 生产构建通过,仅有既有 chunk size warning;默认 Node.js 14 不支持当前 Vite 的 `??=` 语法且会错误返回退出码 0,因此未将旧 Node 结果计为有效构建。按要求暂不提交、不 push、不部署。
## 2026-07-16 全平台金额四位小数精度(本地未提交)
- 根因确认:企业应用编辑页原先按 `Math.round(元 × 100)` 保存,`0.0325 元`只能落为 3 分;PostgreSQL 的余额、流水、单价、计费和利润字段也均为 `Int` 分,无法表达万分之一元。现统一调整为 `1 元 = 10000 金额单位`,字段名中的 `Cents` 仅为兼容既有 API 保留。
- Prisma 金额列统一升级为 `BigInt`migration `20260716150000_expand_money_precision_to_four_decimals` 将历史整数分乘以 100。迁移前已备份本地真实 PostgreSQL;抽查 `AccountTransaction、SmsApplication、SmsMessageRecord、TenantAccount` 汇总,迁移后整数值均精确为迁移前 100 倍,按新除数换算后的人民币金额不变。53 条 migration 已全部应用,Prisma schema validate 和 migrate status 均通过。
- 企业应用客户价、通道成本价、授信和人工充值输入均允许最多 4 位小数并转换为整数金额单位;运营端与客户端的余额、授信、今日消费、今日返还、充值、短信详单、客户价、通道价和利润报表统一固定展示 4 位小数。利润 CSV 改为以“元”为表头并导出 4 位小数。
- NestJS 对客户价、通道价、充值、授信、计费规则和计费结果增加安全整数校验;Prisma `BigInt` 响应仅在 JavaScript 安全整数范围内序列化为 number,超限直接报错,避免静默精度损失。计费单测新增 `325 × 2 = 650` 金额单位,企业应用更新单测使用 `customerUnitPrice=325`
- 使用真实本地 NestJS API、PostgreSQL、Redis 和 Playwright/Chromium 编辑一条已配置通道组的企业应用:页面填写 `0.0325` 后保存,数据库核对 `SmsApplication.customerUnitPrice=325`,再次进入编辑页仍为 `0.0325`,控制台无 error;随后已恢复原单价并清理临时管理员、角色关联和操作日志。
- API 全量 20 个 Jest 测试套件通过,其中金额与企业应用目标套件 45 条用例通过;API TypeScript build、前端 TypeScript/Vite 生产 build、Gateway `go test ./...`、Prisma validate/status 和 `git diff --check` 均通过。按要求暂不提交、不 push、不部署。
## 2026-07-16 企业应用参数复制与下游 CMPP 约束修复(本地未提交)
- 根因确认:运营端 CMPP 参数 API 原先误取最新上游 `SmsChannel.gatewayHost/gatewayPort`,不是客户接入平台的地址;页面直接调用 `navigator.clipboard.writeText`,在生产 HTTP 非安全上下文可能无提示失败。现改为读取 `CMPP_PUBLIC_HOST/CMPP_PUBLIC_PORT`,并增加 Clipboard API 失败后的 textarea 降级复制和错误提示。
- 运营端企业应用列表补充 HTTP 参数查看/复制;客户端接口对接页补充 HTTP 参数复制。客户端未开通 CMPP 时按钮禁用,客户端 CMPP 参数 API 同时返回 403,避免仅靠前端隐藏后仍可读取密码等接入参数。
- Gateway 登录响应接入真实 `cmppMaxConnections`,按应用活动 TCP 会话计数并拒绝超限 bind;底层连接关闭回调会清理会话和回写断开。API 连接事件再次校验应用状态、接口开关、IP/CIDR 白名单和连接数,存量连接在下一次心跳不再符合配置时由 Gateway 主动关闭。
- 生产只读核查发现应用 `715011` 最大连接数为 1,当前来源 IP 为 `183.194.97.158`,白名单后来改为 `2.2.2.2`;连接建立早于白名单修改,印证旧实现不会主动清理存量连接。核查期间未修改生产配置、数据库或进程。
- API 目标测试 2 个套件 97 条以及全量 20 个套件 213 条断言通过,API TypeScript build、Gateway `go test ./...`、前端 TypeScript/Vite build 和部署脚本语法检查通过;全量 Jest 完成后仍提示既有异步句柄未关闭,断言结果不受影响,测试进程已单独结束。使用真实本地 NestJS API、PostgreSQL 和 Playwright/Chromium 验证运营端两类参数复制、客户端未开通 CMPP 的页面禁用与 API 403,以及 HTTP 页面复制降级;临时数据已清理。按要求暂不提交、不 push、不部署。