# 用户举报 Flutter 对接 本文描述 Flutter App 对接用户举报和房间举报的 HTTP 契约。通用文件上传接口用于上传举报图片并拿到 URL,提交举报接口只接收 JSON。 ## 基础地址 本地开发地址: ```text http://127.0.0.1:13000 ``` 线上环境使用当前 App 配置里的 gateway base URL。下面所有路径都拼在 gateway base URL 后。 ## 请求头 | Header | 必填 | 示例 | 说明 | | --- | --- | --- | --- | | `Authorization` | 是 | `Bearer ` | 登录 access token;举报发起人只从 token 读取,Flutter 不传举报发起人 ID。 | | `X-App-Code` | 否 | `lalu` | App 编码;登录态接口最终以 token 内的 `app_code` 为准。 | | `Content-Type` | 提交举报必填 | `application/json` | 提交举报使用 JSON。 | | `Content-Type` | 上传图片必填 | `multipart/form-data` | 上传举报图片使用 multipart。 | 统一响应 envelope: ```json { "code": "OK", "message": "ok", "request_id": "req_abc", "data": {} } ``` Flutter 只在 `code == "OK"` 时读取 `data`。失败时记录 `request_id`,用于后端排查。 ## 调用顺序 ```text 1. 用户在举报弹窗选择举报类型。 2. 如有举报图,逐张调用 POST /api/v1/files/upload,收集每次返回的 data.url。 3. 调用 POST /api/v1/reports 提交举报。 4. 提交成功后关闭弹窗,并用 data.report_id 写本地日志。 ``` ## 上传举报图片 上传接口只负责把文件写入对象存储并返回 URL,不创建举报单。 ```http POST /api/v1/files/upload Authorization: Bearer X-App-Code: lalu Content-Type: multipart/form-data file= ``` 请求字段: | 字段 | 必填 | 类型 | 说明 | | --- | --- | --- | --- | | `file` | 是 | multipart file | 单文件最大 20MB。多图逐张上传,Flutter 把每张图返回的 `data.url` 放进提交举报接口的 `image_urls`。 | 成功响应: ```json { "code": "OK", "message": "ok", "request_id": "req_upload_report_image", "data": { "url": "https://cdn.example.com/app/files/10001/20260529/file_xxx.jpg", "object_key": "app/files/10001/20260529/file_xxx.jpg", "content_type": "image/jpeg", "size_bytes": 345678 } } ``` Flutter 提交举报时只需要使用 `data.url`。 ## 提交举报 ```http POST /api/v1/reports Authorization: Bearer X-App-Code: lalu Content-Type: application/json { "user_id": "10002", "report_type": "pornography", "reason": "用户在房间内发布违规内容", "image_urls": [ "https://cdn.example.com/app/files/10001/20260529/file_xxx.jpg" ] } ``` 举报房间示例: ```http POST /api/v1/reports Authorization: Bearer X-App-Code: lalu Content-Type: application/json { "room_id": "lalu_abc123", "report_type": "fraud", "reason": "房间引导线下交易", "image_urls": [] } ``` 请求字段: | 字段 | 必填 | 类型 | 说明 | | --- | --- | --- | --- | | `user_id` | 条件必填 | string | 被举报用户 ID。和 `room_id` 二选一,只能传一个。 | | `room_id` | 条件必填 | string | 被举报房间 ID。和 `user_id` 二选一,只能传一个。 | | `report_type` | 是 | string | 举报类型,取值见下方枚举。 | | `reason` | 否 | string | 举报理由;Flutter 可允许用户不填。 | | `image_urls` | 否 | string[] | 举报图 URL 数组;无图可传 `[]` 或不传。 | 目标校验规则: | 场景 | 结果 | | --- | --- | | 只传 `user_id` | 举报用户。 | | 只传 `room_id` | 举报房间。 | | `user_id` 和 `room_id` 都不传 | `400 INVALID_ARGUMENT`。 | | `user_id` 和 `room_id` 同时传 | `400 INVALID_ARGUMENT`,Flutter 需要拆成一次明确的用户举报或房间举报。 | 举报发起人不在请求体里传。gateway 必须从 `Authorization` 对应的 access token 读取当前登录用户,避免客户端伪造举报发起人。 ## 举报类型 | UI 文案 | `report_type` | | --- | --- | | Politics | `politics` | | Pornography | `pornography` | | Graphic Violence | `graphic_violence` | | Verbal Abuse | `verbal_abuse` | | Fraud | `fraud` | | Illegal Content | `illegal_content` | | Minors | `minors` | | In-Person Transactions | `in_person_transactions` | | Other | `other` | ## 返回体模拟数据 举报用户成功: ```json { "code": "OK", "message": "ok", "request_id": "req_report_user", "data": { "report_id": "rpt_10001_1778000000000", "target_type": "user", "user_id": "10002", "room_id": "", "report_type": "pornography", "status": "submitted", "created_at_ms": 1778000000000 } } ``` 举报房间成功: ```json { "code": "OK", "message": "ok", "request_id": "req_report_room", "data": { "report_id": "rpt_10001_1778000001000", "target_type": "room", "user_id": "", "room_id": "lalu_abc123", "report_type": "fraud", "status": "submitted", "created_at_ms": 1778000001000 } } ``` 字段说明: | 字段 | 类型 | 说明 | | --- | --- | --- | | `report_id` | string | 举报单 ID;Flutter 可用于本地日志和客服排查。 | | `target_type` | string | `user` 或 `room`,由服务端按本次传入目标生成。 | | `user_id` | string | 被举报用户 ID;房间举报返回空字符串。 | | `room_id` | string | 被举报房间 ID;用户举报返回空字符串。 | | `report_type` | string | 本次提交的举报类型。 | | `status` | string | 首次提交固定返回 `submitted`。 | | `created_at_ms` | int64 | 举报创建时间,Unix epoch milliseconds。 | ## 错误处理 失败返回仍使用统一 envelope: ```json { "code": "INVALID_ARGUMENT", "message": "invalid argument", "request_id": "req_abc" } ``` | HTTP 状态码 | `code` | Flutter 处理 | | --- | --- | --- | | `400` | `INVALID_JSON` | JSON 格式错误,检查本地序列化逻辑。 | | `400` | `INVALID_ARGUMENT` | `user_id/room_id` 二选一不满足、`report_type` 非法、图片 URL 格式非法或字段超长。 | | `401` | `UNAUTHORIZED` | token 缺失、无效、过期或会话失效,重新登录。 | | `403` | `PROFILE_REQUIRED` | 用户资料未完成,跳转资料补全流程。 | | `404` | `NOT_FOUND` | 被举报用户不存在,刷新页面状态。 | | `502` | `UPSTREAM_ERROR` | 内部服务暂不可用,可提示稍后重试。 | ## Flutter 数据模型建议 ```dart class SubmitReportRequest { SubmitReportRequest({ this.userId, this.roomId, required this.reportType, this.reason, this.imageUrls = const [], }); final String? userId; final String? roomId; final String reportType; final String? reason; final List imageUrls; Map toJson() => { if (userId != null && userId!.isNotEmpty) 'user_id': userId, if (roomId != null && roomId!.isNotEmpty) 'room_id': roomId, 'report_type': reportType, if (reason != null && reason!.trim().isNotEmpty) 'reason': reason!.trim(), if (imageUrls.isNotEmpty) 'image_urls': imageUrls, }; } ``` 提交前本地校验: ```dart void validateReportTarget({String? userId, String? roomId}) { final hasUser = userId != null && userId.trim().isNotEmpty; final hasRoom = roomId != null && roomId.trim().isNotEmpty; if (hasUser == hasRoom) { throw ArgumentError('user_id and room_id must be exactly one'); } } ``` ## 实现边界 - Flutter 不传举报发起人 ID,服务端只认 token 中的当前用户。 - `user_id` 表示被举报用户,不是举报发起人。 - `room_id` 表示被举报房间,不要求同时传房间内某个用户。 - `user_id` 和 `room_id` 必须二选一,不能同时提交一个混合举报。 - 举报图片先上传再提交 URL;提交举报接口不接收 multipart 文件。 - `message` 不作为业务分支依据,Flutter 只按 `code` 和 HTTP 状态处理。