# 账户管理 — 技术方案

> **依据：** 《账户管理功能需求.md》v1.0  
> **关联：** 平台《提现审核功能需求》v1.0、《提现审核技术方案》v1.0、《账务管理.md》v1.0；商家《提现管理功能需求》v1.0、《提现管理技术方案》v1.0、《资金概览功能需求》v1.0；《关联需求分析.md》v1.6、《商家端店铺上下文接口说明》  
> **范围：** 商家端 **财务中心 · 账户管理** — 当前店铺 **提现收款账户** 列表、添加、编辑、删除，及 **提现下拉 options**；**不含** 提现提交、平台审核、余额/流水。  
> **原则：** `X-Shop-Id` 店铺隔离；**逻辑删除**；账号 **明文存库、展示脱敏**；编辑/删除 **不影响** 已提交提现 **账户快照**。

---

## 1. 技术架构

| 项 | 选型 |
|----|------|
| 基础框架 | RuoYi **v3.9.2**（`springboot2` 分支） |
| 数据库 | **MySQL 5.7.39**；表 **`biz_shop_pay_account`**（财务公共 DDL） |
| ORM / 权限 / 响应 | MyBatis；`@PreAuthorize`；`AjaxResult` / `TableDataInfo` |
| 店铺上下文 | `SellerShopContext` + 请求头 **`X-Shop-Id`** |
| 脱敏 | `FinanceSupport.maskAccountNo`；列表/options **不下发明文**（VO 用 mask） |
| 类型文案 | `FinanceSupport.toAccountTypeText` |

### 1.1 与兄弟模块边界

| 维度 | **本模块 · 账户管理** | 提现管理 | 资金概览 | 平台 · 提现审核 |
|------|----------------------|----------|----------|-----------------|
| 路径 | `/agri/seller/finance/payAccount` | `/agri/seller/finance/withdraw` | `/agri/seller/finance/overview` | `/agri/finance/withdrawAudit` |
| 写 | **POST / PUT / DELETE** | POST submit | **无** | PUT approve/reject |
| 读本模块表 | 列表 CRUD | **options** + submit 校验 | **countBank** | **不读**（用提现快照） |
| 数据范围 | **当前 shop_id** | 当前 shop_id | 当前 shop_id | 全平台提现单 |

### 1.2 关联协作链

```text
【本模块】POST 添加账户 → biz_shop_pay_account（del_flag=0）
    → GET /list 列表展示（脱敏）
    → GET /options 供提现管理下拉

【提现管理 · submit】
    → selectByIdAndShop(accountId, shopId, del_flag=0)
    → 快照写入 biz_shop_withdraw（type/name/mask）
    → 此后 edit/delete 本模块 **不改** 已存在提现单

【资金概览 · summary】
    → countBankByShopId（account_type=1 且 del_flag=0）
    → **不** 调用本模块 Controller

【平台 · 提现审核】
    → 读 biz_shop_withdraw 账户快照打款
    → **无** 本模块 API
```

> 本表 **不同于** 平台商户 **主体结算账户**、**不同于** `biz_shop_wechat_pay`（C 端代收）；见需求 **PA-A9 / PA-A10**。

### 1.3 模块落位

**文档目录：** `doc/店铺后台/财务中心/账户管理/`  
**菜单：** 财务中心 → 账户管理（`menu_id=6630`）  
**后端包：** `com.ruoyi.web.modules.finance`

```text
finance/
├── controller/SellerPayAccountController.java      # 本模块入口
├── service/IShopFundService.java                 # 账户 CRUD + options（与财务共用 Service）
├── service/impl/ShopFundServiceImpl.java
├── domain/BizShopPayAccount.java
├── mapper/BizShopPayAccountMapper.java
├── dto/PayAccountSaveDTO.java
├── vo/PayAccountListRowVO.java
├── vo/PayAccountOptionVO.java
├── constant/FinanceConstants.java
├── support/FinanceSupport.java
resources/mapper/finance/BizShopPayAccountMapper.xml

sql/biz_shop_finance.sql                          # 公共 DDL（含本表）
sql/biz_shop_finance_menu.sql                     # 6630～6633
```

**前端（建议）：**

| 项 | 值 |
|----|-----|
| 页面 | `ruoyi-ui/src/views/agri/seller/finance/payAccount/index.vue` |
| API | `ruoyi-ui/src/api/agri/seller/finance/payAccount.js` |

---

## 2. 数据库设计

**权威 DDL：** [`sql/biz_shop_finance.sql`](../../../../sql/biz_shop_finance.sql)

### 2.1 收款账户表 `biz_shop_pay_account`

| 字段 | 类型 | 说明 |
|------|------|------|
| account_id | bigint PK | 账户 ID |
| shop_id | bigint | **= X-Shop-Id** |
| account_type | char(1) | `1`银行卡 `2`支付宝 `3`微信 |
| real_name | varchar(64) | 真实姓名 |
| account_no | varchar(128) | 账号（**库内明文**） |
| del_flag | char(1) | `0`正常 `2`逻辑删除 |
| create_by | varchar(64) | 创建人 |
| create_time | datetime | 添加时间 |
| update_by | varchar(64) | 更新人 |
| update_time | datetime | 更新时间 |

**索引：** `idx_shop_del (shop_id, del_flag)`。

**枚举（account_type）：**

| 值 | 文案 | 资金概览计数 |
|----|------|--------------|
| `1` | 银行卡 | **计入** bankAccountCount |
| `2` | 支付宝 | 不计入 |
| `3` | 微信 | 不计入 |

### 2.2 与提现单关系（快照，非外键）

| 项 | 说明 |
|----|------|
| 提交时 | `biz_shop_withdraw.account_id` 存源 ID；**同时** 快照 type/name/mask |
| 编辑后 | **新提现** 用新账号；**旧单** 仍用快照 |
| 删除后 | `selectByIdAndShop` **查不到**；**不可再选**；历史快照 **保留** |

### 2.3 查询约定

| 场景 | 条件 |
|------|------|
| 列表 / options | `shop_id = ? AND del_flag = '0'` |
| 编辑 / 删除 | 同上 + `account_id = ?` |
| 提现 submit 校验 | `selectByIdAndShop` |
| 概览银行卡数 | `account_type = '1' AND del_flag = '0'` |

---

## 3. 接口设计

**基路径：** `/agri/seller/finance/payAccount`  
**上下文：** 请求头 **`X-Shop-Id`** 必填

### 3.1 接口一览

| 方法 | 路径 | 权限 | 说明 |
|------|------|------|------|
| GET | `/list` | `agri:seller:finance:payAccount:list` | 账户分页列表 |
| GET | `/options` | `agri:seller:finance:withdraw:list` | 提现可选下拉（协作） |
| POST | `/` | `agri:seller:finance:payAccount:add` | 添加账户 |
| PUT | `/` | `agri:seller:finance:payAccount:edit` | 编辑账户 |
| DELETE | `/{accountId}` | `agri:seller:finance:payAccount:remove` | 逻辑删除 |

### 3.2 列表 `GET /list`

**Query：** `pageNum`, `pageSize`（草稿 **无** 关键词检索）。

**固定条件：** `shop_id = requireShopId()`；`del_flag = '0'`；`ORDER BY create_time DESC, account_id DESC`。

**行 VO（PayAccountListRowVO）：**

| 字段 | 说明 |
|------|------|
| accountId | 账户 ID |
| accountType / accountTypeText | 类型码 + 文案 |
| realName | 真实姓名 |
| accountNoMask | **脱敏账号**（列表 **不返回** 明文 accountNo） |
| createTime | 添加时间 |

### 3.3 提现下拉 `GET /options`（协作）

> 权限挂 **提现 list**，供 **提现管理** 提交页调用；**非** 账户管理菜单专属。

**响应 rows（PayAccountOptionVO）：**

| 字段 | 说明 |
|------|------|
| accountId | 可选值 |
| accountType / accountTypeText | 类型 |
| realName | 姓名 |
| accountNoMask | 脱敏账号 |
| label | `{类型} {脱敏} {姓名}` 摘要 |

### 3.4 添加 `POST /`

**Body（PayAccountSaveDTO）：**

| 字段 | 必填 | 说明 |
|------|------|------|
| accountType | 是 | `1` / `2` / `3` |
| realName | 是 | 非空，trim |
| accountNo | 是 | 非空，trim；银行卡须 **纯数字** |

**成功：** `AjaxResult.success()`（无 data 或返回 accountId 均可）。

### 3.5 编辑 `PUT /`

**Body（PayAccountSaveDTO）：**

| 字段 | 必填 | 说明 |
|------|------|------|
| accountId | 是 | 本店有效账户 |
| accountType | — | **前端禁用**；服务端 **忽略变更** 或须与库内 **一致**（PA-A3） |
| realName | 是 | 可改 |
| accountNo | 是 | 可改 |

**更新条件：** `account_id AND shop_id AND del_flag='0'`；影响行 0 → 账户不存在。

### 3.6 删除 `DELETE /{accountId}`

**逻辑删除：** `del_flag = '2'`；**不校验** 是否有关联待审提现（快照已固化）。

**成功：** `AjaxResult.success()`。

---

## 4. 业务规则（实现）

| 编号 | 需求规则 | 实现要点 |
|------|----------|----------|
| PA-A1 | 唯一维护入口 | 本 Controller CRUD |
| PA-A2 | 三类收款方式 | `FinanceConstants.ACCOUNT_TYPE_*` 校验 |
| PA-A3 | 类型不可改 | edit **不更新** account_type（或 DTO 忽略） |
| PA-A4 | 仅当前店铺 | 全 SQL `shop_id = requireShopId()` |
| PA-A5 | 逻辑删除 | `logicDelete`；列表/options **过滤** del |
| PA-A6 | 不影响提现快照 | **无** 回写 `biz_shop_withdraw` |
| PA-A7 | 提现仅选有效户 | withdraw submit 调 `selectByIdAndShop` |
| PA-A8 | 概览银行卡数 | `countBankByShopId`（overview 模块调用） |

**校验（validatePayAccountDto）：**

| 场景 | 处理 |
|------|------|
| 类型无效 | `MSG_PAY_ACCOUNT_TYPE_INVALID` |
| 姓名为空 | `MSG_PAY_ACCOUNT_NAME_REQUIRED` |
| 账号为空 | `MSG_PAY_ACCOUNT_NO_REQUIRED` |
| 编辑无 accountId | `MSG_PAY_ACCOUNT_NOT_FOUND` |

**脱敏（FinanceSupport.maskAccountNo）：**

| 类型 | 规则 |
|------|------|
| 银行卡 | `**** **** **** {后4位}` |
| 支付宝/微信 | 首尾保留 + 中间 `****` |

---

## 5. 权限与菜单

| 权限 | 说明 |
|------|------|
| `agri:seller:finance:payAccount:list` | 列表 |
| `agri:seller:finance:payAccount:add` | 添加 |
| `agri:seller:finance:payAccount:edit` | 编辑 |
| `agri:seller:finance:payAccount:remove` | 删除 |

菜单：**财务中心 → 账户管理**（`parent_id=6600`，`menu_id=6630`，`path=payAccount`）。  
脚本：[`sql/biz_shop_finance_menu.sql`](../../../../sql/biz_shop_finance_menu.sql)（6630～6633）。

---

## 6. 测试要点

| 编号 | 场景 | 预期 |
|------|------|------|
| T1 | 列表 shop 隔离 | 仅本店、未删除 |
| T2 | 添加三类账户 | insert 成功；列表可见 |
| T3 | 编辑姓名/账号 | update 成功；**type 不变** |
| T4 | 删除账户 | del_flag=2；options 不可选 |
| T5 | 删除后 submit 旧 accountId | 提现报账户不存在 |
| T6 | options label | 含类型、脱敏、姓名 |
| T7 | countBankByShopId | 仅 type=1 计数 |

**建议测试类：** `ShopFundServiceImplTest`（扩展账户用例）、`SellerPayAccountControllerTest`（MockMvc）。

---

## 7. 实现状态

| 项 | 状态 |
|----|------|
| DDL `biz_shop_pay_account` | **已有** |
| `SellerPayAccountController` / `ShopFundServiceImpl` | **已实现** |
| 菜单 SQL 6630 段 | **已有** |
| edit 锁定 accountType（PA-A3） | **建议** 服务端忽略 type 变更 |
| ruoyi-ui 账户管理页 | **待建** |
| 四要素校验 / 默认账户 | **非本期** |

---

## 8. 版本记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版：对齐《账户管理功能需求》v1.0；关联提现管理/资金概览/平台提现审核；简练 DB/接口设计 |

---

*文档版本：v1.0 · 依据《账户管理功能需求.md》v1.0*
