1. 项目背景与核心价值
去年帮老家县城的农业合作社搭建线上销售平台时,我深刻体会到传统农产品流通的痛点:中间环节多导致农户利润薄、消费者买得贵。这个基于SpringBoot和微信小程序的易农产品商城,正是针对这个痛点设计的B2B2C解决方案。
多商家模式让农户/合作社能直接开店,消费者可以像逛集市一样选购不同产地的生鲜。相比单一店铺的小程序,技术实现上需要解决三个核心问题:
- 多租户数据隔离(不同商家的商品/订单独立)
- 统一支付与分账体系(平台抽成+商家结算)
- 高并发秒杀设计(应对季节性农产品集中上市)
2. 技术架构设计
2.1 后端SpringBoot选型考量
选择SpringBoot 2.7.x版本(非最新的3.x)主要基于:
- 微信小程序官方SDK对JDK8兼容性更好
- 农产品场景不需要响应式编程等新特性
- 社区资源丰富(遇到问题容易搜索解决方案)
关键依赖配置示例:
xml复制<!-- 多租户支持 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 微信支付分账 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.5.0</version>
</dependency>
2.2 微信小程序端特殊处理
由于农产品需要展示新鲜度,我们做了这些优化:
- 图片懒加载+WebP格式转换(节省流量)
- 自定义顶部导航栏(显示地理位置和配送时间)
- 视频展示模块(农户可上传种植过程视频)
小程序端核心代码结构:
code复制pages/
├── market/ # 多商家集市页
├── shop/ # 单商家主页
├── product/ # 商品详情页
└── order/ # 订单流程
components/
├── countdown/ # 秒杀倒计时
└── location/ # 地理围栏组件
3. 多商家系统实现细节
3.1 租户隔离方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 独立数据库 | 完全隔离 | 成本高 | 大型平台 |
| Schema隔离 | 中等成本 | 需DB权限 | 中台系统 |
| 字段标记(最终采用) | 改造成本低 | 需严格代码审查 | 中小型项目 |
我们采用字段标记方案,核心逻辑:
java复制// 所有实体类继承此基类
public class BaseEntity {
@TableField(fill = FieldFill.INSERT)
private String tenantId;
}
// 自动注入租户ID
public class TenantInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String tenantId = WxAppContext.getCurrentShopId();
TenantContext.setCurrentTenant(tenantId);
}
}
3.2 支付分账的坑
微信支付分账接口有几个关键限制:
- 分账比例必须≤30%(平台抽成需设计合理)
- 冻结期默认30天(需申请缩短)
- 每日限额50万(需提前报备)
分账核心代码:
java复制public void handleProfitSharing(Order order) {
// 构造分账请求
ProfitSharingRequest request = new ProfitSharingRequest();
request.setTransactionId(order.getPayNo());
request.setOutOrderNo(order.getOrderNo());
// 平台抽成10%
request.addReceiver(new Receiver(
platformMchId, 10, "平台服务费"));
// 商家得90%
request.addReceiver(new Receiver(
order.getShop().getMchId(), 90, "货款结算"));
// 异步处理分账结果
wxPayService.profitSharingAsync(request);
}
4. 高并发优化实践
4.1 农产品秒杀设计
采用分级缓存策略:
- 预热阶段:提前1小时加载库存到Redis
bash复制
redis-cli> HSET seckill:product:123 stock 1000 - 扣减库存:Lua脚本保证原子性
lua复制local key = KEYS[1] local num = tonumber(ARGV[1]) if (tonumber(redis.call('HGET', key, 'stock')) >= num) then redis.call('HINCRBY', key, 'stock', -num) return 1 end return 0 - 最终一致性:通过RocketMQ异步同步数据库
4.2 消息队列选型
对比了三种方案后选择RocketMQ:
- Kafka:延迟低但配置复杂
- RabbitMQ:易用但吞吐量不足
- RocketMQ(最终选择):支持事务消息和顺序消息
订单创建消息示例:
java复制@GetMapping("/create")
public String createOrder(@RequestBody OrderDTO dto) {
// 1. 本地事务
Order order = orderService.create(dto);
// 2. 发送半消息
Message msg = new Message(
"ORDER_TOPIC",
"PAY_TIMEOUT",
order.getOrderNo().getBytes());
// 3. 事务监听器处理后续
transactionMQProducer.sendMessageInTransaction(msg, null);
return order.getOrderNo();
}
5. 运维监控要点
5.1 日志收集方案
农产品订单需要完整链路追踪,采用:
- ELK收集业务日志
- SkyWalking监控JVM
- 自定义日志标记(便于排查问题)
java复制MDC.put("traceId", UUID.randomUUID().toString()); MDC.put("shopId", TenantContext.getCurrentTenant());
5.2 灰度发布策略
为保证农户使用稳定性:
- 按商家ID分片发布
- 先发布非核心功能(如评价模块)
- 观察30分钟无异常再全量
Nginx配置示例:
nginx复制# 按商家ID路由到不同版本
map $http_x_shop_id $backend {
default v1;
"1001-2000" v2;
}
server {
location / {
proxy_pass http://$backend;
}
}
6. 踩坑实录
-
微信小程序图片域名限制:
- 必须备案域名
- 最多配置20个downloadFile域名
- 解决方案:使用统一图片服务域名,通过路径区分商家
-
农产品物流状态同步:
- 微信通知模板限制每天5000条
- 改用WebSocket实时推送(需处理iOS后台唤醒问题)
-
地域性缓存问题:
- 不同地区显示不同价格
- 使用GeoHash优化Redis缓存策略
java复制String geoKey = "price:" + GeoHash.encode(lat, lng); redisTemplate.opsForValue().set(geoKey, price);
这个项目让我深刻体会到:农业电商的技术方案必须"接地气"。比如我们为山区农户增加了离线订单功能(网络不稳定时先记录本地),以及用语音输入替代文字输入(很多农户不习惯打字)。技术人需要走出代码世界,真正理解行业痛点。
