接入举报摄影师正式接口

This commit is contained in:
2026-07-08 16:38:15 +08:00
parent 4a78a0c21a
commit 290a01e699
12 changed files with 2586 additions and 218 deletions

View File

@ -0,0 +1,290 @@
# 举报摄影师接口文档
本文档记录 iOS `WildPhotographerReport` 模块当前使用的后端接口,以及仍处于 Mock 阶段的业务点。
## 基础约定
- 业务域:举报摄影师
- API 封装:`suixinkan/Features/WildPhotographerReport/API/WildPhotographerReportAPI.swift`
- 网络入口:`NetworkServices.shared.wildPhotographerReportAPI`
- 后端成功码:`100000`
- 响应包裹:工程统一 `APIEnvelope<T>`
- 当前状态:“举报类型”、“提交举报”和“我的举报列表”已接接口;详情、补充证据、风险地图仍使用 Mock 数据闭环。
## 1. 获取举报类型
### 用途
提交举报页面进入时拉取可选举报类型。举报类型由后台动态维护,客户端不能用枚举写死,也不展示图标。
### 请求
```http
GET /api/yf-handset-app/photog/report/types
```
### 请求参数
无。
### 响应示例
```json
{
"code": 100000,
"msg": "success",
"data": {
"list": [
{
"value": 1,
"label": "私下收款"
},
{
"value": 2,
"label": "疑似打野"
},
{
"value": 3,
"label": "未戴工牌"
},
{
"value": 4,
"label": "其他"
}
]
},
"time": "2026-07-08 14:21:21"
}
```
### iOS 模型
```swift
struct WildReportType: Decodable, Hashable {
let value: Int
let label: String
}
struct WildReportTypeListResponse: Decodable, Hashable {
let list: [WildReportType]
}
```
### UI 行为
- 页面加载时调用 `WildPhotographerReportSubmitViewModel.loadReportTypes(api:)`
- 按接口返回顺序渲染举报类型。
- 每行最多展示 3 个类型,超过 3 个自动换行。
-`label` 展示文字,不展示图标。
- 默认选中 `value == 2` 的类型;如果接口返回中不存在该类型,则选中列表第一项。
- 接口失败时回退本地兜底列表,并提示“举报类型获取失败,已使用默认类型”。
### 当前调用位置
- `WildPhotographerReportSubmitViewController.viewDidLoad()`
- `WildPhotographerReportSubmitViewModel.loadReportTypes(api:)`
- `WildPhotographerReportAPI.reportTypes()`
## 2. 提交举报
### 用途
提交页点击“提交举报”时调用。客户端会先把现场证据和支付截图上传到 OSS全部上传成功后再提交表单数据。
### 请求
```http
POST /api/yf-handset-app/photog/report/submit
```
### 请求参数
```json
{
"scenic_id": 1,
"report_type": 2,
"desc": "疑似人员在观景台附近主动揽客,并引导游客线下转账。",
"contact_phone": "13800138000",
"location_name": "黄山风景区",
"location_address": "光明顶景区",
"lat": 30.12345,
"lng": 120.12345,
"evidences": [
{
"file_url": "https://example.com/photog_report/20260708/1/report_image.jpg",
"file_type": 1,
"file_name": "report_image.jpg"
},
{
"file_url": "https://example.com/photog_report/20260708/1/report_video.mp4",
"file_type": 2,
"file_name": "report_video.mp4"
}
],
"payment_screenshots": [
{
"file_url": "https://example.com/photog_report/20260708/1/payment.jpg",
"file_type": 1,
"file_name": "payment.jpg"
}
]
}
```
### 字段说明
- `scenic_id`:当前景区 ID来自 `AppStore.shared.currentScenicId`,必须大于 0。
- `report_type`:举报类型接口返回的 `value`
- `desc`:举报说明,必填,最多 500 字。
- `store_user_id`:被举报摄影师 ID当前页面无来源本轮暂不传。
- `contact_phone`:当前联系方式输入值,非空才传。
- `location_name` / `location_address`:从当前定位展示文案拆分;无法拆分时使用景区名和完整地址兜底。
- `lat` / `lng`:当前定位快照坐标;定位失败时使用 Mock 地址对应的兜底坐标。
- `evidences`:现场证据,必填,至少 1 项、最多 9 项。
- `payment_screenshots`:线下微信、支付宝截图,选填,最多 3 张。
- `file_type`:举报提交接口约定为 `1 = 图片``2 = 视频`
### iOS 模型
```swift
struct WildReportSubmitRequest: Encodable, Equatable {
let scenicId: Int
let reportType: Int
let desc: String
let storeUserId: Int?
let contactPhone: String?
let locationName: String?
let locationAddress: String?
let lat: Double
let lng: Double
let evidences: [WildReportEvidenceRequest]
let paymentScreenshots: [WildReportEvidenceRequest]
}
struct WildReportEvidenceRequest: Encodable, Equatable {
let fileURL: String
let fileType: Int
let fileName: String?
}
```
### UI 与上传行为
- 图片、视频和支付截图使用系统相册选择器。
- 图片/视频选择后先保存到本地临时目录,点击附件 tile 可预览本地文件。
- 提交时顺序上传附件到 OSS按钮展示上传进度并禁用重复提交。
- 任一 OSS 上传失败时不调用提交接口,并保留用户已填写内容。
- 支付截图通过 `payment_screenshots` 独立提交,不替代现场证据必填校验。
### 当前调用位置
- `WildPhotographerReportSubmitViewController.submitTapped()`
- `WildPhotographerReportSubmitViewModel.submitReport(api:uploader:scenicId:scenicName:)`
- `OSSUploadService.uploadWildReportAttachment(...)`
- `WildPhotographerReportAPI.submitReport(_:)`
## 3. 我的举报列表
### 用途
“我的举报”页面进入、切换状态筛选、滚动加载更多时调用。
### 请求
```http
GET /api/yf-handset-app/photog/report/list
```
### 请求参数
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `scenic_id` | integer | 可选,当前景区 ID大于 0 时传 |
| `handle_status` | integer | 可选,状态筛选;“全部”不传该参数,其他筛选 iOS 固定使用该参数 |
| `status` | integer | 可选,与 `handle_status` 等价iOS 不传 |
| `page` | integer | 可选,默认 1 |
| `page_size` | integer | 可选,默认 10最大 50 |
### 状态码
- 全部:不传 `handle_status`
- `0`:待处理
- `1`:处理中
- `2`:已处理
### 响应示例
```json
{
"code": 100000,
"msg": "success",
"data": {
"list": [
{
"id": 32,
"complaint_no": "JB20260708917",
"title": "私下收款举报",
"report_type": 1,
"report_type_text": "私下收款",
"desc": "我在",
"scenic_id": 128,
"scenic_name": "伊犁那拉提景区-5A",
"location_name": "伊犁那拉提景区-5A",
"latitude": "32.4299428",
"longitude": "119.4403263",
"handle_status": 0,
"handle_status_text": "待处理",
"created_at": "2026-07-08 16:06:59"
}
],
"total": 1,
"page": 1,
"page_size": 10
},
"time": "2026-07-08 16:07:30"
}
```
### iOS 模型
```swift
struct WildReportListRequest: Equatable {
let scenicId: Int?
let handleStatus: Int?
let page: Int
let pageSize: Int
}
struct WildReportListResponse: Decodable, Equatable {
let list: [WildReportListItem]
let total: Int
let page: Int
let pageSize: Int
}
```
### UI 行为
- 页面首次进入请求 `page=1&page_size=10`,不传 `handle_status`
- 切换筛选时重置为第一页,待处理传 `handle_status=0`,处理中传 `handle_status=1`,已处理传 `handle_status=2`
- 滚动到底部时,如果仍有更多数据,则请求下一页并追加。
- 首屏失败显示 toast 和空态;加载更多失败只 toast并保留已有列表。
- 列表接口暂不返回媒体数量,`WildReportRecord.imageCount/videoCount` 暂按 0 处理,后续详情接口补齐。
### 当前调用位置
- `WildPhotographerReportListViewController.viewDidLoad()`
- `WildPhotographerReportListViewModel.loadInitial(api:scenicId:)`
- `WildPhotographerReportListViewModel.loadMoreIfNeeded(api:scenicId:)`
- `WildPhotographerReportAPI.reportList(_:)`
## Mock 阶段业务
以下业务当前仍由 `WildPhotographerReportMockStore` 和 ViewModel 本地逻辑完成,暂未接真实接口:
- 举报详情
- 补充证据
- 附近风险地图
- 分享、媒体预览、地图导航演示提示
后续接入真实接口时,应优先在 `WildPhotographerReportServing` 中补充语义化方法,并保持 ViewModel 不依赖 UIKit 视图类型。