# 洗车系统 (car-wash-java) ## 项目结构 ``` car-wash-java/ ├── car-wash-entity/ # 实体、VO、SQL 迁移脚本 ├── car-wash-mapper/ # MyBatis-Plus Mapper ├── car-wash-service/ # 核心业务逻辑 ├── car-wash-common/ # 共享工具(Sa-Token、Redis、AOP) ├── car-wash-admin/ # 管理后台 API(Spring Boot) ├── car-wash-miniapp/ # 微信小程序 API(Spring Boot) ├── car-wash-mp/ # 微信公众号 API(Spring Boot) ├── admin-web/ # 运营管理后台前端(Vue 3 + Element Plus + Vite) ├── admin-h5/ # 运营商 H5(uni-app) └── admin-web-new/ # 管理后台前端重构版(Vite + Vue 3) ``` ## 技术栈 - **后端**: Java 21, Spring Boot 3.3.3, MyBatis-Plus 3.x, Sa-Token, Redis + Redisson, RabbitMQ, 微信支付 APIv3 - **前端**: Vue 3 + TypeScript + Vite + Element Plus + Pinia + uni-app - **构建**: Maven 多模块 ## 核心业务表 | 表 | 用途 | |---|---| | `t_user` | 微信小程序用户(`station_id` 标识归属站点) | | `t_wash_order` | 洗车订单(`is_cross` 标记是否跨店消费) | | `t_split_record` | 分账流水,按类型追踪站点间资金流向 | | `t_settlement_record` | 月度结算单(幂等键 `uk_station_period`) | | `t_station_account` | 站点账户余额(`available_balance` 可提现额) | | `t_wallet_detail` | 用户钱包流水(充值/消费/退款) | | `t_pay_log` | 微信支付日志(`out_trade_no` 商户订单号,`transaction_id` 微信交易号) | | `t_recharge_config` | 充值配置(归属 `group_id` 分组) | | `t_recharge_config_group` | 充值配置分组(`is_default=1` 为默认分组) | ## 结算 V2 模型 月度结算由 `SettlementServiceImpl.executeMonthlySettlement()` 驱动,每月对上月数据进行结算。 **SplitRecord 资金类型**: | 类型 | 值 | 方向 | |---|---|---| | `TYPE_RECHARGE` | 1 | 同一站点收支,`from=to` | | `TYPE_CROSS_EXPEND` | 4 | 跨店:归属站 → 消费站 | | `TYPE_CROSS_INCOME` | 6 | 跨店:归属站 → 消费站(与 EXPEND 配对) | | `TYPE_REFUND` | 5 | 退款支出 | **月结公式**: ``` feeBase = totalRecharge - totalRefund - totalCrossExpend + totalCrossIncome platformFee = feeBase × 10% available = openingBalance + feeBase - platformFee ``` `available > 0` 时正常结算,转入站点可提现余额,同时记录平台服务费。负数则结转下期。 ## 关键业务链路 ### 用户注册与站点归属 - 扫码设备注册:直接通过 `shortId` 确定 `stationId`(`UserServiceImpl.java:135-137`) - 自主注册:`stationId = null`,首次消费时在 `WashOrderServiceImpl.createOrder()` 中自动归属 ### 充值 → 分账 - 有归属站:`WxPayServiceImpl.wxNotify()` 中创建 `SplitRecord(TYPE_RECHARGE, from=stationId, to=stationId)` - 无归属站:不创建 SplitRecord,由 `OrderSettlementServiceImpl.backfillUnattributedRecharges()` 在首次消费结算时追溯补建 ### 消费 → 结算 - `createOrder()` → 扣预付款,设置 `isCross` 标记 - `settleOrder()` → 扣款,回填无归属充值,本店跳过/跨店创建分账 ## 注意事项 - `SaToken` 注解 `@SaIgnore` 需要确保被 Spring 代理拦截才能生效 - 分账记录 `tradeNo` 与微信支付 `transactionId` 对应,通过 `t_pay_log` 桥接 `WalletDetail` - 月结有幂等保护(`uk_station_period`),重复执行不会产生重复结算单