# 夜市摊位抽成设计

## 背景

现有抽成模式：平台从商家（userType=1）每笔交易中按 `commission` 比例收取佣金，收入入商家个人钱包。

新增夜市层级后，交易主体变为摊位（PosStore, isStall=1），摊位归属夜市（InfoUser, userType=3）管理。摊位可能有多个摊位主，钱包是门店维度的，不是个人维度的。

## 业务规则

- 平台从摊位每笔交易中收取佣金，比例由所属夜市统一设定
- 夜市不参与交易抽成，只线下收取摊位费
- 抽成比例存在夜市用户的 `commission` 字段上，下属所有摊位统一使用
- 资金流向：交易额 - 平台抽成 = 摊位到手，平台直接扣除
- 摊位收入进入摊位门店钱包（UserWallet.storeId 关联），不进摊位主个人钱包

## 技术设计

### 改动范围

| 改动 | 文件 |
|------|------|
| 抽成判断逻辑 | `PosOrderController.setSanghuBilling()` |
| 摊位钱包更新 | `WalletService` 新增摊位钱包操作方法 |
| 账单关联摊位 | `UserBilling` 新增 `storeId` 字段 |
| 数据库 | `user_billing` 表新增 `store_id` 列 |

### 1. 抽成判断逻辑

修改 `PosOrderController.setSanghuBilling()`：

```
PosStore store = posStoreService.getById(posOrder.getMdId())
if (store.getIsStall() == 1 && store.getNightMarketId() != null) {
    // 摊位订单：用夜市的抽成比例
    InfoUser nightMarket = infoUserService.getById(store.getNightMarketId())
    fcbili = nightMarket.getCommission() == null ? 0.00 : nightMarket.getCommission()
} else {
    // 普通商家：保持原逻辑
    InfoUser user = infoUserService.getById(posOrder.getShId())
    fcbili = user.getCommission() == null ? 0.00 : user.getCommission()
}
```

后续计算不变：`chouc = fenc * fcbili`，`shfc = fenc - chouc`。

### 2. 账单记录

`user_billing` 表新增 `store_id` 列（BIGINT, NULL, 默认NULL）。

摊位订单账单记录：

| 字段 | 值 | 说明 |
|------|-----|------|
| userId | shId（夜市ID） | 标识交易归属哪个夜市 |
| mdId | 摊位门店ID | 标识哪个摊位的交易 |
| storeId | 摊位门店ID | 关联摊位钱包（新增字段） |
| amount | shfc | 摊位到手金额 |
| divvy | chouc | 平台抽成金额 |
| divvyRate | fcbili | 抽成比例 |

普通商家订单不受影响，`storeId` 为 NULL。

### 3. 钱包更新

`WalletService` 新增摊位钱包操作方法 `addStallBalance(Long storeId, BigDecimal amount, UserBilling billing)`：

```
1. 通过 storeId 查找 UserWallet（WHERE store_id = ?）
2. 加分布式锁
3. 乐观锁更新钱包余额（与现有 addBalance 逻辑相同）
4. 创建账单记录（billing.storeId 已设置）
```

`setSanghuBilling()` 中的调用变更：

```
if (isStall) {
    billing.setStoreId(store.getId())
    walletService.addStallBalance(store.getId(), amount, billing)
} else {
    walletService.addBalance(userId, amount, billing)  // 原逻辑不变
}
```

### 4. 数据模型

仅一个数据库变更：

```sql
ALTER TABLE user_billing ADD COLUMN store_id BIGINT DEFAULT NULL COMMENT '关联摊位ID，非摊位账单为NULL';
```

复用现有字段：

| 字段 | 所在实体 | 用途 |
|------|---------|------|
| commission | InfoUser (夜市) | 夜市设定的平台抽成比例 |
| isStall | PosStore | 判断是否为摊位（1=摊位） |
| nightMarketId | PosStore | 摊位关联的夜市用户ID |
| storeId | UserWallet | 摊位钱包关联的门店ID（已有） |
