# 商家门店管理账号 API 接口文档

> 更新时间：2026-08-31
> 适用范围：商家端门店分管账号管理、分管账号登录，以及平台端查看和强制启停分管账号。
> 本文档以当前后端实现为准。

## 1. 公共约定

### 1.1 账号类型

| `userType` | 含义 |
|---|---|
| `1` | 商家主账号 |
| `5` | 商家门店分管账号 |

只有 `userType = 1` 的商家主账号可以新增、编辑、重置密码和启停分管账号。分管账号不能调用账号管理接口。

### 1.2 请求格式

- 商家端接口使用 `Content-Type: application/json`。
- 除登录接口外，请求头必须携带商家端登录返回的 `token`。
- `token` 直接放在请求头中，不添加 `Bearer` 前缀。

```http
token: 登录接口返回的 token
Content-Type: application/json
```

平台端接口沿用若依后台现有认证方式。

### 1.3 通用响应

成功响应：

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {}
}
```

没有返回数据的成功响应不包含 `data`：

```json
{
  "code": 200,
  "msg": "操作成功"
}
```

业务失败响应：

```json
{
  "code": 500,
  "msg": "具体错误信息"
}
```

前端应以响应体中的 `code === 200` 判断业务是否成功，不要只依赖 HTTP 状态码。

### 1.4 分管账号返回对象

| 字段 | 类型 | 说明 |
|---|---|---|
| `userId` | `number` | 分管账号用户 ID |
| `name` | `string` | 姓名 |
| `phone` | `string` | 登录手机号，同时也是登录账号 |
| `merchantOwnerId` | `number` | 所属商家主账号用户 ID |
| `ownerEnabled` | `boolean` | 主账号控制状态，`true` 为启用 |
| `platformEnabled` | `boolean` | 平台控制状态，`true` 为启用 |
| `online` | `boolean` | 当前是否存在有效商家端登录会话 |
| `lastLoginAt` | `string \| null` | 最后登录时间，未登录过时可能为 `null` |
| `createdAt` | `string \| null` | 创建时间 |
| `stores` | `array` | 当前负责的门店列表 |
| `stores[].storeId` | `number` | 门店 ID |
| `stores[].storeName` | `string` | 门店名称 |

账号实际可用条件：

```text
ownerEnabled === true && platformEnabled === true
```

## 2. 商家端接口汇总

基础路径：`/merchant/subaccounts`

| 功能 | 方法 | 地址 | 调用方 |
|---|---|---|---|
| 查询分管账号列表 | `GET` | `/merchant/subaccounts` | 商家主账号 |
| 创建分管账号 | `POST` | `/merchant/subaccounts` | 商家主账号 |
| 编辑姓名和负责门店 | `PUT` | `/merchant/subaccounts/{subaccountUserId}` | 所属商家主账号 |
| 重置密码 | `PUT` | `/merchant/subaccounts/{subaccountUserId}/password` | 所属商家主账号 |
| 启用或停用账号 | `PUT` | `/merchant/subaccounts/{subaccountUserId}/status` | 所属商家主账号 |

## 3. 获取可分配门店

创建或编辑分管账号前，可调用现有“我的门店”接口取得门店选择数据。

### `GET /chanting/store/getmystorelist`

请求头：

```http
token: 商家主账号 token
```

请求参数：无。

成功响应示例：

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "id": 101,
      "posName": "台北一店"
    },
    {
      "id": 102,
      "posName": "台北二店"
    }
  ]
}
```

该接口实际会返回完整门店对象。账号管理页面至少使用以下两个字段：

| 门店字段 | 用途 |
|---|---|
| `id` | 提交到账号接口的 `storeIds` |
| `posName` | 门店名称展示 |

## 4. 查询分管账号列表

### `GET /merchant/subaccounts`

仅商家主账号可调用。接口不分页，返回当前主账号名下全部未删除的分管账号。

请求头：

```http
token: 商家主账号 token
```

请求参数：无。

成功响应示例：

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "userId": 501,
      "name": "张三",
      "phone": "+886900000001",
      "merchantOwnerId": 10001,
      "ownerEnabled": true,
      "platformEnabled": true,
      "online": true,
      "lastLoginAt": "2026-08-28T10:30:00",
      "createdAt": "2026-08-20T09:00:00",
      "stores": [
        {
          "storeId": 101,
          "storeName": "台北一店"
        },
        {
          "storeId": 102,
          "storeName": "台北二店"
        }
      ]
    }
  ]
}
```

## 5. 创建分管账号

### `POST /merchant/subaccounts`

仅商家主账号可调用。

请求头：

```http
token: 商家主账号 token
Content-Type: application/json
```

请求体：

```json
{
  "phone": "+886900000001",
  "name": "张三",
  "password": "前端 RSA 加密后的密码",
  "storeIds": [101, 102]
}
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `phone` | `string` | 是 | 登录手机号，同时作为登录账号；服务端会移除其中的空白字符；最长 32 个字符；全局不可重复 |
| `name` | `string` | 是 | 分管账号姓名，不能只包含空白字符 |
| `password` | `string` | 是 | 使用商家端现有登录密码相同的 RSA 加密方式提交 |
| `storeIds` | `number[]` | 是 | 至少一个门店 ID；所有门店必须属于当前主账号；重复 ID 会被去重 |

成功响应的 `data` 为完整的分管账号对象：

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "userId": 501,
    "name": "张三",
    "phone": "+886900000001",
    "merchantOwnerId": 10001,
    "ownerEnabled": true,
    "platformEnabled": true,
    "online": false,
    "lastLoginAt": null,
    "createdAt": "2026-08-31T11:00:00",
    "stores": [
      {
        "storeId": 101,
        "storeName": "台北一店"
      },
      {
        "storeId": 102,
        "storeName": "台北二店"
      }
    ]
  }
}
```

## 6. 编辑姓名和负责门店

### `PUT /merchant/subaccounts/{subaccountUserId}`

只能修改当前主账号所属的分管账号。

路径参数：

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `subaccountUserId` | `number` | 是 | 分管账号的 `userId` |

请求体：

```json
{
  "name": "张三",
  "storeIds": [102, 103]
}
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `name` | `string` | 是 | 修改后的姓名 |
| `storeIds` | `number[]` | 是 | 修改后的完整门店 ID 集合，至少一个；不是增量添加 |

成功响应的 `data` 为修改后的完整分管账号对象。

注意：当前接口不支持修改手机号。`storeIds` 会整体覆盖原门店授权，提交前必须传入希望保留的全部门店 ID。

## 7. 重置密码

### `PUT /merchant/subaccounts/{subaccountUserId}/password`

只能重置当前主账号所属分管账号的密码。

请求体：

```json
{
  "password": "前端 RSA 加密后的新密码"
}
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `password` | `string` | 是 | 使用商家端现有登录密码相同的 RSA 加密方式提交 |

成功响应：

```json
{
  "code": 200,
  "msg": "操作成功"
}
```

密码重置成功后，后端会立即撤销该分管账号已有的 App 和 PC 会话，账号需要使用新密码重新登录。

## 8. 启用或停用账号

### `PUT /merchant/subaccounts/{subaccountUserId}/status`

该接口只修改主账号控制状态，即返回对象中的 `ownerEnabled`。

请求体：

```json
{
  "enabled": false
}
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `enabled` | `boolean` | 是 | `true` 启用，`false` 停用 |

成功响应：

```json
{
  "code": 200,
  "msg": "操作成功"
}
```

- 停用后，后端会立即撤销该分管账号已有的 App 和 PC 会话。
- 主账号重新启用账号时，只能把 `ownerEnabled` 改为 `true`，不能覆盖平台的 `platformEnabled` 状态。
- 如果 `platformEnabled === false`，即使主账号已启用，该账号仍然不可登录和使用。

## 9. 分管账号登录

分管账号复用现有商家登录接口，不使用单独的登录地址。

### `POST /infouser/user/shanglodeing`

该接口不需要 `token`。

请求体示例：

```json
{
  "userName": "+886900000001",
  "password": "前端 RSA 加密后的密码",
  "cid": "推送客户端标识",
  "cidType": "ios",
  "deviceToken": "设备推送 token",
  "voIPToken": "iOS VoIP token"
}
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `userName` | `string` | 是 | 创建分管账号时提交的手机号 |
| `password` | `string` | 是 | RSA 加密后的密码 |
| `cid` | `string` | 否 | 当前商家端使用的推送客户端标识 |
| `cidType` | `string` | 否 | 设备类型，例如 `ios`、`android` |
| `deviceToken` | `string` | 否 | 普通设备推送 token |
| `voIPToken` | `string` | 否 | iOS VoIP 推送 token |

分管账号登录成功响应示例：

```json
{
  "code": 200,
  "msg": "登录成功",
  "token": "登录 token",
  "data": {
    "userId": 501,
    "userName": "+886900000001",
    "nickName": "张三",
    "userType": "5",
    "storeId": null,
    "merchantOwnerId": 10001,
    "status": "0",
    "subaccountStatus": "0",
    "lastLoginAt": "2026-08-31T11:30:00"
  }
}
```

前端可使用 `data.userType === "5"` 识别分管账号。分管账号只能访问其被授权的门店；门店权限由后端校验，前端不能通过修改门店 ID 扩大权限。

登录时以下任一情况都会失败：

- 分管账号被主账号停用；
- 分管账号被平台停用；
- 所属商家主账号不可用；
- 账号或密码错误。

## 10. 退出登录

### `POST /infouser/user/merchantLogout`

请求头：

```http
token: 当前商家端 token
```

请求体：无。

成功响应：

```json
{
  "code": 200,
  "msg": "操作成功"
}
```

前端应先调用退出接口，再清除本地 token。

## 11. 平台端接口

平台端只允许查看分管账号以及强制启用或停用。平台不能新增分管账号、重置密码或修改门店授权。

### 11.1 查询指定商家的分管账号

#### `GET /infouser/merchant-subaccounts?merchantUserId={id}`

权限标识：`infouser:user:list`

查询参数：

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `merchantUserId` | `number` | 是 | 商家主账号用户 ID，必须是 `userType = 1` 的有效账号 |

成功响应的 `data` 与商家端“查询分管账号列表”一致。

### 11.2 平台强制启用或停用

#### `PUT /infouser/merchant-subaccounts/{subaccountUserId}/platform-status`

权限标识：`infouser:user:edit`

请求体：

```json
{
  "enabled": false
}
```

该接口只修改返回对象中的 `platformEnabled`。平台停用账号后会立即撤销该账号已有的 App 和 PC 会话。

成功响应：

```json
{
  "code": 200,
  "msg": "操作成功"
}
```

## 12. 常见业务错误

错误消息会根据服务端当前语言返回对应的简体中文、繁体中文、英文或越南语。以下为简体中文示例：

| `msg` 示例 | 触发情况 |
|---|---|
| `仅商家主账号可执行此操作` | 分管账号或非商家主账号调用账号管理接口 |
| `商家主账号不存在` | 平台查询的 `merchantUserId` 不是有效商家主账号 |
| `请求数据不能为空` | 创建或编辑请求体为空 |
| `分管账号姓名不能为空` | `name` 为空或只有空白字符 |
| `分管账号手机号不能为空` | `phone` 为空 |
| `分管账号手机号格式无效` | 去除空白后的手机号长度超过 32 个字符 |
| `该手机号已被使用` | 手机号或同名登录账号已存在 |
| `分管账号密码不能为空` | `password` 为空或只有空白字符 |
| `请至少选择一个负责店铺` | `storeIds` 为空数组或未提交 |
| `无权访问该店铺` | `storeIds` 包含不属于当前主账号的门店 |
| `分管账号状态不能为空` | `enabled` 未提交或为 `null` |
| `分管账号不存在` | 目标 ID 不是有效分管账号 |
| `无权管理该分管账号` | 主账号尝试修改其他商家的分管账号 |
| `商家账号不可用` | 账号被平台停用 |
| `所属商家主账号不可用` | 分管账号所属主账号不可用 |

## 13. 前端调用顺序

```text
进入账号管理页
  -> GET /chanting/store/getmystorelist
  -> GET /merchant/subaccounts

创建账号
  -> POST /merchant/subaccounts
  -> 成功后使用响应 data 更新列表，或重新查询列表

编辑账号
  -> PUT /merchant/subaccounts/{userId}
  -> 成功后使用响应 data 更新列表

重置密码或启停账号
  -> 调用对应 PUT 接口
  -> 成功后重新查询列表，刷新 online 和状态字段
```
