1. 项目背景与核心价值
汽车后市场规模近年来持续扩大,数据显示2023年国内汽车用品市场规模已突破1.2万亿元。传统线下门店和简单电商平台在商品管理、用户服务等方面存在明显短板:商品信息更新滞后、库存同步困难、用户行为数据缺失等问题制约着业务发展。这正是我们开发这套微信小程序销售系统的核心驱动力。
这个系统最显著的特点是"双端协同"架构:
- 微信小程序端提供轻量级用户交互界面
- SpringBoot后端实现高并发业务处理
- MySQL数据库确保交易数据可靠性
在实际测试中,系统单节点QPS可达800+,订单处理延迟控制在200ms以内,完全满足中型汽配商城的性能需求。相比传统PHP架构,采用SpringBoot带来的优势尤为明显:
- 内置Tomcat容器简化部署
- Starter依赖自动配置减少70%的XML配置
- Actuator端点提供完善的系统监控
提示:MySQL必须使用5.7版本,因为该系统使用了JSON字段存储商品扩展属性,这是MySQL5.7开始支持的特性
2. 技术架构深度解析
2.1 整体架构设计
系统采用经典的三层架构,但针对移动电商特点做了特殊优化:
code复制[微信小程序] <-HTTPS-> [SpringBoot REST API] <-MyBatis-> [MySQL]
│ │
└──微信支付SDK └──Redis缓存
关键设计决策:
- 小程序与后端完全解耦,通过HTTPS传输JSON数据
- 采用JWT进行无状态认证,避免Session存储开销
- 商品数据使用多级缓存策略:
- 一级:本地Caffeine缓存(有效期5分钟)
- 二级:Redis集群(有效期30分钟)
2.2 数据库设计要点
商品核心表结构设计值得特别说明:
sql复制CREATE TABLE `product` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`sku_code` VARCHAR(32) NOT NULL COMMENT '国际条形码',
`name` VARCHAR(100) NOT NULL,
`category_id` INT NOT NULL COMMENT '外键关联分类表',
`spec_json` JSON DEFAULT NULL COMMENT '存储动态规格参数',
`price` DECIMAL(10,2) NOT NULL,
`stock` INT NOT NULL DEFAULT 0,
`is_hot` TINYINT(1) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_sku` (`sku_code`),
KEY `idx_category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创新点在于:
- 使用JSON类型存储动态规格(如机油粘度、滤芯尺寸等)
- SKU编码唯一索引确保商品唯一性
- 分类索引加速类目页查询
3. 核心功能实现细节
3.1 微信登录集成
小程序端调用wx.login获取code,后端通过此code与微信接口服务交换openid:
java复制public String wechatLogin(String code) throws WechatException {
// 构造请求URL
String url = "https://api.weixin.qq.com/sns/jscode2session?appid="
+ appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
// 发送HTTP请求
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
WechatSessionResponse session = objectMapper.readValue(response.getBody(), WechatSessionResponse.class);
if (session.getErrcode() != null) {
throw new WechatException(session.getErrmsg());
}
// 生成JWT令牌
return Jwts.builder()
.setSubject(session.getOpenid())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
注意:务必在微信公众平台配置合法域名,否则会出现跨域错误。建议将access_token存入Redis并设置110分钟过期(微信token有效期为120分钟)
3.2 高并发库存管理
解决超卖问题的核心方案:
java复制@Transactional
public boolean reduceStock(Long productId, int quantity) {
// 使用悲观锁确保数据一致性
Product product = productMapper.selectForUpdate(productId);
if (product.getStock() < quantity) {
return false;
}
// 采用CAS机制更新
int affected = productMapper.updateStock(productId, product.getVersion(), quantity);
return affected > 0;
}
对应的Mapper XML实现:
xml复制<update id="updateStock">
UPDATE product
SET stock = stock - #{quantity},
version = version + 1
WHERE id = #{productId}
AND version = #{version}
AND stock >= #{quantity}
</update>
实际测试表明,这种方案在100并发下仍能保证数据一致性,相比简单的UPDATE语句更可靠。
4. 支付系统关键技术
4.1 微信支付对接
支付流程的关键步骤:
- 小程序调用wx.requestPayment发起支付
- 后端生成预支付订单:
java复制public PrepayResponse createPrepayOrder(Order order) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("body", order.getProductName());
params.put("out_trade_no", order.getOrderNo());
params.put("total_fee", String.valueOf(order.getAmount().multiply(new BigDecimal(100)).intValue()));
params.put("openid", order.getUserOpenid());
params.put("trade_type", "JSAPI");
// 调用微信统一下单接口
WXPay wxPay = new WXPay(config);
Map<String, String> result = wxPay.unifiedOrder(params);
// 生成小程序支付参数
PrepayResponse response = new PrepayResponse();
response.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
response.setNonceStr(RandomStringUtils.randomAlphanumeric(32));
response.setPackage("prepay_id=" + result.get("prepay_id"));
response.setSignType("MD5");
// 计算签名
String sign = generateSignature(response);
response.setPaySign(sign);
return response;
}
4.2 支付结果异步通知
微信支付结果通知处理要点:
java复制@PostMapping("/notify")
public String paymentNotify(HttpServletRequest request) {
// 验证签名
if (!wxPay.isPayResultNotifySignatureValid(request)) {
return "FAIL";
}
// 解析通知内容
String orderNo = request.getParameter("out_trade_no");
String transactionId = request.getParameter("transaction_id");
// 处理订单状态
orderService.handlePaymentSuccess(orderNo, transactionId);
return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
}
重要:必须实现幂等性处理,同一笔订单可能收到多次通知。建议在数据库记录transaction_id避免重复处理
5. 性能优化实践
5.1 缓存策略设计
商品详情采用分级缓存:
java复制public Product getProductWithCache(Long id) {
// 一级缓存查询
Product product = caffeineCache.get(id);
if (product != null) {
return product;
}
// 二级缓存查询
String redisKey = "product:" + id;
String json = redisTemplate.opsForValue().get(redisKey);
if (json != null) {
product = objectMapper.readValue(json, Product.class);
caffeineCache.put(id, product); // 回填一级缓存
return product;
}
// 数据库查询
product = productMapper.selectById(id);
if (product != null) {
String productJson = objectMapper.writeValueAsString(product);
redisTemplate.opsForValue().set(redisKey, productJson, 30, TimeUnit.MINUTES);
caffeineCache.put(id, product);
}
return product;
}
5.2 SQL优化案例
商品搜索接口的优化过程:
原始方案:
sql复制SELECT * FROM product
WHERE name LIKE '%机油%'
OR description LIKE '%机油%'
优化后方案:
sql复制SELECT p.* FROM product p
JOIN product_search ps ON p.id = ps.product_id
WHERE MATCH(ps.keywords) AGAINST('机油' IN BOOLEAN MODE)
配合新建的搜索索引表:
sql复制CREATE TABLE `product_search` (
`product_id` BIGINT NOT NULL,
`keywords` TEXT NOT NULL,
FULLTEXT KEY `ft_idx` (`keywords`),
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB;
实测表明,在10万商品数据量下,搜索性能提升约40倍。
6. 部署与监控
6.1 生产环境部署
推荐使用Docker Compose部署:
yaml复制version: '3'
services:
app:
image: openjdk:8-jre
ports:
- "8080:8080"
volumes:
- ./app.jar:/app.jar
command: java -jar /app.jar
depends_on:
- redis
- mysql
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- redis_data:/data
mysql:
image: mysql:5.7
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: yourpassword
volumes:
- mysql_data:/var/lib/mysql
volumes:
redis_data:
mysql_data:
6.2 监控配置
SpringBoot Actuator关键配置:
properties复制# application.properties
management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.metrics.export.prometheus.enabled=true
management.endpoint.health.show-details=always
配合Grafana仪表板可以监控:
- JVM内存使用
- 接口响应时间
- 数据库连接池状态
- 缓存命中率
7. 踩坑经验分享
-
微信支付证书问题:
- 错误现象:调用支付接口返回"证书验证失败"
- 原因:服务器时间与网络时间不同步
- 解决方案:安装NTP服务定期同步时间
-
MyBatis缓存污染:
- 现象:更新商品后查询仍返回旧数据
- 原因:二级缓存未正确清除
- 修复:在更新方法添加
@CacheEvict注解
-
小程序图片加载慢:
- 优化前:直接使用原图,平均加载时间2.8s
- 优化方案:
- 使用腾讯云图片压缩(?imageView2/2/w/500)
- 实现懒加载
- 结果:平均加载时间降至0.6s
-
库存超卖问题:
- 初始方案:简单UPDATE语句
- 问题:高并发下仍会出现超卖
- 最终方案:版本号+悲观锁双重保障
这套系统在上线后经受住了618大促的考验,单日处理订单量突破1.2万笔。最大的收获是认识到分布式环境下数据一致性的复杂性,后续计划引入分布式锁进一步优化秒杀场景。