# 夜市摊位抽成设计 ## 背景 现有抽成模式:平台从商家(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(已有) |