|
|
@@ -1,77 +1,87 @@
|
|
|
# 第三方登录接口文档
|
|
|
|
|
|
-> 版本:V1.6
|
|
|
-> 更新日期:2026-07-10
|
|
|
-> 变更说明:`licenseApproved`(Boolean)改为 `status`(Integer:0-审核中 / 1-审核成功 / 2-审核失败),支持审核失败状态;调用第三方同步接口同步传 `status`
|
|
|
+> 版本:V2.1
|
|
|
+> 更新日期:2026-07-11
|
|
|
+> 变更说明:**登录接口恢复全部字段**(登录同时可一并传入用户资料和入驻信息,兼容旧版);信息同步接口(`/external/sync`)已存在记录时仅更新审核状态,其他字段不允许覆盖
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. 概述
|
|
|
|
|
|
-本接口用于第三方系统通过用户手机号换取登录 Token,实现免密登录。系统会自动查找或注册用户,返回访问令牌和刷新令牌。同时支持第三方系统传入用户详细信息(用户名、药店名称、地址、联系人等),由我方负责存储,第三方无需我方再次维护用户信息。
|
|
|
+本套接口用于第三方系统与药汇购之间的用户数据互通,包含**两个接口**:
|
|
|
+
|
|
|
+| # | 接口 | 路径 | 鉴权 | 职责 |
|
|
|
+|---|------|------|------|------|
|
|
|
+| **登录接口** | 按手机号签发 Token | `POST /api/auth/external/miniapp-token` | X-Api-Key | 查找/注册用户 + 更新资料 + 同步入驻信息,返回 Token |
|
|
|
+| **同步接口** | 信息同步(独立) | `POST /api/auth/external/sync` | X-Api-Key | 更新用户资料 + 同步入驻信息(需用户已存在) |
|
|
|
+
|
|
|
+> 💡 **两种使用方式**:
|
|
|
+> - **一步到位**:登录接口直接传入全部字段(手机号 + 用户信息 + 入驻信息),一次请求完成登录和信息同步
|
|
|
+> - **分步调用**:先调登录接口获取 Token,再调同步接口更新详细资料和入驻资质
|
|
|
|
|
|
**核心特性:**
|
|
|
- 按手机号自动查找或注册用户
|
|
|
-- 支持第三方传入用户名、药店信息等,自动保存到用户表
|
|
|
-- 支持第三方传入入驻信息(资质图片URL等),同步写入 `t_business_license`,免去额外调用入驻接口
|
|
|
-- 不递增 `tokenVersion`,不踢出 PC 端已有会话
|
|
|
+- **支持双渠道**:`loginSource=MINIAPP`(小程序,30天过期,不踢PC) / `loginSource=PC`(PC端,1小时过期,踢旧PC会话),不传默认 MINIAPP
|
|
|
+- 支持传入 `skipRefreshToken=true` 仅获取 accessToken,不生成/覆盖 refreshToken
|
|
|
- 新用户自动初始化 **PLUS 会员(普通会员)**
|
|
|
- 第三方已审核用户(`status=1`)自动赠送 **30 天 PRO 高级会员**
|
|
|
-- 支持传入小程序 `openId` / `unionId`,一次性保存后后续支付等场景直接从用户表读取,**无需重复授权**
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 2. 接口信息
|
|
|
-
|
|
|
-| 项目 | 说明 |
|
|
|
-|------|------|
|
|
|
-| **接口名称** | 外部系统换取小程序 Token |
|
|
|
-| **请求方式** | `POST` |
|
|
|
-| **接口路径** | `/api/auth/external/miniapp-token` |
|
|
|
-| **Content-Type** | `application/json` |
|
|
|
-| **字符编码** | `UTF-8` |
|
|
|
+- 支持传入小程序 `openId` / `unionId`,支付场景无需重复授权
|
|
|
+- **已存在用户的入驻信息仅允许更新审核状态,其他字段保留原值**(后续由运营端确认更新)
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 3. 鉴权方式
|
|
|
+## 2. 鉴权方式
|
|
|
|
|
|
-使用 **API Key** 鉴权,在请求头中传入:
|
|
|
+两个接口均使用 **API Key** 鉴权,在请求头中传入:
|
|
|
|
|
|
```
|
|
|
X-Api-Key: {我方提供的API Key}
|
|
|
```
|
|
|
|
|
|
-> **API Key 格式建议**:32 位随机十六进制字符串(大小写字母 + 数字)。
|
|
|
-> 参考示例:`a3f8c21e67b94d0e5f1a6c8d3b9e2071`
|
|
|
->
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
+| `X-Api-Key` | String | 是 | 接口鉴权密钥 |
|
|
|
+| `Content-Type` | String | 是 | 固定值 `application/json` |
|
|
|
+
|
|
|
> ⚠️ 请妥善保管 API Key,不要泄露到前端代码或客户端。请在后端服务中调用此接口。
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 4. 请求参数
|
|
|
+## 3. 登录接口(`miniapp-token`)
|
|
|
|
|
|
-### 4.1 请求头(Headers)
|
|
|
+### 3.1 接口信息
|
|
|
|
|
|
-| 参数名 | 类型 | 必填 | 说明 |
|
|
|
-|--------|------|------|------|
|
|
|
-| `X-Api-Key` | String | 是 | 接口鉴权密钥 |
|
|
|
-| `Content-Type` | String | 是 | 固定值 `application/json` |
|
|
|
+| 项目 | 说明 |
|
|
|
+|------|------|
|
|
|
+| **接口名称** | 外部系统换取登录 Token |
|
|
|
+| **请求方式** | `POST` |
|
|
|
+| **接口路径** | `/api/auth/external/miniapp-token` |
|
|
|
+| **Content-Type** | `application/json` |
|
|
|
+
|
|
|
+> 💡 登录接口支持**登录 + 信息同步一体化**:除了必传手机号外,可选择性传入用户资料和入驻信息字段,一步完成登录和信息同步。
|
|
|
|
|
|
-### 4.2 请求体(Body)
|
|
|
+### 3.2 请求参数
|
|
|
|
|
|
-#### 基础字段
|
|
|
+#### 必填字段
|
|
|
|
|
|
| 参数名 | 类型 | 必填 | 说明 |
|
|
|
|--------|------|------|------|
|
|
|
| `phone` | String | 是 | 用户手机号 |
|
|
|
+
|
|
|
+#### 登录控制字段
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
| `inviteCode` | String | 否 | 邀请码(仅新用户注册时生效) |
|
|
|
+| `loginSource` | String | 否 | 登录来源。`MINIAPP`(小程序,30天过期,不踢PC) / `PC`(PC端,1小时过期,踢旧PC会话)。**不传默认 `MINIAPP`** |
|
|
|
+| `skipRefreshToken` | Boolean | 否 | `true`=仅返回 accessToken(refreshToken 返回 null),适用于客户端已持有 refreshToken 仅需换 accessToken 的场景;**不传默认 `false`** |
|
|
|
|
|
|
-#### 用户信息字段(第三方传入,自动保存到用户表)
|
|
|
+#### 用户信息字段(全部可选,非空才写入/覆盖)
|
|
|
|
|
|
| 参数名 | 类型 | 必填 | 说明 | 对应我方字段 |
|
|
|
|--------|------|------|------|--------------|
|
|
|
-| `username` | String | 否 | 用户名(保存到 userName 字段;若未传 nickname,则自动作为 nickname 的兜底值) | `nickname` / `userName` |
|
|
|
-| `nickname` | String | 否 | 昵称(不传则使用 username,再不传则按手机号生成) | `nickname` |
|
|
|
+| `username` | String | 否 | 用户名(保存到 userName 字段;若未传 nickname,则自动作为 nickname 的兜底值) | `user_name` |
|
|
|
+| `nickname` | String | 否 | 昵称(不传则依次取 username、手机号后4位生成) | `nickname` |
|
|
|
| `pharmacyName` | String | 否 | 药店名称 | `pharmacy_name` |
|
|
|
| `province` | String | 否 | 省 | `province` |
|
|
|
| `city` | String | 否 | 市 | `city` |
|
|
|
@@ -80,44 +90,24 @@ X-Api-Key: {我方提供的API Key}
|
|
|
| `contactPerson` | String | 否 | 联系人 | `contact_person` |
|
|
|
| `contactPhone` | String | 否 | 联系电话 | `contact_phone` |
|
|
|
| `businessLicenseNo` | String | 否 | 营业执照号(统一社会信用代码) | `business_license_no` |
|
|
|
-| `openId` | String | 否 | 微信小程序 openId(第三方从小程序 wx.login 获取后传入,保存到 wechat_mini_open_id。传入后支付场景无需重复授权) | `wechat_mini_open_id` |
|
|
|
-| `unionId` | String | 否 | 微信开放平台 unionId(用于跨应用用户统一标识,保存到 wechat_union_id) | `wechat_union_id` |
|
|
|
+| `openId` | String | 否 | 微信小程序 openId(保存到 wechat_mini_open_id) | `wechat_mini_open_id` |
|
|
|
+| `unionId` | String | 否 | 微信开放平台 unionId(跨应用用户统一标识) | `wechat_union_id` |
|
|
|
|
|
|
-#### 入驻信息字段(对应 /api/business-license/upload,均为可选,传入后同步写入 t_business_license)
|
|
|
+#### 入驻信息字段(全部可选,传入后同步写入 t_business_license)
|
|
|
|
|
|
| 参数名 | 类型 | 必填 | 说明 | 对应我方字段 |
|
|
|
|--------|------|------|------|--------------|
|
|
|
-| `storeName` | String | 否 | 店铺名称(不传则使用 pharmacyName) | `store_name` |
|
|
|
-| `terminalType` | String | 否 | 终端类型:`1`-单店 / `2`-连锁 / `3`-诊所、社康等 | `terminal_type` |
|
|
|
+| `storeName` | String | 否 | 店铺名称(不传则使用 pharmacyName 兜底) | `store_name` |
|
|
|
+| `terminalType` | String | 否 | 终端类型:`SINGLE`-单店 / `CHAIN`-连锁 / `CLINIC`-诊所 / `COMMUNITY_HEALTH`-社康 | `terminal_type` |
|
|
|
| `businessLicenseUrl` | String | 否 | 营业执照图片URL | `license_image_url` |
|
|
|
| `drugLicenseUrl` | String | 否 | 药品经营许可证图片URL | `drug_license_url` |
|
|
|
| `medicalDeviceClass2Url` | String | 否 | 二类医疗器械备案图片URL | `medical_device_class2_url` |
|
|
|
| `medicalDeviceClass3Url` | String | 否 | 三类医疗器械备案图片URL | `medical_device_class3_url` |
|
|
|
-| `status` | Integer | 否 | **审核状态**:`0`-审核中 / `1`-审核成功 / `2`-审核失败(不传默认`0`)。`1` = 第三方已审核通过,无需我方再次审核,直接设为 APPROVED 并赠送 30 天高级会员 | — |
|
|
|
-
|
|
|
-> 💡 **审核状态说明**:
|
|
|
-> - 如果第三方系统已完成资质审核,传 `status: 1`,我方将直接标记为"审核通过"并赠送 30 天 PRO 高级会员
|
|
|
-> - 如果第三方审核失败,传 `status: 2`,我方将标记为"审核失败"
|
|
|
-> - 如果第三方尚未审核或不确定,传 `status: 0` 或不传(默认 `PENDING`),由我方运营审核
|
|
|
-> - 已审核通过的入驻记录不会因后续调用而降级
|
|
|
-> - 只要请求中包含任一入驻信息字段(storeName / terminalType / businessLicenseUrl / drugLicenseUrl / medicalDeviceClass2Url / medicalDeviceClass3Url),即自动写入 `t_business_license` 表
|
|
|
-> - `status=1`:审核状态设为 `APPROVED`(审核通过),首次通过赠送 30 天 PRO 高级会员
|
|
|
-> - `status=0` 或不传:审核状态设为 `PENDING`(待运营审核)
|
|
|
-> - `status=2`:审核状态设为 `REJECTED`(审核失败)
|
|
|
-> - 已存在入驻记录:已 APPROVED 状态不降级,PENDING/REJECTED 状态可随 `status` 更新(含互转、升级到 APPROVED)
|
|
|
-> - 省/市/区/地址/联系人/电话等字段会自动从用户信息字段中提取填入
|
|
|
->
|
|
|
-> 💡 **字段更新规则**:
|
|
|
-> - 新用户会写入所有传入字段
|
|
|
-> - 已存在用户仅更新非空字段(不会清空已有数据)
|
|
|
->
|
|
|
-> 💡 **nickname 优先级**:`nickname` > `username` > 手机号生成(如 `用户8000`)
|
|
|
->
|
|
|
-> 💡 **建议**:第三方每次调用时传入最新的用户信息,我方会自动同步更新,无需额外维护。
|
|
|
-
|
|
|
-### 4.3 请求示例
|
|
|
-
|
|
|
-#### 最简请求(仅必填字段)
|
|
|
+| `status` | Integer | 否 | **审核状态**:`0`-审核中 / `1`-审核成功 / `2`-审核失败(不传默认`0`) | — |
|
|
|
+
|
|
|
+### 3.3 请求示例
|
|
|
+
|
|
|
+#### 最简请求(默认小程序,30天过期)
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
@@ -125,12 +115,31 @@ X-Api-Key: {我方提供的API Key}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-#### 完整请求(含用户信息 + 入驻信息)
|
|
|
+#### PC端登录(1小时过期)
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"phone": "13800138000",
|
|
|
- "inviteCode": "ABC123",
|
|
|
+ "loginSource": "PC"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 仅刷新 accessToken(不生成新 refreshToken)
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "phone": "13800138000",
|
|
|
+ "loginSource": "MINIAPP",
|
|
|
+ "skipRefreshToken": true
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 一步到位(登录 + 用户信息 + 入驻信息)
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "phone": "13800138000",
|
|
|
+ "loginSource": "MINIAPP",
|
|
|
"username": "yaohuigou001",
|
|
|
"nickname": "药汇购001",
|
|
|
"pharmacyName": "北京同仁堂大药房",
|
|
|
@@ -144,7 +153,7 @@ X-Api-Key: {我方提供的API Key}
|
|
|
"openId": "oJx1x5xxxxxxxxxxxxxxxxx",
|
|
|
"unionId": "oABCD1234xxxxxxxxxxxxxx",
|
|
|
"storeName": "北京同仁堂大药房(朝阳分店)",
|
|
|
- "terminalType": "1",
|
|
|
+ "terminalType": "SINGLE",
|
|
|
"businessLicenseUrl": "https://oss.example.com/yyzz.jpg",
|
|
|
"drugLicenseUrl": "https://oss.example.com/ypjy.jpg",
|
|
|
"medicalDeviceClass2Url": "https://oss.example.com/elqx.jpg",
|
|
|
@@ -153,11 +162,9 @@ X-Api-Key: {我方提供的API Key}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
----
|
|
|
-
|
|
|
-## 5. 响应参数
|
|
|
+### 3.4 响应参数
|
|
|
|
|
|
-### 5.1 通用响应结构
|
|
|
+#### 通用响应结构
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
@@ -173,43 +180,43 @@ X-Api-Key: {我方提供的API Key}
|
|
|
| `message` | String | 提示信息 |
|
|
|
| `data` | Object | 登录响应数据 |
|
|
|
|
|
|
-### 5.2 data 字段
|
|
|
+#### data 字段
|
|
|
|
|
|
| 参数名 | 类型 | 说明 |
|
|
|
|--------|------|------|
|
|
|
| `userId` | String | 用户ID(字符串,避免JS大数精度丢失) |
|
|
|
| `accessToken` | String | 访问令牌 |
|
|
|
-| `refreshToken` | String | 刷新令牌 |
|
|
|
-| `expiresIn` | Long | accessToken有效期(秒),默认`7200` |
|
|
|
+| `refreshToken` | String | 刷新令牌(`skipRefreshToken=true` 时为 null) |
|
|
|
+| `expiresIn` | Long | accessToken有效期(秒):MINIAPP=2592000(30天),PC=3600(1小时) |
|
|
|
| `newUser` | Boolean | 是否为新注册用户 |
|
|
|
| `needBindPhone` | Boolean | 始终返回`false` |
|
|
|
| `role` | String | 固定值`"USER"` |
|
|
|
| `userInfo` | Object | 用户基本信息 |
|
|
|
|
|
|
-### 5.3 userInfo 字段说明
|
|
|
+#### userInfo 字段
|
|
|
|
|
|
| 参数名 | 类型 | 说明 |
|
|
|
|--------|------|------|
|
|
|
| `id` | String | 用户ID |
|
|
|
| `phone` | String | 手机号 |
|
|
|
-| `nickname` | String | 昵称(即第三方传入的 nickname 或 username) |
|
|
|
+| `nickname` | String | 昵称 |
|
|
|
| `userName` | String | 第三方传入的原始用户名 |
|
|
|
| `avatar` | String | 头像 URL |
|
|
|
| `wechatBound` | Boolean | 是否已绑定微信 |
|
|
|
-| `loginSource` | String | 登录来源,`"MINIAPP"` 表示小程序 |
|
|
|
+| `loginSource` | String | 登录来源,`"MINIAPP"` 或 `"PC"` |
|
|
|
| `pharmacyName` | String | 药店名称 |
|
|
|
| `province` | String | 省 |
|
|
|
| `city` | String | 市 |
|
|
|
| `district` | String | 区 |
|
|
|
| `membershipLevel` | String | 会员等级代码(PLUS / PRO / ULTRA) |
|
|
|
| `membershipLevelName` | String | 会员等级名称 |
|
|
|
-| `licenseStatus` | String | 营业执照审核状态:`NOT_SUBMITTED` / `PENDING` / `APPROVED` / `REJECTED` |
|
|
|
-| `inviterId` | String | 邀请人用户ID(未绑定邀请码则为 null) |
|
|
|
+| `licenseStatus` | String | 营业执照审核状态 |
|
|
|
+| `inviterId` | String | 邀请人用户ID |
|
|
|
| `inviterNickname` | String | 邀请人昵称 |
|
|
|
| `hasBoundInviteCode` | Boolean | 是否已绑定邀请码 |
|
|
|
| `createTime` | String | 注册时间 |
|
|
|
|
|
|
-### 5.4 响应示例
|
|
|
+#### 响应示例
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
@@ -219,15 +226,15 @@ X-Api-Key: {我方提供的API Key}
|
|
|
"userId": "1234567890123456789",
|
|
|
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
|
|
|
"refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
|
|
|
- "expiresIn": 7200,
|
|
|
+ "expiresIn": 2592000,
|
|
|
"newUser": false,
|
|
|
"needBindPhone": false,
|
|
|
"role": "USER",
|
|
|
"userInfo": {
|
|
|
"id": "1234567890123456789",
|
|
|
"phone": "138****8000",
|
|
|
- "nickname": "药汇购001",
|
|
|
- "userName": "yaohuigou001",
|
|
|
+ "nickname": "用户8000",
|
|
|
+ "userName": null,
|
|
|
"avatar": null,
|
|
|
"wechatBound": false,
|
|
|
"loginSource": "MINIAPP",
|
|
|
@@ -249,30 +256,124 @@ X-Api-Key: {我方提供的API Key}
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 6. 错误码
|
|
|
+## 4. 信息同步接口(`sync`)
|
|
|
+
|
|
|
+### 4.1 接口信息
|
|
|
+
|
|
|
+| 项目 | 说明 |
|
|
|
+|------|------|
|
|
|
+| **接口名称** | 外部系统信息同步 |
|
|
|
+| **请求方式** | `POST` |
|
|
|
+| **接口路径** | `/api/auth/external/sync` |
|
|
|
+| **Content-Type** | `application/json` |
|
|
|
+
|
|
|
+> ⚠️ 用户必须先通过登录接口创建后,才能调用本接口同步信息。如果手机号对应的用户不存在,将返回错误。
|
|
|
+
|
|
|
+### 4.2 请求参数
|
|
|
+
|
|
|
+#### 必填字段
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
+| `phone` | String | 是 | 用户手机号(用于定位用户) |
|
|
|
+
|
|
|
+#### 用户信息字段(全部可选,非空才覆盖)
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
+| `username` | String | 否 | 用户名 |
|
|
|
+| `nickname` | String | 否 | 昵称 |
|
|
|
+| `pharmacyName` | String | 否 | 药店名称 |
|
|
|
+| `province` | String | 否 | 省 |
|
|
|
+| `city` | String | 否 | 市 |
|
|
|
+| `district` | String | 否 | 区 |
|
|
|
+| `pharmacyAddress` | String | 否 | 药店详细地址 |
|
|
|
+| `contactPerson` | String | 否 | 联系人 |
|
|
|
+| `contactPhone` | String | 否 | 联系电话 |
|
|
|
+| `businessLicenseNo` | String | 否 | 营业执照号 |
|
|
|
+| `openId` | String | 否 | 微信小程序 openId |
|
|
|
+| `unionId` | String | 否 | 微信开放平台 unionId |
|
|
|
+
|
|
|
+#### 入驻信息字段(全部可选)
|
|
|
+
|
|
|
+> ⚠️ **重要**:入驻信息已存在记录时,**仅允许更新审核状态(status)**,其他字段(店名、地区、证照图片等)保留原值,不允许通过第三方接口覆盖。后续修改需通过运营端确认。
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
+| `storeName` | String | 否 | 店铺名称(不传则用 pharmacyName 兜底) |
|
|
|
+| `terminalType` | String | 否 | 终端类型 |
|
|
|
+| `businessLicenseUrl` | String | 否 | 营业执照图片URL |
|
|
|
+| `drugLicenseUrl` | String | 否 | 药品经营许可证图片URL |
|
|
|
+| `medicalDeviceClass2Url` | String | 否 | 二类医疗器械备案图片URL |
|
|
|
+| `medicalDeviceClass3Url` | String | 否 | 三类医疗器械备案图片URL |
|
|
|
+| `status` | Integer | 否 | **审核状态**:`0`-审核中 / `1`-审核成功 / `2`-审核失败(不传默认`0`) |
|
|
|
+
|
|
|
+### 4.3 审核状态说明
|
|
|
+
|
|
|
+- `status=1`:审核状态设为 `APPROVED`(审核通过),**首次通过赠送 30 天 PRO 高级会员**
|
|
|
+- `status=0` 或不传:审核状态设为 `PENDING`(待运营审核)
|
|
|
+- `status=2`:审核状态设为 `REJECTED`(审核失败)
|
|
|
+- 已 APPROVED 状态**不降级**(不会因后续调用覆盖)
|
|
|
+- PENDING/REJECTED 状态可随 `status` 更新(含互转、升级到 APPROVED)
|
|
|
+
|
|
|
+### 4.4 入驻信息更新规则
|
|
|
+
|
|
|
+| 场景 | 行为 |
|
|
|
+|------|------|
|
|
|
+| **首次同步**(无入驻记录) | 创建新记录,写入全部字段 |
|
|
|
+| **已存在记录** | **仅更新审核状态**,店名、证照图片等所有字段保留原值 |
|
|
|
+| 需修改入驻信息 | 通过运营端人工审核后更新 |
|
|
|
+
|
|
|
+### 4.5 请求示例
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "phone": "13800138000",
|
|
|
+ "username": "yaohuigou001",
|
|
|
+ "nickname": "药汇购新昵称",
|
|
|
+ "pharmacyName": "北京同仁堂大药房",
|
|
|
+ "province": "北京市",
|
|
|
+ "city": "北京市",
|
|
|
+ "district": "朝阳区",
|
|
|
+ "status": 1
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4.6 响应
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "message": "success",
|
|
|
+ "data": null
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 响应 `code=200` 表示同步成功,无 data 内容。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 错误码
|
|
|
|
|
|
| 错误码 | 说明 | 处理建议 |
|
|
|
|--------|------|----------|
|
|
|
| `200` | 成功 | — |
|
|
|
| `401` | API Key 无效或缺失 | 检查`X-Api-Key`请求头 |
|
|
|
| `400` | 参数校验失败 | 检查`phone`是否为空 |
|
|
|
+| `400` | 用户不存在(同步接口) | 先调用登录接口创建用户 |
|
|
|
| `500` | 服务器内部错误 | 重试,持续失败联系我方 |
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 7. Token 使用说明
|
|
|
-
|
|
|
-### 7.1 携带 Token
|
|
|
+## 6. Token 使用说明
|
|
|
|
|
|
-后续所有业务接口请求需在请求头中携带 accessToken:
|
|
|
+### 6.1 携带 Token
|
|
|
|
|
|
```
|
|
|
Authorization: Bearer {accessToken}
|
|
|
```
|
|
|
|
|
|
-### 7.2 Token 刷新
|
|
|
-
|
|
|
-accessToken 过期后,使用 refreshToken 换取新 Token:
|
|
|
+### 6.2 Token 刷新
|
|
|
|
|
|
```
|
|
|
POST /api/auth/refresh
|
|
|
@@ -283,49 +384,54 @@ Content-Type: application/json
|
|
|
|
|
|
> 刷新后旧 refreshToken 立即失效(Token 旋转机制)。
|
|
|
|
|
|
-### 7.3 心跳保活
|
|
|
-
|
|
|
-建议客户端定时调用心跳接口,保持登录状态:
|
|
|
+### 6.3 心跳保活
|
|
|
|
|
|
```
|
|
|
POST /api/auth/heartbeat
|
|
|
Authorization: Bearer {currentAccessToken}
|
|
|
```
|
|
|
|
|
|
-> 建议间隔 = accessToken有效期 × 80%,约5760秒。
|
|
|
+> **建议间隔**:PC 端约 48 分钟(3600 × 80%),小程序端约 24 天(2592000 × 80%)。
|
|
|
+
|
|
|
+> 💡 **小程序端建议**:已持有有效 refreshToken 时,调用登录接口传 `skipRefreshToken=true` 仅换新 accessToken,避免频繁生成 refreshToken。
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 8. 注意事项
|
|
|
+## 7. 注意事项
|
|
|
|
|
|
| # | 说明 |
|
|
|
|---|------|
|
|
|
-| 1 | PC端严格单设备,小程序与PC互不干扰,同一账号可同时登录 |
|
|
|
-| 2 | 手机号未注册时自动创建账号并初始化 **PLUS 会员(普通会员)** |
|
|
|
-| 3 | `inviteCode`仅新用户注册时生效 |
|
|
|
-| 4 | API Key请在后端调用,勿暴露到客户端 |
|
|
|
-| 5 | accessToken默认2小时,refreshToken默认7天 |
|
|
|
-| 6 | 每次刷新后旧refreshToken立即作废,请保存最新的 |
|
|
|
-| 7 | 用户信息字段(username、药店信息等)每次调用都会同步更新,建议传入最新数据 |
|
|
|
-| 8 | `username` 会保存到我方的 `nickname` 字段,不需要额外维护用户名表 |
|
|
|
-| 9 | 传入入驻信息字段后自动创建/更新入驻记录,无需再调用 `/api/business-license/upload` |
|
|
|
-| 10 | `status=1` 时入驻状态直接审核通过,首次通过赠送 **30 天 PRO 高级会员** |
|
|
|
-| 11 | 已审核通过的入驻记录(APPROVED)不会因后续调用而降级 |
|
|
|
-| 12 | 传入 `openId`/`unionId` 后,小程序支付无需再次 `wx.login` 授权,后端直接从 `t_user.wechat_mini_open_id` 读取 |
|
|
|
+| 1 | 登录接口支持一步完成登录+信息同步(传入全部字段即可) |
|
|
|
+| 2 | 同步接口要求用户已存在(先调用登录接口创建),否则返回错误 |
|
|
|
+| 3 | PC端单设备互踢(递增 tokenVersion),小程序与PC互不干扰 |
|
|
|
+| 4 | Token 过期时间:PC=1小时,小程序=30天 |
|
|
|
+| 5 | 手机号未注册时自动创建账号并初始化 **PLUS 会员** |
|
|
|
+| 6 | `inviteCode`仅新用户注册时生效 |
|
|
|
+| 7 | API Key请在后端调用,勿暴露到客户端 |
|
|
|
+| 8 | `skipRefreshToken=true` 时 refreshToken 返回 null |
|
|
|
+| 9 | 每次刷新后旧 refreshToken 立即作废,请保存最新的 |
|
|
|
+| 10 | 同步接口仅更新非空字段,不会清空已有数据 |
|
|
|
+| 11 | `status=1` 时入驻状态直接审核通过,**首次**通过赠送 30 天 PRO 高级会员 |
|
|
|
+| 12 | 已审核通过的入驻记录(APPROVED)不会因后续调用而降级 |
|
|
|
+| 13 | 传入 `openId`/`unionId` 后,小程序支付无需再次 `wx.login` 授权 |
|
|
|
+| 14 | **入驻信息已存在时仅更新审核状态**,其他字段保留原值,需通过运营端修改 |
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 9. 调用示例
|
|
|
+## 8. 调用示例
|
|
|
|
|
|
-### cURL
|
|
|
+### 完整流程(cURL)
|
|
|
|
|
|
```bash
|
|
|
+# 一步到位:登录 + 同步用户信息和入驻资质
|
|
|
curl -X POST "https://your-domain.com/api/auth/external/miniapp-token" \
|
|
|
-H "Content-Type: application/json" \
|
|
|
-H "X-Api-Key: your-api-key-here" \
|
|
|
-d '{
|
|
|
"phone": "13800138000",
|
|
|
+ "loginSource": "PC",
|
|
|
"username": "yaohuigou001",
|
|
|
+ "nickname": "药汇购001",
|
|
|
"pharmacyName": "北京同仁堂大药房",
|
|
|
"province": "北京市",
|
|
|
"city": "北京市",
|
|
|
@@ -334,147 +440,75 @@ curl -X POST "https://your-domain.com/api/auth/external/miniapp-token" \
|
|
|
"contactPerson": "张三",
|
|
|
"contactPhone": "13800138000",
|
|
|
"businessLicenseNo": "91110105MA12345678",
|
|
|
- "openId": "oJx1x5xxxxxxxxxxxxxxxxx",
|
|
|
- "unionId": "oABCD1234xxxxxxxxxxxxxx",
|
|
|
"storeName": "北京同仁堂大药房(朝阳分店)",
|
|
|
- "terminalType": "1",
|
|
|
+ "terminalType": "SINGLE",
|
|
|
"businessLicenseUrl": "https://oss.example.com/yyzz.jpg",
|
|
|
"drugLicenseUrl": "https://oss.example.com/ypjy.jpg",
|
|
|
"status": 1
|
|
|
}'
|
|
|
-```
|
|
|
-
|
|
|
-### Java (OkHttp)
|
|
|
-
|
|
|
-```java
|
|
|
-OkHttpClient client = new OkHttpClient();
|
|
|
-
|
|
|
-String json = "{"
|
|
|
- + "\"phone\": \"13800138000\","
|
|
|
- + "\"username\": \"yaohuigou001\","
|
|
|
- + "\"pharmacyName\": \"北京同仁堂大药房\","
|
|
|
- + "\"province\": \"北京市\","
|
|
|
- + "\"city\": \"北京市\","
|
|
|
- + "\"district\": \"朝阳区\","
|
|
|
- + "\"pharmacyAddress\": \"建国路88号\","
|
|
|
- + "\"contactPerson\": \"张三\","
|
|
|
- + "\"contactPhone\": \"13800138000\","
|
|
|
- + "\"businessLicenseNo\": \"91110105MA12345678\","
|
|
|
- + "\"openId\": \"oJx1x5xxxxxxxxxxxxxxxxx\","
|
|
|
- + "\"unionId\": \"oABCD1234xxxxxxxxxxxxxx\","
|
|
|
- + "\"storeName\": \"北京同仁堂大药房(朝阳分店)\","
|
|
|
- + "\"terminalType\": \"1\","
|
|
|
- + "\"businessLicenseUrl\": \"https://oss.example.com/yyzz.jpg\","
|
|
|
- + "\"drugLicenseUrl\": \"https://oss.example.com/ypjy.jpg\","
|
|
|
- + "\"status\": 1"
|
|
|
- + "}";
|
|
|
-
|
|
|
-RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
|
|
|
-Request request = new Request.Builder()
|
|
|
- .url("https://your-domain.com/api/auth/external/miniapp-token")
|
|
|
- .post(body)
|
|
|
- .addHeader("X-Api-Key", "your-api-key-here")
|
|
|
- .addHeader("Content-Type", "application/json")
|
|
|
- .build();
|
|
|
-
|
|
|
-try (Response response = client.newCall(request).execute()) {
|
|
|
- System.out.println(response.body().string());
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-### Python (requests)
|
|
|
|
|
|
-```python
|
|
|
-import requests
|
|
|
-
|
|
|
-url = "https://your-domain.com/api/auth/external/miniapp-token"
|
|
|
-headers = {
|
|
|
- "X-Api-Key": "your-api-key-here",
|
|
|
- "Content-Type": "application/json"
|
|
|
-}
|
|
|
-data = {
|
|
|
+# 后续独立更新信息(如仅变更用户昵称)
|
|
|
+curl -X POST "https://your-domain.com/api/auth/external/sync" \
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
+ -H "X-Api-Key: your-api-key-here" \
|
|
|
+ -d '{
|
|
|
"phone": "13800138000",
|
|
|
- "username": "yaohuigou001",
|
|
|
- "pharmacyName": "北京同仁堂大药房",
|
|
|
- "province": "北京市",
|
|
|
- "city": "北京市",
|
|
|
- "district": "朝阳区",
|
|
|
- "pharmacyAddress": "建国路88号",
|
|
|
- "contactPerson": "张三",
|
|
|
- "contactPhone": "13800138000",
|
|
|
- "businessLicenseNo": "91110105MA12345678",
|
|
|
- "openId": "oJx1x5xxxxxxxxxxxxxxxxx",
|
|
|
- "unionId": "oABCD1234xxxxxxxxxxxxxx",
|
|
|
- "storeName": "北京同仁堂大药房(朝阳分店)",
|
|
|
- "terminalType": "1",
|
|
|
- "businessLicenseUrl": "https://oss.example.com/yyzz.jpg",
|
|
|
- "drugLicenseUrl": "https://oss.example.com/ypjy.jpg",
|
|
|
- "status": 1
|
|
|
-}
|
|
|
-
|
|
|
-response = requests.post(url, json=data, headers=headers)
|
|
|
-print(response.json())
|
|
|
+ "nickname": "新昵称"
|
|
|
+ }'
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 10. 字段映射表
|
|
|
-
|
|
|
-第三方传入字段与我方数据库字段的对应关系:
|
|
|
-
|
|
|
-### 10.1 用户信息(写入 t_users)
|
|
|
-
|
|
|
-| 第三方传入字段 | 我方数据库字段 | 我方实体字段 | 说明 |
|
|
|
-|----------------|----------------|--------------|------|
|
|
|
-| `phone` | `phone` | `phone` | 手机号(必填) |
|
|
|
-| `username` | `nickname` | `nickname` | 用户名(同时写入 nickname 和 userName) |
|
|
|
-| `nickname` | `nickname` | `nickname` | 昵称(优先级低于 username) |
|
|
|
-| `pharmacyName` | `pharmacy_name` | `pharmacyName` | 药店名称(也作为 storeName 兜底) |
|
|
|
-| `province` | `province` | `province` | 省 |
|
|
|
-| `city` | `city` | `city` | 市 |
|
|
|
-| `district` | `district` | `district` | 区 |
|
|
|
-| `pharmacyAddress` | `pharmacy_address` | `pharmacyAddress` | 药店详细地址 |
|
|
|
-| `contactPerson` | `contact_person` | `contactPerson` | 联系人 |
|
|
|
-| `contactPhone` | `contact_phone` | `contactPhone` | 联系电话 |
|
|
|
-| `businessLicenseNo` | `business_license_no` | `businessLicenseNo` | 营业执照号 |
|
|
|
-| `openId` | `wechat_mini_open_id` | `wechatMiniOpenId` | 微信小程序 openId |
|
|
|
-| `unionId` | `wechat_union_id` | `wechatUnionId` | 微信开放平台 unionId |
|
|
|
-
|
|
|
-### 10.2 入驻信息(写入 t_business_license,全部可选)
|
|
|
-
|
|
|
-| 第三方传入字段 | 我方数据库字段 | 我方实体字段 | 说明 |
|
|
|
-|----------------|----------------|--------------|------|
|
|
|
-| `storeName` | `store_name` | `storeName` | 店铺名称(不传则用 pharmacyName) |
|
|
|
-| `terminalType` | `terminal_type` | `terminalType` | 终端类型 |
|
|
|
-| `businessLicenseUrl` | `license_image_url` | `licenseImageUrl` | 营业执照图片URL |
|
|
|
-| `drugLicenseUrl` | `drug_license_url` | `drugLicenseUrl` | 药品经营许可证图片URL |
|
|
|
-| `medicalDeviceClass2Url` | `medical_device_class2_url` | `medicalDeviceClass2Url` | 二类医疗器械备案图片URL |
|
|
|
-| `medicalDeviceClass3Url` | `medical_device_class3_url` | `medicalDeviceClass3Url` | 三类医疗器械备案图片URL |
|
|
|
-| `status` | `review_status` | `reviewStatus` | 审核状态:`0`→PENDING / `1`→APPROVED / `2`→REJECTED |
|
|
|
-
|
|
|
-> 💡 入驻记录保存时,以下字段由系统自动填入(无需第三方传入):
|
|
|
+## 9. 字段映射表
|
|
|
+
|
|
|
+### 9.1 用户信息(写入 t_users)
|
|
|
+
|
|
|
+| 第三方传入字段 | 我方数据库字段 | 说明 |
|
|
|
+|----------------|----------------|------|
|
|
|
+| `phone` | `phone` | 手机号(必填,用于定位用户) |
|
|
|
+| `username` | `user_name` | 用户名 |
|
|
|
+| `nickname` | `nickname` | 昵称 |
|
|
|
+| `pharmacyName` | `pharmacy_name` | 药店名称 |
|
|
|
+| `province` | `province` | 省 |
|
|
|
+| `city` | `city` | 市 |
|
|
|
+| `district` | `district` | 区 |
|
|
|
+| `pharmacyAddress` | `pharmacy_address` | 药店详细地址 |
|
|
|
+| `contactPerson` | `contact_person` | 联系人 |
|
|
|
+| `contactPhone` | `contact_phone` | 联系电话 |
|
|
|
+| `businessLicenseNo` | `business_license_no` | 营业执照号 |
|
|
|
+| `openId` | `wechat_mini_open_id` | 微信小程序 openId |
|
|
|
+| `unionId` | `wechat_union_id` | 微信开放平台 unionId |
|
|
|
+
|
|
|
+### 9.2 入驻信息(写入 t_business_license)
|
|
|
+
|
|
|
+| 第三方传入字段 | 我方数据库字段 | 说明 |
|
|
|
+|----------------|----------------|------|
|
|
|
+| `storeName` | `store_name` | 店铺名称(不传则用 pharmacyName) |
|
|
|
+| `terminalType` | `terminal_type` | 终端类型 |
|
|
|
+| `businessLicenseUrl` | `license_image_url` | 营业执照图片URL |
|
|
|
+| `drugLicenseUrl` | `drug_license_url` | 药品经营许可证图片URL |
|
|
|
+| `medicalDeviceClass2Url` | `medical_device_class2_url` | 二类医疗器械备案图片URL |
|
|
|
+| `medicalDeviceClass3Url` | `medical_device_class3_url` | 三类医疗器械备案图片URL |
|
|
|
+| `status` | `review_status` | 审核状态:`0`→PENDING / `1`→APPROVED / `2`→REJECTED |
|
|
|
+
|
|
|
+> 💡 入驻记录保存时,以下字段由系统自动填入:
|
|
|
> - `store_address` ← 从 `pharmacyAddress` 提取
|
|
|
> - `credit_code` ← 从 `businessLicenseNo` 提取
|
|
|
> - `sync_source` ← 固定为 `MINIAPP_SYNC`
|
|
|
-> - `review_status` ← 取决于 `status`:`1` → `APPROVED`,`2` → `REJECTED`,`0`/不传 → `PENDING`
|
|
|
> - `show_verified_badge` ← 首次审核通过时设为 `true`
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 11. 联系方式
|
|
|
+## 10. 联系方式
|
|
|
|
|
|
如有问题,请联系我方技术支持。
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 12. 变更记录
|
|
|
+## 11. 变更记录
|
|
|
|
|
|
| 版本 | 日期 | 变更内容 |
|
|
|
|------|------|----------|
|
|
|
-| V1.0 | 2026-07-07 | 初始版本:基础登录接口 |
|
|
|
-| V1.1 | 2026-07-07 | 扩展请求参数:支持传入用户名、药店信息等,自动保存到用户表 |
|
|
|
-| V1.2 | 2026-07-09 | 新增入驻信息字段(storeName / terminalType / businessLicenseUrl / drugLicenseUrl / medicalDeviceClass2Url / medicalDeviceClass3Url),传入后同步写入 t_business_license,无需额外调用入驻接口 |
|
|
|
-| V1.3 | 2026-07-09 | 新增 `licenseApproved` 参数支持第三方已审核入驻状态直通;审核通过自动赠送 30 天 PRO 高级会员;已 APPROVED 状态不降级;响应新增 `licenseStatus` 字段 |
|
|
|
-| V1.4 | 2026-07-09 | 响应 userInfo 补全完整返回字段(userName、pharmacyName、province、city、district、inviterId、inviterNickname、hasBoundInviteCode);修正 nickname 优先级描述(nickname > username) |
|
|
|
-| V1.5 | 2026-07-09 | 新增 `openId`、`unionId` 参数,第三方传入后保存到 `wechat_mini_open_id` / `wechat_union_id`;后续小程序支付等场景直接从用户表读取,无需重复 wx.login 授权
|
|
|
-| V1.6 | 2026-07-10 | `licenseApproved`(Boolean)改为 `status`(Integer:`0`-审核中 / `1`-审核成功 / `2`-审核失败),支持审核失败状态;已 APPROVED 不降级,PENDING/REJECTED 可随 `status` 互转或升级;调用第三方同步接口同步传 `status` |
|
|
|
+| V1.0 ~ V1.7 | 2026-07-07 ~ 2026-07-11 | 初始版本、字段扩展、状态字段、loginSource/skipRefreshToken |
|
|
|
+| V2.0 | 2026-07-11 | 拆分为登录接口 + 信息同步接口;登录接口仅接收登录字段 |
|
|
|
+| **V2.1** | 2026-07-11 | **登录接口恢复全部字段**(登录与信息同步可在同一请求完成);同步接口已存在入驻记录时仅允许更新审核状态 |
|