# 店铺管理 — 技术方案

> **依据：** 《店铺管理功能需求.md》v1.3.6  
> **关联：** 《商户管理》《商品分类》《商品管理》功能需求（`doc/农资商城web/`）、《商户管理技术方案.md》v1.4、《店铺设置技术方案.md》v1.1.1    
> **范围：** 本文以 **店铺模块** 的数据库、接口为主；商品/分类/订单/商家端登录给出 **表关系、Facade、联动**；不展开商品/C 端完整设计。  
> **原则：** **不考虑会员**；**店铺经营账号** 由 **商户入驻** 写入 `biz_merchant_account`，本模块 **添加店铺时仅校验已有账号**；账号改密走 **店铺账号管理** 接口。  
> **v1.4：** 对齐 [`sql/biz_shop.sql`](../../../sql/biz_shop.sql) 新结构：`account_id=sys_user.user_id`；登录凭证在 `sys_user`；`shop_id=NULL` 为商户管理员。
> **v1.3：** 对齐当前实现与 [`sql/biz_shop.sql`](../../../sql/biz_shop.sql)；经营账号绑定规则与商户模块 **role_key=merchant/member** 一致。  
> **v1.2.5：** `biz_shop.shop_status`、`shop_phone` 选填；`POST /` **不再** 接收 `loginName`/`adminName`/`password`（与需求 v1.3.5 对齐）。  
> **v1.2.6：** 商家端 **`GET /agri/seller/shop/employeeStats`** 员工概览；GET 资料 VO 扩展商户联系人只读字段；详册见 **商家端**《店铺设置技术方案》v1.1。  
> **v1.2.3：** 商家端 **`GET/PUT /agri/seller/shop`** 维护店资料，与平台列表 **同源** `biz_shop`。  
> **v1.2.4：** 同步《商品分类技术方案》v1.2 — `biz_goods_category.shop_id` 可空（平台分类与店铺分类同表隔离）。

---

## 1. 技术架构

| 项 | 选型 |
|----|------|
| 基础框架 | RuoYi **v3.9.2**（`springboot2` 分支） |
| 数据库 | **MySQL 5.7.39** |
| ORM / 权限 / 响应 | MyBatis；`@PreAuthorize`；`AjaxResult` / `TableDataInfo` |
| 文件 | 店铺头像走 `CommonController` 上传，存 URL |
| 密码 | `SecurityUtils.encryptPassword`（BCrypt，与 `sys_user` 一致） |
| 日志 | 增删改、账号/设置变更 `@Log` |

### 1.1 模块落位

```text
baqing-shop/src/main/java/com/ruoyi/web/modules/store/
├── controller/ShopController.java              # 平台 /agri/shop
├── controller/SellerShopController.java          # 商家 /agri/seller/shop（X-Shop-Id）
├── service/impl/ShopServiceImpl.java
├── service/impl/ShopGlobalConfigServiceImpl.java # 全局策略见 shopSetting 模块
├── dto/ShopCreateDTO、ShopUpdateDTO、MerchantAccountUpdateDTO、SellerShopProfileDTO
├── mapper/BizShopMapper.xml、BizMerchantAccountMapper.xml
├── facade/ShopFacadeImpl、MerchantShopFacadeImpl、GoodsShopFacadeImpl …
└── constant/ShopConstants.java

resources/mapper/store/BizShopMapper.xml          # 列表 JOIN merchant + account
sql/biz_shop.sql                                  # biz_merchant_account + biz_shop DDL
```

### 1.2 业务链与数据归属

```text
biz_merchant
    └── biz_merchant_account（1 商户 1 套经营账号）
            └── biz_shop（1 商户多店，merchant_id 创建后不可改；本期不设店数上限）
                    ├── biz_goods_category（shop_id = 本店 ID；店铺商品分类二级树）
                    ├── biz_goods（shop_id；category_id 指向 **平台** 二级类；shop_category_id 可选指向本店二级类；submit 读 **全局** default_audit_pass）
                    └── biz_order（shop_id；删店校验）

biz_goods_category（shop_id IS NULL）  ← 平台分类（商品 category_id 引用）
```

| 模块 | 核心表 | 与店铺关系 |
|------|--------|------------|
| 商户管理 | `biz_merchant` | 上游；`shop_count` 回写 |
| **店铺管理** | `biz_shop`；`biz_merchant_account`（表归商户维度，**本模块维护**） | **本文** |
| 商品管理 | `biz_goods` | 删店前置：无出售中/待审核 |
| 商品分类 | `biz_goods_category` | **店铺行** `shop_id = 本店`；平台行 `shop_id IS NULL`（商品模块不引用）；删店后本店分类 **不可再维护** |
| 订单 | `biz_order` | 删店校验 `IOrderFacade.hasUnfinishedOrdersByShop`；DDL [`sql/biz_order.sql`](../../../sql/biz_order.sql) |
| 商家端 | `biz_shop` 资料 | `SellerShopController`；`X-Shop-Id` 上下文；改资料后平台 list **同源** |

### 1.3 跨模块 Facade

| 接口 | 提供方 | 调用方 | 方法 |
|------|--------|--------|------|
| `IMerchantFacade` | 商户 | 店铺 | `checkOpenShop(merchantId)`、`listSelectableMerchants()` |
| `IMerchantShopFacade` | **店铺** | 商户 | `incrementShopCount` / `decrementShopCount`（**与店铺同事务**） |
| `IGoodsFacade` | 商品 | 店铺 | `hasBlockingGoodsForShopDelete(shopId)` — 存在 **待审核(1)** 或 **出售中(2)** |
| `IOrderFacade` | 订单（**目标包** `com.ruoyi.web.modules.order`，**待实现**） | 店铺、商户 | `hasUnfinishedOrdersByShop(shopId)` |
| `IShopFacade` | 店铺 | 商品/C端 | `getShopStatus(shopId)`、`isShopOpenForOrder(shopId)` — 开业且未逻辑删除 |

> 订单未实现前，由 **`store.facade.DefaultShopOrderFacade`** 桩返回 `false`（不阻塞删除）；实现后迁入 `order.facade.IOrderFacade`。

### 1.4 状态联动

| 事件 | 店铺 | 商品 | 商户 | C 端 |
|------|------|------|------|------|
| 新建店 | 默认 **开业(0)** | — | shop_count+1 | — |
| 开业→停业 | 二次确认后保存 | **不改** 商品状态 | 不改认证 | **禁止下单** |
| 停业→开业 | 直接保存 | — | — | 可下单（还须商品等条件） |
| 商户冻结/注销 | 不可 **新开店**；已有店不自动停业 | 不改状态 | — | — |
| 逻辑删店 | 列表隐藏 | 历史保留 | shop_count-1 | 店不可用 |
| 改全局 `default_audit_pass` | —（见《店铺设置》） | **仅此后** 新 submit 生效 | — | — |

---

## 2. 数据库设计

### 2.1 本模块表

| 表名 | 说明 |
|------|------|
| `biz_shop` | 店铺主数据（名称、头像、状态、描述、商家电话） |
| `biz_merchant_account` | 商户级经营账号（**同商户多店共用**；原命名 `biz_shop_account` 已统一为本表名） |

### 2.2 `biz_merchant_account`（关联表，登录凭证在 sys_user）

| 字段 | 类型 | 说明 |
|------|------|------|
| account_id | bigint PK | **等于 `sys_user.user_id`**，非自增 |
| merchant_id | bigint | 所属商户；商户管理员行 **一商户一条**（`shop_id IS NULL` 且 `del_flag=0`） |
| shop_id | bigint | **NULL=商户管理员**；非 NULL=店铺员工账号（预留） |
| del_flag | char(1) | 0 存在 2 删除 |
| create_by/time, update_by/time | | |

> 第 2、3 家店 **不新建** 账号行；添加店铺 **不** 再传账号字段，仅校验商户 **已有** 商户管理员绑定。  
> **创建时机：** 商户入驻（平台新增商户 / 入驻审核通过）时 `MerchantAccountBindServiceImpl.bindAccountOnMerchantCreate` 写入一行 `(account_id, merchant_id, shop_id=NULL)`。  
> **展示/登录名：** `sys_user.user_name`（登录名）、`sys_user.nick_name`（管理员姓名）；绑管理员或绑会员均 **不复制** 至本表。  
> **末店逻辑删除后：** 账号行 **保留** `del_flag=0`，同商户再开店可沿用/重置（需求 §11.5）。

### 2.3 `biz_shop`

| 字段 | 类型 | 说明 |
|------|------|------|
| shop_id | bigint PK | |
| merchant_id | bigint | FK；**创建后不可 UPDATE** |
| shop_name | varchar(128) | 未删除店铺内 **唯一** |
| shop_avatar | varchar(512) | 头像 URL |
| shop_status | char(1) | **0 开业 1 停业**；**选填**，未传默认 0 |
| shop_desc | varchar(1000) | 选填 |
| shop_phone | varchar(20) | 商家电话，**选填**；有值时校验格式 |
| del_flag | char(1) | 逻辑删除 |
| 审计字段 | | |

列表「经营账号」：`JOIN biz_merchant_account ON merchant_id AND shop_id IS NULL` → `JOIN sys_user ON account_id`，展示 `nick_name` + 脱敏 `user_name`。

### 2.4 索引

| 索引 | 字段 |
|------|------|
| PK | shop_id |
| idx_merchant | merchant_id, del_flag |
| idx_status | shop_status, del_flag |
| idx_create | create_time |
| uk_shop_name | shop_name + del_flag（或应用层唯一） |
| uk_merchant_admin | merchant_id + shop_id IS NULL + del_flag（account 表，应用层保证） |
| uk_account_id | account_id + del_flag（account 表，一用户一条有效绑定） |

### 2.5 DDL（权威脚本）

> 完整建表、**merchant** 角色初始化及迁移注释见 [`sql/biz_shop.sql`](../../../sql/biz_shop.sql)。下文为摘要。

```sql
CREATE TABLE `biz_merchant_account` (
  `account_id` bigint(20) NOT NULL COMMENT '经营账号ID，与 sys_user.user_id 一致',
  `merchant_id` bigint(20) NOT NULL COMMENT '所属商户ID',
  `shop_id` bigint(20) DEFAULT NULL COMMENT 'NULL=商户管理员',
  `del_flag` char(1) NOT NULL DEFAULT '0',
  `create_by` varchar(64) DEFAULT '',
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(64) DEFAULT '',
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`account_id`),
  KEY `idx_merchant_id` (`merchant_id`,`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户经营账号（商家端登录）';

CREATE TABLE `biz_shop` (
  `shop_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '店铺ID',
  `merchant_id` bigint(20) NOT NULL COMMENT '商户ID',
  `shop_name` varchar(128) NOT NULL COMMENT '店铺名称',
  `shop_avatar` varchar(512) NOT NULL COMMENT '店铺头像',
  `shop_status` char(1) DEFAULT '0' COMMENT '0开业 1停业，选填，默认0',
  `shop_desc` varchar(1000) DEFAULT NULL COMMENT '店铺描述',
  `shop_phone` varchar(20) DEFAULT NULL COMMENT '商家电话，选填',
  `del_flag` char(1) NOT NULL DEFAULT '0',
  `create_by` varchar(64) DEFAULT '',
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(64) DEFAULT '',
  `update_time` datetime DEFAULT NULL,
  `remark` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`shop_id`),
  KEY `idx_merchant_id` (`merchant_id`,`del_flag`),
  KEY `idx_shop_status` (`shop_status`,`del_flag`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店铺';
```

### 2.6 字典

| dict_type | 值 |
|-----------|-----|
| shop_status | 0 开业 / 1 停业 |

### 2.7 关联表（他模块）

```text
biz_goods.goods_status  0未上架 1待审核 2出售中 3审核失败 4已下架
biz_goods.shop_id       → biz_shop.shop_id
```

---

## 3. 接口设计

**路径：** `/agri/shop`  
**权限：** `agri:shop:list|query|add|edit|remove|account`

> **店铺设置** 接口已迁至 `/agri/shopSetting`，见《店铺设置技术方案.md》。

### 3.1 接口一览

| 方法 | 路径 | 权限 | 说明 |
|------|------|------|------|
| GET | `/list` | list | 分页列表 + 检索 |
| GET | `/{shopId}` | query | 详情 |
| POST | `/` | add | 添加店铺（**不含**经营账号字段） |
| PUT | `/` | edit | 编辑店铺（**不含**账号、不含 merchantId） |
| GET | `/{shopId}/deleteCheck` | query | 删除预检 |
| DELETE | `/{shopIds}` | remove | 逻辑删除（批量整批失败） |
| GET | `/merchant/{merchantId}/account` | account | 店铺账号管理-查询 |
| PUT | `/merchant/{merchantId}/account` | account | 改登录名/姓名/密码 |

**复用商户接口（选商户）：** `GET /agri/merchant/selectList`（S1：正常 + 经营完整；**不含** 店数上限）。  
**开店校验（店铺侧）：** 创建前调用 `IMerchantFacade.checkOpenShop(merchantId)`，与 `GET /agri/merchant/{id}/openShopCheck` **同一套 reason**（§7.7）。

### 3.2 列表 `GET /list`

| Query | 说明 |
|-------|------|
| pageNum, pageSize | 分页 |
| keyword | 模糊：shop_name、商户名称、`sys_user.user_name`、`sys_user.nick_name` |
| shopStatus | 0/1，空=全部 |
| merchantName | 模糊商户名（商户详情跳转带入） |

| rows 字段 | 说明 |
|-----------|------|
| shopId, shopName, shopAvatar | |
| merchantName | 商户 merchant_name，空则 unitName |
| shopStatus | |
| accountAdminName, accountLoginName | 脱敏手机号 |
| 操作 | 编辑/账号管理/设置/删除（前端按权限） |

默认：`del_flag=0`，`create_time DESC`。

### 3.3 详情 `GET /{shopId}`

返回店铺字段 + `merchantId`/`merchantName`（只读）+ 经营账号摘要（只读）+ `canDelete`（同 deleteCheck）。

### 3.4 添加店铺 `POST /`

**Body 要点**

```json
{
  "shopName": "张三农资店",
  "shopAvatar": "/profile/upload/xx.jpg",
  "shopPhone": "010-88886666",
  "merchantId": 1,
  "shopStatus": "0"
}
```

| 步骤 | 逻辑 |
|------|------|
| 1 | `IMerchantFacade.checkOpenShop(merchantId)` 不通过则阻断 |
| 2 | 店名唯一；名称、头像必填；**电话、状态选填**（填电话则校验格式） |
| 3 | 校验商户 **已有** `biz_merchant_account`；无则「该商户尚未配置经营账号…」 |
| 4 | insert `biz_shop`：`shop_status` 未传则 **0** |
| 5 | 同事务 `incrementShopCount(merchantId)` |

> **不再** 在开店时 insert/更新账号；账号由商户入驻阶段创建。

### 3.5 编辑店铺 `PUT /`

| 可改 | shopName, shopAvatar, shopStatus, shopDesc, shopPhone |
| 不可改 | merchantId；经营账号字段（走账号接口） |

**开业→停业（S12）：** 与商户认证类似，支持 `confirm`：

| confirm | 行为 |
|---------|------|
| false | `needConfirm=true`，`confirmMessage=停业后用户端将无法下单，是否继续？` |
| true | 持久化 `shop_status=1` |

**停业→开业：** 直接保存，无需 confirm。

**编辑 Body 样例：**

```json
{
  "shopId": 1,
  "shopName": "张三农资店",
  "shopAvatar": "/profile/upload/xx.jpg",
  "shopPhone": "010-88886666",
  "shopDesc": "主营农资",
  "shopStatus": "1",
  "confirm": false
}
```

`shopStatus` 由 `0→1` 时先 `confirm:false` 取 `needConfirm`；再 `confirm:true` 提交。`0→0` 或 `1→0` 不需 confirm。

### 3.6 店铺账号管理

**`GET /merchant/{merchantId}/account`**

```json
{
  "adminName": "张三",
  "loginName": "138****8000",
  "shops": [
    { "shopId": 1, "shopName": "店A", "shopStatus": "0" }
  ]
}
```

**`PUT /merchant/{merchantId}/account`**

| 字段 | 规则 |
|------|------|
| loginName | **11 位手机号**；对应 `sys_user.user_name`，`del_flag=0` 时全平台唯一；改登录名须 `confirm=true`，文案含「同步至该商户下全部店铺」 |
| adminName | 对应 `sys_user.nick_name` |
| password | 非空则重置 `sys_user.password`（BCrypt） |

保存后：更新 **`sys_user`** 登录凭证；`biz_merchant_account` 仅更新审计字段；列表展示同步（同 merchant_id 下所有店）；**不** 改商品/商户认证状态。

### 3.7 店铺设置（已迁出）

全平台策略 **`GET/PUT /agri/shopSetting`**、表 **`biz_shop_global_config`** 见 **《店铺设置技术方案.md》**；本模块 **无** `/{shopId}/settings` 接口。

### 3.10 商家端店铺资料

**路径：** `/agri/seller/shop`  
**上下文：** 请求头 `X-Shop-Id`（与分类/商品商家端一致）；Controller **`requireShopId()`** 强制非空  
**权限：** `agri:seller:shop:query` / `agri:seller:shop:edit`  
**详册：** 商家端 [`doc/店铺后台/店铺设置/店铺设置技术方案.md`](../../../店铺后台/店铺设置/店铺设置技术方案.md) v1.1

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 当前店铺资料（含商户联系人只读字段） |
| PUT | `/` | 修改名称、头像、描述、商家电话 |
| GET | `/employeeStats` | 当前店员工数 + 商户级配额（`IShopGlobalConfigFacade`） |

**GET `/` 成功 `data`（SellerShopProfileVO）扩展字段：**

| 字段 | 说明 |
|------|------|
| merchantId | 所属商户 |
| createTime | 开店时间 |
| contactName / contactPhone | 商户联系人（只读；电话脱敏） |
| merchantName | 商户名称（只读） |

**Body（PUT）：** `SellerShopProfileDTO`

| 字段 | 规则 |
|------|------|
| shopName | 必填；未删除店铺内唯一 |
| shopAvatar | 必填 |
| shopPhone | **选填**；有值时格式同平台（`^[0-9\-+()\s]{5,20}$`） |
| shopDesc | 选填，≤1000 |

**不可改：** `shop_status`、`merchant_id`、经营账号。  
**同步：** 更新 `biz_shop` 后平台 `GET /agri/shop/list` **直接读新值**，无离线同步。

### 3.11 删除

**`GET /{shopId}/deleteCheck`**

```json
{
  "canDelete": false,
  "reasons": ["请先下架或处理完商品后再删除店铺", "存在未完成订单，无法删除"]
}
```

| # | 条件 |
|---|------|
| 1 | `del_flag=0` |
| 2 | `!IGoodsFacade.hasBlockingGoodsForShopDelete` |
| 3 | `!IOrderFacade.hasUnfinishedOrdersByShop` |

**`DELETE /{shopIds}`：** 任一条失败 **整批不删**；成功 `del_flag=2` + `decrementShopCount`（同事务）。

### 3.12 对外协作（商品/C 端）

| 能力 | 说明 |
|------|------|
| `IShopFacade.isShopOpenForOrder(shopId)` | `shop_status=0` 且 `del_flag=0` |
| 新商品 submit 初始状态 | 读 **`IShopGlobalConfigFacade.getDefaultAuditPass()`**：0→待审核(1)，1→出售中(2) |
| C 端下单 | 商品出售中 + 库存&gt;0 + `isShopOpenForOrder`；停业返回「店铺已停业」 |

---

## 4. 端到端关联流程

### 4.1 入驻 → 开店 → 发品

```text
商户：主体入驻 → 绑定经营账号（biz_merchant_account）→ 编辑补全经营（biz_complete=1）
    ↓
GET /agri/merchant/selectList → POST /agri/shop（仅店铺字段，须已有经营账号）
    ↓
商家端：经营账号登录 → 选当前 shop_id → **本店** 分类/商品
    ↓
上架：按 **全局** default_audit_pass 进待审核或出售中
```

### 4.2 删店 → 删商户

```text
商品：无出售中/待审核 → DELETE /agri/shop/{id} → shop_count-1
    ↓
商户：shop_count=0 且无未完成订单 → DELETE /agri/merchant/{id}
```

### 4.3 商户详情跳转

商户详情 `shops` / 前端跳转：`/agri/shop/list?merchantName=xxx`。

---

## 5. 业务规则映射

| 编号 | 实现 |
|------|------|
| S1 | `checkOpenShop` + selectList |
| S2 | **不校验** 店铺数量上限；`shop_count` 仅 increment/decrement 统计 |
| S3 | insert `shop_status=0` |
| S4 | 添加店铺 **须** 商户已有经营账号；**不** 在 POST 中传账号字段 |
| S5 | 账号仅 `.../account` 接口；改 login 同步展示 |
| S6 | 无会员校验 |
| S7 | UPDATE 排除 `merchant_id` |
| S8 | 店名唯一校验 |
| S9 | 无级联 UPDATE goods/merchant |
| S10 | deleteCheck + DELETE + Facade |
| S11 | **全局** `default_audit_pass` 默认 0（见《店铺设置》） |
| S12 | 开业→停业 `confirm` |
| S13 | `IShopFacade.isShopOpenForOrder` 供 C 端 |
| S14 | 商家端 `updateSellerShopProfile` → 平台 list 同源 |

---

## 6. 菜单权限

| 权限 | 标识 |
|------|------|
| 列表 | agri:shop:list |
| 查询 | agri:shop:query |
| 新增 | agri:shop:add |
| 修改 | agri:shop:edit |
| 删除 | agri:shop:remove |
| 账号管理 | agri:shop:account |

> **店铺设置** 权限 `agri:shop:setting` 见《店铺设置技术方案》。

| 商家端-查询资料 | agri:seller:shop:query |
| 商家端-改资料 | agri:seller:shop:edit |

---

## 7. 实现补充（联调契约）

### 7.1 业务错误 msg

| 场景 | msg |
|------|-----|
| 店名重复 | 店铺名称已存在 |
| 登录名重复 | 登录名已存在 |
| 商户无经营账号 | 该商户尚未配置经营账号，请先在商户管理中完成入驻绑定 |
| 商户不可开店 | 请先完善该商户的经营与结算信息 |
| 删店-商品 | 请先下架或处理完商品后再删除店铺 |
| 删店-订单 | 存在未完成订单，无法删除 |
| 批量删除失败 | 部分店铺不满足删除条件，操作已取消 |

### 7.2 添加店铺 JSON（同商户第二家店）

```json
{
  "shopName": "张二店",
  "shopAvatar": "/profile/upload/y.jpg",
  "merchantId": 1
}
```

`shopPhone`、`shopStatus` 可省略；商户须已在入驻时绑定经营账号。

### 7.3 账号改登录名 confirm

```json
{ "loginName": "13900139000", "adminName": "张三", "password": null, "confirm": false }
```

→ `needConfirm=true`，`confirmMessage=将同步至该商户下全部店铺，是否继续？`

### 7.4 列表 login 脱敏

中间 4 位 `*`（如 `138****8000`）。

### 7.5 事务边界与 shop_count 对账

```text
@Transactional
createShop()  → insert shop → incrementShopCount
deleteShop()  → 预检 → del_flag=2 → decrementShopCount（账号行不删，见 §2.2）
```

| 项 | 说明 |
|----|------|
| 并发 | 开店/删店与 `increment/decrement` **同一事务**；禁止绕过 Service 直接改 `shop_count` |
| 对账（可选） | 低频任务：`COUNT(biz_shop WHERE merchant_id=? AND del_flag=0)` 与 `biz_merchant.shop_count` 比对告警 |

### 7.6 字段校验规则（Service / JSR303）

| 字段 | 规则 |
|------|------|
| shopName | 非空，长度 ≤128；未删除店铺内唯一 |
| shopAvatar | 非空 URL；jpg/png/jpeg；≤5MB（与 RuoYi 上传一致） |
| shopPhone | **选填**；有值时手机或固话（项目统一正则） |
| shopStatus | **选填**；0/1；未传默认 0 |
| shopDesc | 选填，≤1000 |

### 7.7 `checkOpenShop` / `openShopCheck`（与商户方案对齐）

店铺 `POST /` 与商户 `GET /agri/merchant/{id}/openShopCheck` 共用 `IMerchantFacade.checkOpenShop`：

| allowed | reason（data.reason 或 msg） | 触发条件 |
|:-------:|------------------------------|----------|
| false | 经营信息未完善 | `biz_complete=0` |
| false | 商户已冻结 | `cert_status=1` |
| false | 商户已注销 | `cert_status=2` |
| false | 商户已删除 | `del_flag=2` |
| true | null | 上述均不满足 |

### 7.8 经营账号生命周期（需求 §11.5）

| 事件 | `biz_merchant_account` |
|------|-------------------|
| 商户入驻绑定 | insert，del_flag=0（**非** 首家店时创建） |
| 删店但仍有店 | 不删账号 |
| 删末店 | **保留**账号行，便于再开店 |
| 改登录名/密码 | update 同一 merchant_id 行 |

---

## 8. 文档索引

| 文档 | 版本 |
|------|------|
| 店铺管理功能需求.md | v1.3.6 |
| 店铺管理测试用例.md | v1.0（表名已同步为 `biz_merchant_account`） |
| 商户管理功能需求/技术方案 | v1.3.1 / v1.3 |
| 商品管理功能需求/技术方案 | v1.3.3 / v1.2 |
| 商品分类功能需求/技术方案 | v1.4 / v1.2 |

---

## 9. 非本期实现

商家端完整登录与 `shop_id` 切换、子管理员 CRUD、店铺逻辑删除恢复、会员打通、导出。

---

## 13. v1.2.5 修订摘要

| 项 | 内容 |
|----|------|
| `biz_shop` | `shop_status`、`shop_phone` **选填**（可 NULL；status 默认 0） |
| `POST /agri/shop` | **移除** `loginName`/`adminName`/`password`；开店前校验商户 **已有** `biz_merchant_account` |
| 账号创建 | 改由 **商户入驻** `bindAccountOnMerchantCreate`；入驻审核通过开店同理 |

## 13. v1.2.4 修订摘要

| 项 | 内容 |
|----|------|
| `biz_goods_category` | `shop_id` **可空**；NULL=平台分类；非空=店铺分类 |
| 与店铺关系 | 商品/订单仍经 `biz_shop.shop_id`；平台分类与店铺 **无直接 FK** |
| 维护边界 | 店铺模块 **不** CRUD 分类；平台/商家在商品分类模块分别维护 |

## 12. v1.2.1 修订摘要

| 项 | 内容 |
|----|------|
| 表名 | `biz_shop_account` → **`biz_merchant_account`**（与 `merchant_id` 维度一致） |
| 实体类 | `BizShopAccount` → **`BizMerchantAccount`**；`IShopAccountService` → **`IMerchantAccountService`** |
| 能力归属 | 仍由 **店铺模块** 维护 CRUD 与开店事务；仅表名/实体名调整 |

---

## 14. v1.4 修订摘要

| 项 | 内容 |
|----|------|
| 表结构 | `account_id=sys_user.user_id`（PK 非自增）；移除 `login_name`/`admin_name`/`password`；新增 `shop_id`（NULL=商户管理员） |
| 绑定 | 商户入驻仅 insert 关联行；登录凭证沿用/维护于 `sys_user` |
| 账号管理 | `BizMerchantAccountMapper.updateSysUserProfile` 更新 `sys_user` |
| 列表 SQL | JOIN `biz_merchant_account`（`shop_id IS NULL`）+ `sys_user` |
| 删除校验 | 平台管理员删除：`countByAccountId(user_id)` |

## 15. v1.3 修订摘要

| 项 | 内容 |
|----|------|
| DDL | 权威脚本 `sql/biz_shop.sql`；`biz_shop` 无经营账号列 |
| 经营账号 | 商户入驻时创建关联行；展示字段来自 `sys_user` |
| 列表 SQL | `BizShopMapper.selectList` JOIN `biz_merchant` + LEFT JOIN `biz_merchant_account` + `sys_user` |
| 登录名 | 账号管理改密：`sys_user.user_name` 须 11 位手机号 |
| 商家电话 | 平台添加/编辑、商家端资料均为 **选填**（有值校验格式） |

---

## 16. v1.2.6 修订摘要

| 项 | 内容 |
|----|------|
| 商家端 | `GET /employeeStats` 员工概览 + 配额；GET 资料 VO 扩展 `merchantId/createTime/contactName/contactPhone/merchantName` |
| 实现 | `SellerShopController`、`ShopServiceImpl.selectSellerEmployeeStats`；详册见商家端《店铺设置技术方案》v1.1 |
| 测试 | `ShopServiceImplTest`、`SellerShopControllerTest` 已覆盖 |

---

*文档版本：v1.2.6 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《店铺管理功能需求.md》v1.3.7、《商品分类技术方案.md》v1.2*
