For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [x]) syntax for tracking.
Goal: 摊位订单完成后,使用所属夜市的抽成比例计算平台佣金,收入入摊位门店钱包。
Architecture: 在现有 setSanghuBilling 中增加摊位判断分支,通过 PosStore.isStall 判断是否为摊位订单,是则用夜市的 commission 计算抽成,并调用新增的 addStallBalance 方法更新摊位门店钱包。
Tech Stack: Java, Spring Boot, MyBatis-Plus, Redisson
Files:
Modify: ruoyi-system/src/main/java/com/ruoyi/system/domain/UserBilling.java
[x] Step 1: 在 UserBilling 实体中添加 storeId 字段
在 private Long mdId; 之后添加:
/** 关联摊位ID,非摊位账单为NULL */
private Long storeId;
文件:ruoyi-system/src/main/resources/mapper/system/UserBillingMapper.xml
找到 mdId 的 result 映射,在其后添加:
<result property="storeId" column="store_id" />
[x] Step 3: 在数据库中执行 ALTER TABLE
ALTER TABLE user_billing ADD COLUMN store_id BIGINT DEFAULT NULL COMMENT '关联摊位ID,非摊位账单为NULL';
[x] Step 4: Commit
git add ruoyi-system/src/main/java/com/ruoyi/system/domain/UserBilling.java ruoyi-system/src/main/resources/mapper/system/UserBillingMapper.xml
git commit -m "feat: UserBilling 新增 storeId 字段关联摊位钱包"
Files:
Modify: ruoyi-admin/src/main/java/com/ruoyi/app/service/WalletService.java
[x] Step 1: 添加 addStallBalance 方法
在 addBalance(Long userId, BigDecimal amount, UserBilling billing) 方法之后添加新方法。逻辑与 addBalance 类似,但通过 storeId 查找摊位钱包并更新:
/**
* 增加摊位钱包余额
*
* @param storeId 摊位门店ID
* @param amount 增加金额
* @param billing 账单信息
*/
@Transactional(rollbackFor = Exception.class)
public void addStallBalance(Long storeId, BigDecimal amount, UserBilling billing) {
if (storeId == null || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException(MessageUtils.message("no.wallet.common.cs.error"));
}
UserWallet stallWallet = userWalletService.selectByStoreId(storeId);
if (stallWallet == null) {
throw new ServiceException("摊位钱包不存在");
}
String lockKey = "wallet:stall:lock:" + storeId;
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(LOCK_WAIT_TIME, LOCK_TIMEOUT, TimeUnit.SECONDS)) {
boolean releaseInFinally = true;
try {
BigDecimal newBalance = stallWallet.getBalanceWallet().add(amount);
LambdaUpdateWrapper<UserWallet> updateQuery = new LambdaUpdateWrapper<>();
updateQuery.eq(UserWallet::getStoreId, storeId)
.eq(UserWallet::getVersion, stallWallet.getVersion());
stallWallet.setBalanceWallet(newBalance);
stallWallet.setVersion(stallWallet.getVersion() + 1);
boolean updateSuccess = userWalletService.update(stallWallet, updateQuery);
createUserBill(billing, stallWallet.getBalanceWallet());
if (!updateSuccess) {
throw new ServiceException(MessageUtils.message("no.wallet.update.faile"));
}
releaseInFinally = false;
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
});
} finally {
if (releaseInFinally && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} else {
throw new ServiceException(MessageUtils.message("no.system.busy.try.again"));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ServiceException(MessageUtils.message("no.operation.interrupted.try.again"));
}
}
[x] Step 2: Commit
git add ruoyi-admin/src/main/java/com/ruoyi/app/service/WalletService.java
git commit -m "feat: WalletService 新增 addStallBalance 摊位钱包操作方法"
Files:
Modify: ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderController.java
[x] Step 1: 注入 IPosStoreService 依赖
在 PosOrderController 中已有的 @Autowired 字段区域添加:
@Autowired
private IPosStoreService posStoreService;
确认 import 存在:
import com.ruoyi.system.service.IPosStoreService;
将现有方法(约 576-641 行)替换为:
public void setSanghuBilling(@NotNull PosOrder posOrder) {
long count = userBillingService.count(new LambdaQueryWrapper<UserBilling>().eq(UserBilling::getUserId, posOrder.getShId()).eq(UserBilling::getType, "0").eq(UserBilling::getDdId, posOrder.getDdId()));
if (count <= 0) {
UserBilling billing = new UserBilling();
DecimalFormat df = new DecimalFormat("#.00");
// 判断是否为摊位订单
PosStore store = posStoreService.getById(posOrder.getMdId());
boolean isStall = store != null && store.getIsStall() != null && store.getIsStall() == 1
&& store.getNightMarketId() != null;
Double fcbili;
if (isStall) {
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();
}
fcbili = getShCommissionRate(fcbili, posOrder.getShId());
JSONArray list = JSONArray.parseArray(posOrder.getFood());
int fenc = 0;
for (int i = 0; i < list.size(); i++) {
JSONObject foods = list.getJSONObject(i);
int price = foods.getInteger("price");
int otherPrice = foods.getInteger("otherPrice");
int num = foods.getInteger("number");
int age = (price + otherPrice) * num;
fenc += age;
}
String remark = "";
if (posOrder.getMdYhId() != null && posOrder.getMdDiscountAmount() != null) {
VipUserQuanyi userQuanyi = userQuanyiService.getById(posOrder.getMdYhId());
if (userQuanyi != null && "1".equals(userQuanyi.getType())) {
fenc = fenc - posOrder.getMdDiscountAmount();
String yhmcMessage = MessageUtils.message("no.posorder.md.yh.mc.messag");
yhmcMessage = StrUtil.format(yhmcMessage, posOrder.getMdYhName());
remark += yhmcMessage;
String yhAmount = MessageUtils.message("no.posorder.md.yh.jiner.messag");
yhAmount = StrUtil.format(yhAmount, posOrder.getMdDiscountAmount());
remark += yhAmount;
}
}
if (posOrder.getMdSalesName() != null && posOrder.getMdSalesReduction() != null) {
fenc = fenc - posOrder.getMdSalesReduction();
String cxNameMessage = MessageUtils.message("no.posorder.md.cx.mc.messag");
cxNameMessage = StrUtil.format(cxNameMessage, posOrder.getMdSalesName());
remark += cxNameMessage;
String cxAmount = MessageUtils.message("no.posorder.md.cx.jiner.messag");
cxAmount = StrUtil.format(cxAmount, posOrder.getMdSalesReduction());
remark += cxAmount;
}
billing.setIllustrate(remark);
Double chouc = fenc * fcbili;
Double shfc = fenc - chouc;
billing.setUserId(posOrder.getShId());
billing.setDdId(String.valueOf(posOrder.getDdId()));
billing.setType("0");
billing.setAmount(Double.valueOf(df.format(shfc)));
billing.setDivvy(Double.valueOf(df.format(chouc)));
billing.setState("0");
billing.setMdId(posOrder.getMdId());
billing.setUserType("1");
billing.setDivvyRate(fcbili);
if (isStall) {
billing.setStoreId(store.getId());
walletService.addStallBalance(store.getId(), BigDecimal.valueOf(billing.getAmount()), billing);
} else {
walletService.addBalance(billing.getUserId(), BigDecimal.valueOf(billing.getAmount()), billing);
}
}
}
[x] Step 3: Commit
git add ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderController.java
git commit -m "feat: setSanghuBilling 支持摊位订单抽成逻辑"