|
@@ -0,0 +1,221 @@
|
|
|
|
|
+# 摊位资质认证(034):App 端接入说明
|
|
|
|
|
+
|
|
|
|
|
+**日期**:2026-10-10
|
|
|
|
|
+**分支**:`test-202609v2`(后端已实现并过定向测试;测试库 `stall_qualification` 两张表已建)
|
|
|
|
|
+**一句话**:摊位(`userType=4` 账号)在 App 里提交认证资料、查看审核进度、驳回后重提;**审核通过前摊位不能营业和接新单**。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 1. 功能与角色
|
|
|
|
|
+
|
|
|
|
|
+- 认证归属**摊位**(`storeId`),不归属登录账号。一个摊位可以有多个管理账号,共用同一套认证资料;换账号、停用账号都不影响已通过的资质。
|
|
|
|
|
+- App 端只做**摊主侧**:填表、传图、存草稿、提交、看进度、看驳回原因、重提。
|
|
|
|
|
+- 平台审核(通过/驳回)在平台管理 PC 端操作,App 不涉及。夜市主看进度是 PC 端功能(接口见第 8 节,App 如需也可用)。
|
|
|
|
|
+- 所有摊位同一张表单、同一套必填规则,**没有**个人经营/公司经营的分类分支。
|
|
|
|
|
+
|
|
|
|
|
+## 2. 通用约定
|
|
|
|
|
+
|
|
|
|
|
+| 项 | 说明 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 鉴权 | 请求头 `token: <登录令牌>`,账号必须是有效的 `userType=4`(摊主)且已绑定 `isStall=1` 的摊位;否则返回 `merchant.store.access.denied` |
|
|
|
|
|
+| 响应包裹 | `{ "code": 200, "msg": "...", "data": ... }`;业务失败 `code=500`,`msg` 为服务端按语言下发的本地化文案(zh/tw/en/vi/th),**App 直接展示 msg 即可,不要自己映射错误文案** |
|
|
|
|
|
+| 图片 BASE | 新上传图片是公开路径,直接 `BASE_URL + 路径` 展示,无需登录 |
|
|
|
|
|
+
|
|
|
|
|
+## 3. 状态模型
|
|
|
|
|
+
|
|
|
|
|
+`application.status`:
|
|
|
|
|
+
|
|
|
|
|
+| 值 | 含义 | App 表现 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `0` | 草稿 | 可继续编辑、可提交 |
|
|
|
|
|
+| `1` | 待审核 | **只读**,不能改不能重复提交(服务端拒绝 `stall.qualification.pending`) |
|
|
|
|
|
+| `2` | 通过 | 当前有效资质;重新提交期间保持有效,新申请通过才切换 |
|
|
|
|
|
+| `3` | 驳回 | 展示 `rejectReason`,按新版本重新填写提交 |
|
|
|
|
|
+
|
|
|
|
|
+`GET /stall/qualification` 里 `currentApplication` 为 `null` 表示从未创建过申请(此时 `version=0`)。
|
|
|
|
|
+
|
|
|
|
|
+**版本并发**:`view.version` 是乐观锁版本号。每次保存/提交/审核都会 +1。保存请求必须带**当前** `version`;不匹配返回 `stall.qualification.conflict`(提示用户刷新重新加载,**不要自动重试覆盖**——可能是另一个管理账号刚改过)。提交成功后从响应里取新的 `version` 存本地。
|
|
|
|
|
+
|
|
|
|
|
+## 4. 接口
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 查询资质视图
|
|
|
|
|
+
|
|
|
|
|
+```http
|
|
|
|
|
+GET /stall/qualification
|
|
|
|
|
+token: <摊主登录令牌>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "code": 200,
|
|
|
|
|
+ "data": {
|
|
|
|
|
+ "storeId": 194,
|
|
|
|
|
+ "stallName": "阿美蚵仔煎",
|
|
|
|
|
+ "stallAddress": "台北市士林夜市 108 摊",
|
|
|
|
|
+ "nightMarketId": 12,
|
|
|
|
|
+ "version": 3,
|
|
|
|
|
+ "currentApplication": { "...": "完整申请对象,见 4.4" },
|
|
|
|
|
+ "approvedApplication": { "...": "当前有效版本,可能为 null" },
|
|
|
|
|
+ "history": [
|
|
|
|
|
+ {
|
|
|
|
|
+ "id": 88,
|
|
|
|
|
+ "storeId": 194,
|
|
|
|
|
+ "applicationVersion": 2,
|
|
|
|
|
+ "status": 3,
|
|
|
|
|
+ "stallNameSnapshot": "阿美蚵仔煎",
|
|
|
|
|
+ "stallAddressSnapshot": "台北市士林夜市 108 摊",
|
|
|
|
|
+ "nightMarketIdSnapshot": 12,
|
|
|
|
|
+ "submittedAt": "2026-10-10T09:00:00+08:00",
|
|
|
|
|
+ "reviewedAt": "2026-10-10T11:00:00+08:00",
|
|
|
|
|
+ "rejectReason": "证件照片不清晰"
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- 读操作不会创建草稿;从未提交过时 `version=0`、两个 application 都是 `null`、`history=[]`。
|
|
|
|
|
+- `history` 只有摘要(无姓名/证件号/电话/附件),按 `applicationVersion` 倒序。
|
|
|
|
|
+- 摊位名称、地址、所属夜市由服务端从 `pos_store` 固化快照,**App 不提供修改入口**。
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 保存草稿 / 提交审核
|
|
|
|
|
+
|
|
|
|
|
+```http
|
|
|
|
|
+POST /stall/qualification/draft # 存草稿,允许不完整
|
|
|
|
|
+POST /stall/qualification/submit # 提交审核,四组资料必填
|
|
|
|
|
+Content-Type: application/json
|
|
|
|
|
+token: <摊主登录令牌>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+请求体(两个接口相同):
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "version": 3,
|
|
|
|
|
+ "responsibleName": "王大明",
|
|
|
|
|
+ "contactPhone": "0912345678",
|
|
|
|
|
+ "identityNumber": "A123456789",
|
|
|
|
|
+ "identityImageUrls": ["/profile/upload/2026/10/10/xxx.jpg"],
|
|
|
|
|
+ "stallImageUrls": ["/profile/upload/2026/10/10/yyy.png"],
|
|
|
|
|
+ "foodRegistrationNumber": "F-200000000-00000",
|
|
|
|
|
+ "foodRegistrationImageUrls": ["/profile/upload/2026/10/10/zzz.jpg"]
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 字段 | 必填(提交时) | 限制 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `version` | 是(草稿也要) | 必须等于当前 `view.version`,否则冲突错误 |
|
|
|
|
|
+| `responsibleName` | 是 | ≤100 字符 |
|
|
|
|
|
+| `contactPhone` | 是 | ≤32 字符;与账号登录手机号独立,可以不同 |
|
|
|
|
|
+| `identityNumber` | 是 | ≤64 字符(身份证/护照号) |
|
|
|
|
|
+| `identityImageUrls` | 是 | 1~5 张 |
|
|
|
|
|
+| `stallImageUrls` | 是 | 1~5 张 |
|
|
|
|
|
+| `foodRegistrationNumber` | 是 | ≤64 字符 |
|
|
|
|
|
+| `foodRegistrationImageUrls` | 是 | 1~5 张 |
|
|
|
|
|
+
|
|
|
|
|
+规则:
|
|
|
|
|
+
|
|
|
|
|
+- 文本两端 trim,trim 后为空视为缺失;超长返回 `stall.qualification.input.invalid`。
|
|
|
|
|
+- 草稿也校验已填图片引用的合法性(数量上限、文件存在),只是允许字段不齐。
|
|
|
|
|
+- 不接收 `storeId`、状态、审核人等字段——归属由 token 决定。
|
|
|
|
|
+- 草稿重复保存是**覆盖同一条草稿**(`applicationVersion` 不变);从已提交/已审核状态再保存(提交后想改、驳回重提)会创建**新版本申请**,旧记录不可变。
|
|
|
|
|
+- 响应为保存后的完整 `QualificationView`(同 4.1 结构,`currentApplication` 即刚保存的申请),**从响应更新本地 `version`**。
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 图片上传(商品同款通用上传)
|
|
|
|
|
+
|
|
|
|
|
+```http
|
|
|
|
|
+POST /utils/Upload
|
|
|
|
|
+Content-Type: multipart/form-data
|
|
|
|
|
+(字段名 file;接口匿名,可不带 token)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+响应:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{ "code": 200, "msg": "上传成功", "data": "/profile/upload/2026/10/10/20261010xxxx.jpg" }
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- `data` 就是可保存进 `xxxImageUrls` 的路径,直接使用。
|
|
|
|
|
+- 后端会校验真实图片格式并规范文件名特殊字符,App 无需预处理文件名。
|
|
|
|
|
+
|
|
|
|
|
+**展示规则(重要)**:
|
|
|
|
|
+
|
|
|
|
|
+| 引用形态 | 判别 | 展示方式 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| 新公共路径 | 以 `/profile/upload/` 开头 | `BASE_URL + 引用`,公开访问,无需登录 |
|
|
|
|
|
+| 旧私有引用 | `"<storeId>/<uuid>.jpg|.png"` 形态 | 走 4.4 鉴权接口取 blob 再展示 |
|
|
|
|
|
+
|
|
|
|
|
+### 4.4 历史申请详情 / 旧私有附件
|
|
|
|
|
+
|
|
|
|
|
+```http
|
|
|
|
|
+GET /stall/qualification/applications/{id}
|
|
|
|
|
+token: <摊主登录令牌>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+返回完整 `StallQualificationApplication`(含材料、提交/审核时间、驳回原因)。只能看**本摊位**的,看别人的返回 `merchant.store.access.denied`。用于点开 `history` 记录查看当时提交的材料。
|
|
|
|
|
+
|
|
|
|
|
+```http
|
|
|
|
|
+GET /stall/qualification/files/{storeId}/{fileName}
|
|
|
|
|
+token: <摊主登录令牌>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+旧私有附件读取(`stall-qualification` 目录的历史文件),返回图片二进制(`Cache-Control: no-store`),App 需带 token 请求并转 blob 展示;只能读本人摊位的文件。**新上传不要用 `/stall/qualification/files`(POST)**,那是旧版兼容接口。
|
|
|
|
|
+
|
|
|
|
|
+## 5. 营业门禁(App 需要处理的联动)
|
|
|
|
|
+
|
|
|
|
|
+`isStall=1` 且无通过记录时(含从未提交):
|
|
|
|
|
+
|
|
|
|
|
+| 场景 | 行为 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 摊主 App 创建订单(代客/堂食/外送) | 服务端拒绝,`msg` = 摊位材料尚未审核通过,暂不能营业或建立新订单 |
|
|
|
|
|
+| 用户 App 向该摊位下单 | 同上被拒 |
|
|
|
|
|
+| 门店状态 | 对外按闭店(`state=1`)展示 |
|
|
|
|
|
+| 已有订单 | 出餐、配送、支付后续**不受影响** |
|
|
|
|
|
+
|
|
|
|
|
+**建议**:摊主端遇到 `stall.qualification.approval.required` 文案(或进店页发现未认证)时,引导跳转认证页;认证页根据 4.1 的状态渲染对应操作。
|
|
|
|
|
+
|
|
|
|
|
+## 6. 错误文案(服务端已本地化,直接展示 msg)
|
|
|
|
|
+
|
|
|
|
|
+| 场景 | zh-TW 文案示例 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 版本冲突/已被他人操作 | 資料已變更或已審核,請重新整理後重試 |
|
|
|
|
|
+| 缺 version | 請重新整理後再儲存,缺少資料版本 |
|
|
|
|
|
+| 待审核中重复提交/修改 | 資料正在審核,暫不可修改或重複提交 |
|
|
|
|
|
+| 提交时材料不齐 | 請補齊負責人姓名電話、證件、攤位照片及食品業者登錄資料… |
|
|
|
|
|
+| 参数/超长/图片超5张 | 審核資料參數不正確 |
|
|
|
|
|
+| 图片无效/不存在/非本摊位 | 審核圖片無效、超過限制或不屬於目前攤位 |
|
|
|
|
|
+| 上传处理失败 | 審核材料檔案處理失敗,請稍後重試 |
|
|
|
|
|
+| 营业门禁 | 攤位材料尚未審核通過,暫不能營業或建立新訂單 |
|
|
|
|
|
+| 非本摊位/账号无效 | (merchant.store.access.denied 对应文案) |
|
|
|
|
|
+
|
|
|
|
|
+## 7. 建议页面流程
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+进入认证页
|
|
|
|
|
+ └─ GET /stall/qualification
|
|
|
|
|
+ ├─ currentApplication = null → 空表单(version=0),填完可存草稿/提交
|
|
|
|
|
+ ├─ status = 0 (草稿) → 回填表单,继续编辑或提交
|
|
|
|
|
+ ├─ status = 1 (待审核) → 只读展示材料 + "审核中"
|
|
|
|
|
+ ├─ status = 3 (驳回) → 展示 rejectReason;重填提交走新版本
|
|
|
|
|
+ └─ status = 2 (通过) → 展示有效资料;approvedApplication 为当前有效版本
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- 图片选择后先 `POST /utils/Upload`,成功再把返回路径塞进对应数组;**上传中禁止点提交**。
|
|
|
|
|
+- 待审核状态下隐藏保存/提交按钮(服务端同样会拦)。
|
|
|
|
|
+- 提交成功后用响应刷新整个视图和本地 `version`。
|
|
|
|
|
+
|
|
|
|
|
+## 8. 夜市主进度(可选,type=3 账号)
|
|
|
|
|
+
|
|
|
|
|
+```http
|
|
|
|
|
+GET /stall/qualification/market
|
|
|
|
|
+token: <夜市主登录令牌>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+返回本人旗下摊位数组:`{ storeId, status(-1=未创建), approved, ... }`,**不含**姓名、证件号、电话和附件。App 若不做夜市主进度页可忽略。
|
|
|
|
|
+
|
|
|
|
|
+## 9. 注意事项
|
|
|
|
|
+
|
|
|
|
|
+- `userType` 对照:`0` 用户、`1` 商家、`2` 骑手、`3` 夜市主、`4` 摊主。本功能仅 `4`。
|
|
|
|
|
+- 平台审核接口(`/system/stallQualification*`)走平台管理端 Authorization 登录,与 App 无关,不要在 App 调用。
|
|
|
|
|
+- 提交后的材料**不可变**:想改只能等驳回后重提,或(已通过后)提交新版本申请;已通过资质在新申请审核期间保持有效。
|
|
|
|
|
+- 证件图片链接为公开访问(用户已确认);但姓名、证件号、电话不出现在任何列表/进度接口,App 同样不要在日志里打印这些字段。
|