hyapp-server/docs/flutter对接/用户举报Flutter对接.md
2026-05-29 16:48:00 +08:00

273 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 用户举报 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>` | 登录 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 <access_token>
X-App-Code: lalu
Content-Type: multipart/form-data
file=<binary>
```
请求字段:
| 字段 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `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 <access_token>
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 <access_token>
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 | 举报单 IDFlutter 可用于本地日志和客服排查。 |
| `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<String> imageUrls;
Map<String, dynamic> 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 状态处理。