Browse Source

V2.5.1 广播修复

liuq 1 month ago
parent
commit
68ea19a487

+ 18 - 7
backend/app/api/v1/endpoints/messages.py

@@ -216,14 +216,25 @@ async def create_message(
 
     # 4. 创建消息
     if is_broadcast:
-        # 查找所有活跃用户 (is_deleted=0)
-        active_users = db.query(User.id).filter(
-            User.status == "ACTIVE", 
-            User.is_deleted == 0
-        ).all()
-        
+        # 仅当前认证应用下存在有效映射、且平台账号活跃未删除的用户
+        active_users = (
+            db.query(User.id)
+            .join(AppUserMapping, AppUserMapping.user_id == User.id)
+            .filter(
+                AppUserMapping.app_id == app_id_int,
+                AppUserMapping.is_active.is_(True),
+                User.status == "ACTIVE",
+                User.is_deleted == 0,
+            )
+            .distinct()
+            .all()
+        )
+
         if not active_users:
-            raise HTTPException(status_code=400, detail="没有可发送的活跃用户")
+            raise HTTPException(
+                status_code=400,
+                detail="该应用下没有可发送的活跃用户(需存在有效用户映射且平台账号为 ACTIVE、未删除)",
+            )
             
         messages_to_insert = []
         for u in active_users:

+ 18 - 0
backend/tests/test_messages_broadcast_scope.py

@@ -0,0 +1,18 @@
+"""Guard: broadcast receivers are scoped to active app mappings (see create_message)."""
+import unittest
+from pathlib import Path
+
+
+class TestBroadcastScopeSource(unittest.TestCase):
+    def test_create_message_broadcast_joins_mapping(self):
+        root = Path(__file__).resolve().parent.parent
+        path = root / "app" / "api" / "v1" / "endpoints" / "messages.py"
+        text = path.read_text(encoding="utf-8")
+        self.assertIn("is_broadcast", text)
+        self.assertIn("join(AppUserMapping", text)
+        self.assertIn("AppUserMapping.is_active", text)
+        self.assertIn(".distinct()", text)
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 4 - 4
docs/LLM_UAP_INTEGRATION.md

@@ -897,7 +897,7 @@ X-App-Access-Token: pA9s8d7f6g5h4j3k2l1m0n9o8p7q6r5s4t3u2v1w0
 本节**仅**说明由**应用服务端**调用、使用 **Header 签名**(第 3 节方式 B、第 4.2 节)发送 **`NOTIFICATION`** 的两种形态:
 
 1. **单用户系统通知**(指定一名接收者)  
-2. **广播**(向全体活跃用户各发一条)
+2. **广播**(向当前应用下「有效映射且平台活跃」的用户各发一条)
 
 不涉及用户间私信(`type: MESSAGE`)及用户 JWT 代发,对接方无需关注。
 
@@ -1054,9 +1054,9 @@ X-Sign: <按 X-App-Id 与 X-Timestamp 计算的签名>
 }
 ```
 
-### 8.3 广播(全员系统通知)
+### 8.3 广播(应用内系统通知)
 
-仅应用可调;向平台内**全部活跃用户**各投递一条通知
+仅应用可调;向 **`X-App-Id` / 签名身份对应应用** 下、**存在有效用户映射**(`is_active=true`)且平台用户为 **ACTIVE、未删除** 的每个用户各投递一条通知(全平台其他应用用户不会收到)
 
 | 字段 | 类型 | 必填 | 说明 |
 |------|------|------|------|
@@ -1090,7 +1090,7 @@ X-Sign: 5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcdef0123
 }
 ```
 
-**响应 `200`:** 返回 **`MessageResponse`**,与「为全体用户各创建一条通知」中的**第一条**记录对应(便于对接方拿到一条结构化回包);其余用户的消息结构相同,仅 `id`、`receiver_id` 等不同。若平台无活跃用户则返回 `400`,`detail` 为「没有可发送的活跃用户」
+**响应 `200`:** 返回 **`MessageResponse`**,与「为上述范围内每个用户各创建一条通知」中的**第一条**记录对应(便于对接方拿到一条结构化回包);其余用户的消息结构相同,仅 `id`、`receiver_id` 等不同。若该应用下无可投递用户则返回 `400`,`detail` 标明该应用下无活跃用户(须有效映射且平台账号 ACTIVE、未删除)
 
 ```json
 {

+ 4 - 4
frontend/public/docs/client_api_guide.md

@@ -219,7 +219,7 @@ Authorization: Bearer <JWT_TOKEN>
 - **接口**:`POST {{API_BASE_URL}}/messages/`
 - **说明**:
   - 用户调用:仅允许 `type = MESSAGE`
-  - 应用调用:支持 `MESSAGE` / `NOTIFICATION` / 广播
+  - 应用调用:支持 `MESSAGE` / `NOTIFICATION` / 应用内广播(有映射的用户)
   - **用户申请通知**:`type = MESSAGE` 且 `content_type = USER_NOTIFICATION`,在私信会话中以通知卡片样式展示(标题 + 结构化内容 + 操作按钮);需跳转业务页时配合 `app_id`、`auto_sso`、`target_url`(后端生成 `action_url`)
 
 #### 调用约定
@@ -243,7 +243,7 @@ Authorization: Bearer <JWT_TOKEN>
 | `app_id` | 条件 | 应用字符串 ID;与 `app_user_id` 联用解析接收者;用户发 `USER_NOTIFICATION` 且 `auto_sso` 时需填 |
 | `app_user_id` | 条件 | 业务账号,须在映射表存在 |
 | `sender_app_user_id` | 否 | 应用发信时可选:发起人业务账号,解析后写入 `sender_id` 用于审计和会话聚合;用户 JWT 发信忽略 |
-| `is_broadcast` | 否 | `true` 时向全员发通知;**仅** `type = NOTIFICATION` + **应用签名**;勿填接收者 |
+| `is_broadcast` | 否 | `true` 时向 **当前 `X-App-Id` 应用下已有有效映射** 的活跃用户发通知;**仅** `type = NOTIFICATION` + **应用签名**;勿填接收者 |
 | `auto_sso` | 否 | 与 `target_url` 配合;`NOTIFICATION` 或 `USER_NOTIFICATION` 时可自动生成 `action_url`(jump) |
 | `target_url` | 否 | 业务落地页 URL(`auto_sso = true` 时使用) |
 | `action_url` / `action_text` | 否 | 自定义按钮;接收端点击若走 `callback-url`,需为后端识别的 jump 格式 |
@@ -478,7 +478,7 @@ Content-Type: application/json
 }
 ```
 
-广播示例(应用签名,全员 `NOTIFICATION`):
+广播示例(应用签名,对本应用映射用户 `NOTIFICATION`):
 
 ```http
 POST {{API_BASE_URL}}/messages/
@@ -492,7 +492,7 @@ Content-Type: application/json
   "is_broadcast": true,
   "content_type": "TEXT",
   "title": "系统公告",
-  "content": "这是一条全员通知"
+  "content": "这是一条面向本应用映射用户的公告"
 }
 ```
 

+ 10 - 10
frontend/public/docs/message_integration.md

@@ -14,7 +14,7 @@
 
 | 接口 | 用户权限 | 应用权限 | 说明 |
 |------|---------|---------|------|
-| `POST /messages/` | ✅ 仅 MESSAGE | ✅ MESSAGE + NOTIFICATION + BROADCAST | 用户只能发私信,应用可发通知和广播 |
+| `POST /messages/` | ✅ 仅 MESSAGE | ✅ MESSAGE + NOTIFICATION + BROADCAST | 用户只能发私信;应用可发通知与**应用内**广播(映射用户) |
 | `GET /messages/conversations` | ✅ | ❌ | 仅用户可查询 |
 | `GET /messages/history/{id}` | ✅ | ❌ | 仅用户可查询 |
 | `GET /messages/unread-count` | ✅ | ❌ | 仅用户可查询 |
@@ -148,13 +148,13 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
 
 ### 3.3 广播消息接口 (Broadcast)
 
-广播消息接口允许应用向所有活跃用户发送系统通知。适用于系统公告、重要通知等需要全员推送的场景。
+广播消息接口允许应用向「当前签名身份对应应用」下已有用户映射的活跃用户发送系统通知。适用于面向本应用用户的公告、运维通知等场景。
 
 **重要说明:**
 - **仅支持系统通知**:广播模式仅支持 `type: "NOTIFICATION"`,不支持私信类型
 - **仅应用可调用**:广播功能仅限应用通过签名认证调用,普通用户无权使用
-- **发送给所有活跃用户**:系统会自动查找所有状态为 `ACTIVE` 且未删除的用户,为每个用户创建一条消息记录
-- **实时推送**:所有在线用户会通过 WebSocket 实时收到推送
+- **发送范围(该应用内)**:系统在 `X-App-Id` 对应应用下,查找 **存在有效用户映射**(`is_active=true`)、且平台用户状态为 `ACTIVE`、`is_deleted=0` 的用户,为其中每个用户创建一条消息记录(全平台未绑定该应用的用户不会收到)
+- **实时推送**:上述用户中当前在线者会通过 WebSocket 实时收到推送
 
 **接口地址**: `POST {{API_BASE_URL}}/messages/`
 
@@ -174,7 +174,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
 | `action_url` | string | 否 | 自定义跳转链接(不使用 SSO 时使用) |
 | `action_text` | string | 否 | 操作按钮文案 |
 
-**注意:** 广播模式下,**不需要**提供 `receiver_id` 或 `app_user_id`,系统会自动向所有活跃用户发送。
+**注意:** 广播模式下,**不需要**提供 `receiver_id` 或 `app_user_id`,系统会自动向「本应用下已有有效映射」的活跃用户发送。
 
 **完整 HTTP 请求示例 (应用签名认证):**
 
@@ -221,7 +221,7 @@ X-Sign: a1b2c3d4e5f6... (HMAC-SHA256签名)
 
 **响应示例:**
 
-接口返回的是第一条创建的消息记录(用于签名验证),实际所有用户都会收到消息
+接口返回的是第一条创建的消息记录(用于签名验证);符合上述范围的用户各有一条独立消息记录
 
 ```json
 {
@@ -291,10 +291,10 @@ print(f"Response: {response.json()}")
 
 1. **权限限制**:只有通过应用签名认证的请求才能使用广播功能,普通用户 Token 认证无法使用
 2. **消息类型限制**:广播仅支持 `NOTIFICATION` 类型,不支持 `MESSAGE` 类型
-3. **用户范围**:广播会发送给所有状态为 `ACTIVE` 且 `is_deleted=0` 的用户
-4. **性能考虑**:如果用户数量很大,广播操作可能需要一些时间,建议在后台异步处理
-5. **实时推送**:所有在线用户会通过 WebSocket 实时收到推送,离线用户可以在下次登录时查看
-6. **消息记录**:每个用户都会收到一条独立的消息记录,可以单独标记已读或删除
+3. **用户范围**:广播仅面向「当前应用下存在有效映射」且平台账号为 `ACTIVE`、`is_deleted=0` 的用户;若该条件下无用户则返回 `400`
+4. **性能考虑**:如果映射用户数量很大,广播操作可能需要一些时间,建议在后台异步处理
+5. **实时推送**:在线的接收者会通过 WebSocket 实时收到推送,离线用户可以在下次登录时查看
+6. **消息记录**:每个接收用户都会收到一条独立的消息记录,可以单独标记已读或删除
 
 ### 3.4 如何获取接收者ID (receiver_id) 和应用ID (app_id)
 

+ 4 - 4
frontend/public/skills/uap-api/LLM_UAP_INTEGRATION.md

@@ -705,7 +705,7 @@ X-App-Access-Token: pA9s8d7f6g5h4j3k2l1m0n9o8p7q6r5s4t3u2v1w0
 本节说明由**应用服务端**调用、使用 **Header 签名**(第 3 节方式 B、第 4.2 节)发送 **`NOTIFICATION`** 的两种形态:
 
 1. **单用户系统通知**(指定一名接收者)
-2. **广播**(向全体活跃用户各发一条)
+2. **广播**(向当前应用下「有效映射且平台活跃」的用户各发一条)
 
 不涉及用户间私信(`type: MESSAGE`)的**发送**。
 
@@ -869,9 +869,9 @@ X-Sign: <按 X-App-Id 与 X-Timestamp 计算的签名>
 }
 ```
 
-### 8.3 广播(全员系统通知)
+### 8.3 广播(应用内系统通知)
 
-仅应用可调;向平台内**全部活跃用户**各投递一条通知
+仅应用可调;向 **`X-App-Id` / 签名身份对应应用** 下、**存在有效用户映射**(`is_active=true`)且平台用户为 **ACTIVE、未删除** 的每个用户各投递一条通知(全平台其他应用用户不会收到)
 
 | 字段 | 类型 | 必填 | 说明 |
 |------|------|------|------|
@@ -905,7 +905,7 @@ X-Sign: 5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcdef0123
 }
 ```
 
-**响应 `200`:** 返回 **`MessageResponse`**,与「为全体用户各创建一条通知」中的**第一条**记录对应(便于对接方拿到一条结构化回包);其余用户的消息结构相同,仅 `id`、`receiver_id` 等不同。若平台无活跃用户则返回 `400`,`detail` 为「没有可发送的活跃用户」
+**响应 `200`:** 返回 **`MessageResponse`**,与「为上述范围内每个用户各创建一条通知」中的**第一条**记录对应(便于对接方拿到一条结构化回包);其余用户的消息结构相同,仅 `id`、`receiver_id` 等不同。若该应用下无可投递用户则返回 `400`,`detail` 标明该应用下无活跃用户(须有效映射且平台账号 ACTIVE、未删除)
 
 ```json
 {

+ 8 - 8
frontend/public/skills/uap-api/SKILL.md

@@ -87,7 +87,7 @@ description: 在业务系统中对接统一认证平台(UAP / Unified Authenti
 >
 > - §A 一键登录要**真正跳进本系统**,前提是目标用户手机号**在该 `APP_ID` 下已建立映射**——`POST /simple/validate` 响应里 `mapped_key` 必须非 `null`,业务侧才能据此定位本系统账号。
 > - 也就是说:**§C 用户同步是 §A 一键登录的前置**。上线 SSO 之前,通常要先把本系统用户通过 `POST /apps/mapping/sync` 批量或按需推到 UAP 该 `APP_ID` 下。没有映射的手机号即使能在 UAP 登录,到 callback 侧也会拿到 `mapped_key=null`,无法一键进入本系统。
-> - §B 若按 `app_user_id` 投递消息,同样依赖映射存在;仅按平台 `user_id` 或广播时不依赖
+> - §B 若按 `app_user_id` 投递消息依赖映射存在;按平台 `receiver_id` 投递不依赖映射。**广播**的接收者为「当前 `APP_ID` 下有效映射 ∩ 平台活跃用户」,同样依赖映射已建立
 
 ### A. 一键登录 / SSO(Redirect SSO)
 
@@ -127,7 +127,7 @@ description: 在业务系统中对接统一认证平台(UAP / Unified Authenti
 > |------|-------|-------------|
 > | **B-1 平台自动 → 用户** | 平台定时任务 / 触发器自动发:告警、工单未审核提醒、任务超时提醒、备份完成回执等 | 系统通知 |
 > | **B-2 用户 → 用户**(业务流) | 人类用户在应用里操作触发,平台代发给另一人:OA 请假 / 报销 / 审批通过 / 任务下发 / 评论 @ 等 | 发起人的私信(会话聚合到 A) |
-> | **B-3 平台 → 全员广播** | 仅平台级公告:系统维护、版本上线、全员通知 | 系统通知(全量分发) |
+> | **B-3 应用内广播** | 面向本应用全体**已映射**用户:维护公告、版本须知等 | 系统通知(按应用维度分发) |
 
 **前置确认(三类都要)**:
 
@@ -220,17 +220,17 @@ description: 在业务系统中对接统一认证平台(UAP / Unified Authenti
 
 ---
 
-#### B-3 平台 → 全员广播(独立功能)
+#### B-3 应用内广播(独立功能)
 
-**特征**:一次调用向平台**全部活跃用户**各建一条通知;**仅应用身份可调**。
+**特征**:一次调用向**当前签名应用**下所有「存在有效用户映射且平台账号活跃」的用户各建一条通知;**仅应用身份可调**。
 
 **约束(后端强制)**:
 
 - `is_broadcast: true`;
 - `type` **必须** `"NOTIFICATION"`(否则 403 "广播模式仅支持系统通知");
 - **不**传 `receiver_id` / `app_user_id`;
-- **不**传 `sender_app_user_id`(广播通常是平台级公告,显示系统通知样式);
-- 平台无活跃用户时 400
+- **不**传 `sender_app_user_id`(广播通常是应用级公告,显示系统通知样式);
+- 该应用下无可投递用户时返回 `400`(无有效映射活跃用户)
 
 **Body 模板**:
 
@@ -259,7 +259,7 @@ description: 在业务系统中对接统一认证平台(UAP / Unified Authenti
 | `type` | `NOTIFICATION` | `NOTIFICATION` | `NOTIFICATION`(强制) |
 | `content_type` | `TEXT` 为主 | **`USER_NOTIFICATION`** | `TEXT` |
 | `sender_app_user_id` | **不传** | **必传**(发起人 mapped_key) | **不传** |
-| 接收者字段 | `app_user_id` / `receiver_id` | 同左 | `is_broadcast:true`,其他都不传 |
+| 接收者字段 | `app_user_id` / `receiver_id` | 同左 | `is_broadcast:true`,其他都不传(范围=本应用有效映射用户) |
 | 前端会话归类 | 系统通知 | 发起人的私信会话 | 系统通知 |
 | 触发方 | 定时任务 / 事件监听 | 用户在应用内操作 | 管理员手动(独立入口) |
 
@@ -271,7 +271,7 @@ description: 在业务系统中对接统一认证平台(UAP / Unified Authenti
 - B-2 发起人在 UAP 该 `APP_ID` 下**没有映射** → 404(先走 §C 建映射)。
 - 应用身份想用 `type:"MESSAGE"` 代用户发私信——平台只允许 `NOTIFICATION`,应改走 B-2 的 `USER_NOTIFICATION`。
 - 广播时误传 `receiver_id` / `app_user_id`,或把 `type` 写成 `MESSAGE` → 403 / 400。
-- 把广播入口和 B-1 / B-2 合并 → 误点击一次全员炸
+- 把广播入口和 B-1 / B-2 合并 → 误点击一次影响该应用全体映射用户
 
 ### C. 用户同步 / 映射绑定
 

+ 1 - 1
frontend/src/views/help/ClientApi.vue

@@ -303,7 +303,7 @@ Authorization: Bearer &lt;JWT_TOKEN&gt;</pre>
           <tr><td><code>app_id</code></td><td>条件</td><td>应用字符串 ID;与 <code>app_user_id</code> 一起解析接收者;用户发 <code>USER_NOTIFICATION</code> 且 <code>auto_sso</code> 时需填以生成 jump</td></tr>
           <tr><td><code>app_user_id</code></td><td>条件</td><td>业务侧账号,须在 <code>app_user_mappings</code> 有映射</td></tr>
           <tr><td><code>sender_app_user_id</code></td><td>否</td><td>应用发信时:发起人业务账号,解析后写入 <code>sender_id</code> 用于审计和会话聚合;用户 JWT 发信忽略</td></tr>
-          <tr><td><code>is_broadcast</code></td><td>否</td><td><code>true</code> 时全员通知;仅 <code>type: NOTIFICATION</code> + 应用签名;勿填接收者</td></tr>
+          <tr><td><code>is_broadcast</code></td><td>否</td><td><code>true</code> 时对当前 <code>X-App-Id</code> 下已有有效映射的活跃用户发通知;仅 <code>type: NOTIFICATION</code> + 应用签名;勿填接收者</td></tr>
           <tr><td><code>auto_sso</code> / <code>target_url</code></td><td>否</td><td>为 <code>true</code> 且提供 <code>target_url</code> 时,<code>NOTIFICATION</code> 或 <code>USER_NOTIFICATION</code> 可自动生成 <code>action_url</code>(jump)</td></tr>
           <tr><td><code>action_url</code> / <code>action_text</code></td><td>否</td><td>自定义按钮链接与文案;若用 <code>callback-url</code> 接口,jump 格式需与后端约定一致</td></tr>
         </tbody>

+ 10 - 10
frontend/src/views/help/MessageIntegration.vue

@@ -30,7 +30,7 @@
             <td><code>POST /messages/</code></td>
             <td>✅ 仅 MESSAGE</td>
             <td>✅ MESSAGE + NOTIFICATION + BROADCAST</td>
-            <td>用户只能发私信,应用可发通知和广播</td>
+            <td>用户只能发私信,应用可发通知与<strong>应用内</strong>广播(映射用户)</td>
           </tr>
           <tr>
             <td><code>GET /messages/conversations</code></td>
@@ -223,14 +223,14 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
       </div>
 
       <h4>3.3 广播消息接口 (Broadcast)</h4>
-      <p>广播消息接口允许应用向所有活跃用户发送系统通知。适用于系统公告、重要通知等需要全员推送的场景。</p>
+      <p>广播消息接口允许应用向「当前签名身份对应应用」下已有用户映射的活跃用户发送系统通知。适用于面向本应用用户的公告、运维通知等场景。</p>
       
       <p><strong>重要说明:</strong></p>
       <ul>
         <li><strong>仅支持系统通知</strong>:广播模式仅支持 <code>type: "NOTIFICATION"</code>,不支持私信类型</li>
         <li><strong>仅应用可调用</strong>:广播功能仅限应用通过签名认证调用,普通用户无权使用</li>
-        <li><strong>发送给所有活跃用户</strong>:系统会自动查找所有状态为 <code>ACTIVE</code> 且未删除的用户,为每个用户创建一条消息记录</li>
-        <li><strong>实时推送</strong>:所有在线用户会通过 WebSocket 实时收到推送</li>
+        <li><strong>发送范围(该应用内)</strong>:系统在 <code>X-App-Id</code> 对应应用下,查找存在有效用户映射(<code>is_active=true</code>)、且平台用户为 <code>ACTIVE</code>、未删除的用户,为其中每个用户创建一条消息记录</li>
+        <li><strong>实时推送</strong>:上述用户中当前在线者会通过 WebSocket 实时收到推送</li>
       </ul>
       
       <ul>
@@ -256,7 +256,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
         </tbody>
       </table>
 
-      <p><strong>注意:</strong> 广播模式下,<strong>不需要</strong>提供 <code>receiver_id</code> 或 <code>app_user_id</code>,系统会自动向所有活跃用户发送。</p>
+      <p><strong>注意:</strong> 广播模式下,<strong>不需要</strong>提供 <code>receiver_id</code> 或 <code>app_user_id</code>,系统会自动向「本应用下已有有效映射」的活跃用户发送。</p>
 
       <p><strong>完整 HTTP 请求示例 (应用签名认证):</strong></p>
       <div class="code-block">
@@ -304,7 +304,7 @@ X-Sign: a1b2c3d4e5f6... (HMAC-SHA256签名)
       </div>
 
       <p><strong>响应示例:</strong></p>
-      <p>接口返回的是第一条创建的消息记录(用于签名验证),实际所有用户都会收到消息:</p>
+      <p>接口返回的是第一条创建的消息记录(用于签名验证);符合上述范围的用户各有一条独立消息记录:</p>
       <div class="code-block">
         <pre>
 {
@@ -376,10 +376,10 @@ print(f"Response: {response.json()}")
       <ul>
         <li><strong>权限限制</strong>:只有通过应用签名认证的请求才能使用广播功能,普通用户 Token 认证无法使用</li>
         <li><strong>消息类型限制</strong>:广播仅支持 <code>NOTIFICATION</code> 类型,不支持 <code>MESSAGE</code> 类型</li>
-        <li><strong>用户范围</strong>:广播会发送给所有状态为 <code>ACTIVE</code> 且 <code>is_deleted=0</code> 的用户</li>
-        <li><strong>性能考虑</strong>:如果用户数量很大,广播操作可能需要一些时间,建议在后台异步处理</li>
-        <li><strong>实时推送</strong>:所有在线用户会通过 WebSocket 实时收到推送,离线用户可以在下次登录时查看</li>
-        <li><strong>消息记录</strong>:每个用户都会收到一条独立的消息记录,可以单独标记已读或删除</li>
+        <li><strong>用户范围</strong>:广播仅面向「当前应用下存在有效映射」且平台账号为 <code>ACTIVE</code>、<code>is_deleted=0</code> 的用户;若该条件下无用户则返回 <code>400</code></li>
+        <li><strong>性能考虑</strong>:如果映射用户数量很大,广播操作可能需要一些时间,建议在后台异步处理</li>
+        <li><strong>实时推送</strong>:在线的接收者会通过 WebSocket 实时收到推送,离线用户可以在下次登录时查看</li>
+        <li><strong>消息记录</strong>:每个接收用户都会收到一条独立的消息记录,可以单独标记已读或删除</li>
       </ul>
 
       <h4>3.4 如何获取接收者ID (receiver_id) 和应用ID (app_id)</h4>