ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SSM微信小程序商城源码二次开发:环境配平到支付闭环的实战指南

SSM微信小程序商城源码二次开发:环境配平到支付闭环的实战指南 简介JAVA微信小程序商城源码加完整后台是一套基于SpringMVC、MyBatis、Spring、Maven与MySQL构建的电商系统面向具备一定Java基础的中级开发者、小程序学习者及需要快速搭建商城的创业团队。资源涵盖商品发布、物流管理、评价系统、优惠券、运费计算、在线客服、在线支付、在线退款、微信管理等核心模块前端采用H5与CSS3后台基于Bootstrap-Ace框架前后台结构分明便于理解和二次开发。压缩包约20.5MB适合本地导入运行与源码研读可用于毕业设计、课程项目或商业项目起步。该资源已有5047人学习整体功能链完整能帮助读者掌握SSM框架整合、微信小程序接口对接以及电商后台常见业务逻辑的实现方式。1. 从零手写商城不如改一套SSM源码这套微信小程序商城到底藏着什么拿到这套源码时我刚接一个二手商城项目客户要的正是小程序端商品、下单、支付、退款、优惠券全套。从零写至少一个月改一套能跑的源码一周就能上线。这套源码技术栈是SpringMVC MyBatis Spring Maven MySQL小程序端是原生微信小程序管理后台用bootstrap-ace功能覆盖商品发布、物流管理、评价、优惠券、运费、在线客服、微信支付、退款、微信管理。适合三类人有Java基础想快速落地项目的开发者、拿商城当课程设计练手的学生、接私活要快速交付的独立开发者。下面按复现顺序把每一层的改造点拆开讲。2. 先把环境对齐JDK、Maven、MySQL、微信开发者工具的版本选择拿到源码第一件事不是打开IDE而是先把环境对齐。SSM是2015到2019年间的主流组合现在跑它最省事的方式是照着当年那套环境来而不是用最新版强行兼容。版本不对后面每一步都会冒出莫名其妙的报错。2.1 这套项目对JDK、Maven和Tomcat的真实要求pom.xml里maven-compiler-plugin通常配置的是1.8所以JDK直接用JDK 8不要贪新用17或21。强行上高版本会遇到两个问题一是javax.xml.bind类被移除跑单元测试直接NoClassDefFoundError二是项目里如果用了旧版Lombok和新JDK不兼容得升级Lombok依赖。没必要一开始就给自己加戏。Maven用3.6.3最省心。3.8以上的Maven对settings.xml里的镜像拦截规则改了如果沿用老配置可能会导致依赖下载失败。Tomcat用8.5就行对应Servlet 3.1规范SpringMVC在这上面跑多年了。手头是Tomcat 9问题也不大但要注意servlet-api的jar别和项目里打包的冲突。MySQL这块SQL文件导入时注意字符集建表语句一般是utf8mb4排序规则是utf8mb4_general_ciMySQL 5.7直接吃如果你用8.0要把JDBC驱动换成mysql-connector-java 8.x并在url里加上serverTimezoneAsia/Shanghai。组件推荐版本关键原因JDK1.8pom.xml编译目标为1.8避免javax.xml.bind缺失Maven3.6.3镜像源兼容性最好依赖拉取不折腾Tomcat8.5Servlet 3.1与SpringMVC配合成熟MySQL5.7直接兼容utf8mb4建表语句2.2 数据库初始化与JDBC配置修改数据库是整条链路最容易卡住的一步。SQL文件一般放在源码的sql目录下文件名常见shop.sql或mall.sql。用Navicat执行时注意一个坑如果SQL文件里带CREATE DATABASE语句而你已经连接了某个库整文件执行会报错。我一般先手工建一个空库再把SQL开头的CREATE DATABASE和USE语句删掉只执行表结构和INSERT。配置文件在src/main/resources目录下最常见名字是jdbc.properties内容大概是jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://127.0.0.1:3306/shop?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password123456注意最后一个password源码默认密码各式各样有人是root有人是123456还有人留空。这里不改对启动Tomcat后所有数据库操作都报Access denied。改完建议在Navicat里用同一组账号试连一次能连通再往下一步走。另一个容易忽略的是jdbc.url里的characterEncoding。如果SQL文件是utf8mb4建议把这里改成characterEncodingutf8mb4否则用户昵称或商品标题里的emoji入库全变问号。这问题在评价系统里尤其明显等上线后用户反馈过来再改又要更新连接池成本高一截。2.3 小程序的AppID配置与开发者工具导入小程序端目录通常是wxapp或miniprogram导入微信开发者工具时别选错目录。选“导入项目”把整个小程序目录选中。AppID这里有个分水岭只是看界面用测试号就行要跑通支付必须用已认证的正式小程序AppID否则wx.requestPayment直接被拒。导入之后第一件事是改config.js或app.js里的全局配置// app.js 片段 globalData: { // 小程序端所有请求都走这个baseUrl baseUrl: http://127.0.0.1:8080/shop-api }这里的baseUrl要和你后端Tomcat部署路径对起来。后端项目名如果是shopController的RequestMapping里带/api前缀那baseUrl就是http://127.0.0.1:8080/shop/api具体要看源码里URL前缀怎么拼。改完这两处在开发者工具里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”就能本地联调。注意这只是开发环境开关上线前必须关掉换成正式域名细节在第6章讲。3. 小程序端到后台的数据链路登录、商品、下单、支付全流程小程序商城核心链路就一条用户打开小程序 - 登录拿openid - 浏览商品 - 下订单 - 微信支付 - 后台发货 - 用户确认收货评价。这条链路跑通商城基本活了。3.1 登录态怎么建立wx.login换openid小程序登录不是传统账号密码而是用wx.login拿到临时code再把code发给后端后端拿code和AppID、AppSecret一起调微信jscode2session接口换回openid和session_key。openid是用户在微信体系里的唯一ID商城系统直接拿它对应用户表。小程序端代码一般是wx.login({ success(res) { if (res.code) { wx.request({ url: app.globalData.baseUrl /user/login, method: POST, data: { code: res.code }, success: (loginRes) { // token 后续所有请求都要带用于识别用户身份 wx.setStorageSync(token, loginRes.data.data.token) wx.setStorageSync(userInfo, loginRes.data.data.userInfo) } }) } } })这段代码要点code是一次性的有效期五分钟用完作废所以不要缓存code每次冷启动重新调wx.login。token是后端生成的可以用UUID也可以生成JWT。这套老项目大概率是UUID存在session表里。后端接口长这样RestController RequestMapping(/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody MapString, String params) { String code params.get(code); // 调微信接口常见做法是在WxService里封装 String openid wxService.code2Session(code); User user userService.findOrCreateByOpenid(openid); String token UUID.randomUUID().toString().replace(-, ); userService.saveToken(token, user.getId()); return Result.ok().put(token, token).put(userInfo, user); } }findOrCreateByOpenid是登录关键逻辑用户第一次来自动创建账号第二次直接查出来。不需要注册表单这是小程序商城的通行做法。token和user_id的关系存数据库表项目没引入Redis就存表token过期时间可以在表里加expire_time字段。3.2 商品列表接口参数、SQL、返回结构商品列表是必然要改的模块因为不同商城分类和筛选条件不一样。接口一般GetMapping(/goods/list) public Result list(RequestParam(defaultValue 0) Integer categoryId, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { PageHelper.startPage(page, size); ListGoods goodsList goodsService.getGoodsByCategory(categoryId); PageInfoGoods pageInfo new PageInfo(goodsList); return Result.ok().put(list, pageInfo.getList()).put(total, pageInfo.getTotal()); }PageHelper要看pom.xml里有没有引入pagehelper依赖没有的话就是用MyBatis手写分页。对应的Mapper XMLselect idselectGoodsByCategory resultTypecom.shop.entity.Goods SELECT id, name, main_image, price, original_price, stock, sales FROM t_goods where if testcategoryId ! null and categoryId ! 0 AND category_id #{categoryId} /if AND status 1 /where ORDER BY sort_order DESC /select这里status字段很关键。商品有上下架状态status1上架status0下架。很多人在后台点“上架”小程序端却不显示多半是后台商品管理的状态没存对或者前台SQL里没过滤status。这坑出现频率非常高所以改商品模块时先确认新商品status默认值是1。3.3 下单与微信支付预支付参数生成与回调验证支付是整个商城最核心、最容易出问题的环节。流程是小程序端提交订单 - 后端生成订单记录 - 后端调微信支付API生成预支付订单 - 返回prepay_id给前端 - 前端调wx.requestPayment - 用户输入密码 - 微信服务器回调后端接口通知支付结果。小程序端发起支付wx.request({ url: app.globalData.baseUrl /order/pay, method: POST, data: { orderId: orderId }, success(res) { const payParams res.data.data wx.requestPayment({ timeStamp: payParams.timeStamp, nonceStr: payParams.nonceStr, package: payParams.package, // 注意这个key是 package不是 packages signType: payParams.signType, paySign: payParams.paySign, success() { wx.showToast({ title: 支付成功 }) }, fail(err) { console.log(支付失败, err) } }) } })后端生成预支付订单public MapString, String createPrePayOrder(String openid, String orderNo, Integer totalFee) { // 微信支付参数 String appId wxConfig.getAppId(); String mchId wxConfig.getMchId(); // 常见做法是用WxJava库封装 WxPayUnifiedOrderRequest request new WxPayUnifiedOrderRequest(); request.setBody(商城订单- orderNo); request.setOutTradeNo(orderNo); request.setTotalFee(totalFee); // 单位是分不是元 request.setOpenid(openid); request.setTradeType(JSAPI); request.setSpbillCreateIp(123.12.12.12); // notifyUrl 必须和微信支付后台配置的完全一致 request.setNotifyUrl(wxConfig.getNotifyUrl()); WxPayUnifiedOrderResult result wxPayService.createOrder(request); // 用 result.getPrepayId() 生成前端需要的 paySign return payParams; }totalFee单位是分。这个坑几乎每个人都踩过订单表里存的是元比如99.00传给微信支付时忘了乘100结果用户只需支付99分钱。商家看了可能要骂人。我一般在金额取出时用BigDecimal乘100转成整数分再传给支付接口。支付回调是第二个高频坑。微信服务器支付成功后向notifyUrl发POST请求内容是XML。后端必须验签再返回指定XML串。如果返回格式不对微信会认为回调失败然后反复通知日志里就会出现重复处理同一订单的记录。3.4 退款与售后接口的调用边界退款接口和支付一样需要商户证书。微信支付退款API要求用商户API证书发起请求。常见项目里证书文件apiclient_cert.p12放在resources目录下wx.pay.certPath/data/cert/apiclient_cert.p12 wx.pay.certPwd商户号证书密码默认是商户号本身。退款金额不能大于实付金额后台退款表单提交时要校验。这个校验一旦漏掉用户买100块的商品申请退款200接口调用直接报错用户端看到异常提示后台日志又一团乱麻。退款成功之后还要把订单状态改成已退款优惠券如果已经核销要退回这两步要放在同一个事务里。4. 后台管理系统二次开发bootstrap-ace骨架与优惠券、运费改造后台管理是商城项目的运营门面也是每天都要碰的地方。这套源码后台用bootstrap-ace模板基于Bootstrap 3的Admin模板。左侧菜单、顶部导航、内容区三块构成。它的好处是随手拿一个HTML页面复制粘贴就能扩展出新功能页不需要复杂脚手架因为bootstrap-ace页面就是纯HTML加JS渲染配合jQuery和后端返回的JSON就能干活。4.1 bootstrap-ace后台的结构菜单、页面与权限打开后台首页会看到sidebar、navbar、main-content三块。新增一个菜单项要做两件事先改侧边栏HTML把菜单链接加上再在后端Controller加对应页面路由。li a hrefjavascript:void(0) classdropdown-toggle i classace-icon fa fa-tag/i span classmenu-text优惠券管理/span /a ul classsubmenu li a href/admin/coupon/list i classace-icon fa fa-caret-right/i 优惠券列表 /a /li /ul /li后台Controller方法一般是返回视图名RequestMapping(/coupon/list) public String couponList(Model model) { ListCoupon list couponService.getAll(); model.addAttribute(list, list); return admin/coupon_list; }注意返回值取决于ViewResolver前缀后缀配置。常见配置前缀是/WEB-INF/views/后缀.jsp那return的字符串会去对应目录找coupon_list.jsp。权限控制这层很多商城后台是简单处理登录后把用户ID和角色存session通过SpringMVC拦截器拦截/admin/开头的请求没登录就重定向到登录页。二次开发时新增页面只要放在/admin/路径下拦截器自动覆盖。我见过不少项目在这里栽过后台页面放在其他路径绕过拦截器直接访问数据裸奔。4.2 优惠券模块表结构、发放与核销逻辑优惠券看起来复杂核心就两张表。一张优惠券定义表一张用户优惠券表。CREATE TABLE t_coupon ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(100) COMMENT 优惠券名称, type tinyint(1) DEFAULT 1 COMMENT 1满减 2折扣, amount decimal(10,2) DEFAULT NULL COMMENT 减免金额/折扣, threshold decimal(10,2) DEFAULT NULL COMMENT 使用门槛金额, total_count int(11) DEFAULT 0 COMMENT 发行总量, receive_count int(11) DEFAULT 0 COMMENT 已领取数量, start_time datetime COMMENT 有效开始时间, end_time datetime COMMENT 有效结束时间, status tinyint(1) DEFAULT 1, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_user_coupon ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) COMMENT 用户ID, coupon_id int(11) COMMENT 优惠券ID, order_id int(11) DEFAULT NULL COMMENT 核销订单ID, status tinyint(1) DEFAULT 0 COMMENT 0未使用 1已使用 2过期, receive_time datetime COMMENT 领取时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;total_count配合receive_count判断券是否发完。用户端领取接口要两步先判断库存有没有发完再插入t_user_coupon。这两步中间有并发时会超发。常见做法是给t_coupon的receive_count做乐观锁比如UPDATE时带上旧值判断UPDATE t_coupon SET receive_count receive_count 1 WHERE id #{couponId} AND receive_count total_count影响行数为0说明被并发抢完了。核销逻辑在下单时用户勾选一张可用优惠券订单表冗余coupon_id和优惠券抵扣金额生成订单同时把t_user_coupon的status改为1并关联order_id。注意一个边界订单取消时用户优惠券要退回把status改回0、order_id置空。很多项目退款时只退了钱忘了退券用户投诉说优惠券消失。4.3 运费模板改造按区域、按件数、按重量组合运费系统是运营价值很高的模块。设计上一般有一张模板主表和一张模板明细表。CREATE TABLE t_freight_template ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) COMMENT 模板名称, type tinyint(1) DEFAULT 1 COMMENT 1按件 2按重量 3按金额, default_fee decimal(10,2) DEFAULT 0 COMMENT 首件/首重费用, default_unit decimal(10,2) DEFAULT 0 COMMENT 首件/首重数值, add_fee decimal(10,2) DEFAULT 0 COMMENT 续件/续重费用, add_unit decimal(10,2) DEFAULT 0 COMMENT 续件/续重数值, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;明细表用来存区域规则。比如“江浙沪首重8块续重2块其他地区首重10块续重5块”后台运费模板页面里每一行是一个区域规则。订单提交时后端拿收货地址省市区去明细表匹配区域找到对应首重续重按商品总重量计算。匹配不到就走默认模板。这里最大的坑是区域匹配的数据格式有的系统存省名称有的存行政区域代码。我比较推荐存区域代码比如浙江省是330000前端地址选择器用的正是这个编码匹配起来不折腾。如果源码里存的是名称改起来需要在后台字典表里维护编码和名称映射工作量会多出不少。4.4 物流管理和在线客服的接入方式物流管理模块源码对接的大概率是快递鸟或快递100这类第三方接口。常见配置kdniao.appId你的快递鸟AppId kdniao.appKey你的快递鸟AppKey kdniao.reqUrlhttps://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx在后台订单列表点“查看物流”时后端调第三方物流查询接口返回轨迹列表。注意物流查询接口一般有频率限制别在每次订单列表刷新时都调要把轨迹缓存到本地表比如缓存两小时。否则订单量一大账号被限流所有订单显示“无轨迹”运营那边会连环问为什么。在线客服这块源码里最常见实现有两种。一种是直接用微信原生客服能力小程序端放button按钮设置open-typecontact用户点击进入微信客服会话由绑定在微信公众平台的客服人员接待。第二种是后端接入微信客服消息接口把用户消息转发到自建后台客服在后台回复。源码里如果有“微信管理”菜单多半是AppID、AppSecret、客服URL配置放在一起。二次开发时我建议先用原生方案跑通低成本解决基础客服需求业务复杂了再上自建客服。5. 上线前最容易翻车的五个地方支付回调、物流查询、评价图片、客服会话这套源码我前后经手过两次每次联调阶段都被固定几个问题卡住有的是代码问题有的是配置问题。下面列五个最有代表性的按“现象、原因、解决”顺序讲。5.1 支付回调进不了Controller现象用户在小程序端支付成功钱也扣了但订单状态一直不变停留在“待支付”。原因notifyUrl填的不是外网可访问的HTTPS地址或者和微信支付后台配置不一致。微信支付规定回调地址必须是公网HTTPS而且不能带参数。本地联调用内网穿透但穿透域名证书不全微信服务器验签失败就会一直重试。解决先把notifyUrl改成微信支付后台配置的合法域名然后检查后端回调方法是不是没加ResponseBody导致返回了视图而非XML字符串最后回调方法里处理完业务要返回固定XMLRequestMapping(/pay/notify) ResponseBody public String notify(HttpServletRequest request, HttpServletResponse response) { // 先验签再处理业务 WxPayNotifyResult result wxPayService.parseOrderNotifyResult(request); // 幂等处理判断orderNo是否已经处理过 orderService.handlePayNotify(result.getOutTradeNo(), result.getTransactionId()); // 返回成功通知微信收到这个XML才停止重试 return xmlreturn_code![CDATA[SUCCESS]]/return_codereturn_msg![CDATA[OK]]/return_msg/xml; }我后来每次排查都是先看Tomcat日志里有没有微信服务器的POST请求进来有请求但报错就是代码问题没请求就是地址或证书问题。5.2 物流查询一直报“无轨迹信息”现象后台录入了快递单号小程序端点查看物流一直显示无轨迹。原因两种常见情况。一种是快递公司编码不对顺丰是SF圆通是YTO后台下拉框显示的是名称存库的必须是编码另一种是第三方接口的签名算法没对上请求直接被拒。解决在配置里打开第三方接口调试模式把请求数据和返回结果完整打出来核对快递公司编码。快递鸟申请时要填业务背景个人开发者可能被拒可以换快递100的免费接口参数更简单对个人开发者友好。另外服务商免费版一般只支持最近几个月的轨迹查询老订单查不到是正常现象前台提示文案要写清楚。5.3 评价图片base64入库导致数据库膨胀现象用户上传评价图片后只显示成功但数据库表瞬间多出几百KB高峰期MySQL压力上升订单接口变慢。原因小程序端上传图片时直接读文件转base64后端存进数据库text字段。图片在数据库里估值膨胀三成左右列表查询时如果SQL没有排除大字段每次都要把几百KB拖出来慢是必然的。解决图片上传走文件接口后端把文件存到服务器磁盘或OSS数据库里只存图片URL。文件上传接口PostMapping(/upload) public Result upload(RequestParam(file) MultipartFile file) { // 按日期分目录防止一个目录文件过多 String dir /data/upload/ LocalDate.now().toString(); File dirFile new File(dir); if (!dirFile.exists()) { dirFile.mkdirs(); } String fileName UUID.randomUUID().toString().replace(-, ) .jpg; file.transferTo(new File(dirFile, fileName)); String url https://你的域名/upload/ LocalDate.now() / fileName; return Result.ok().put(url, url); }数据库里只存URL列表查询接口轻很多JVM内存压力也小。从那以后我再没在业务表里见过图片大字段。5.4 在线客服会话接不进来现象线上用户点客服按钮没反应或提示“客服当前不在线”。原因微信原生客服功能需要在微信公众平台绑定客服人员客服人员要在小程序客服插件里保持登录。后台没有设置客服或客服没登录前端点contact就提示不在线。个人主体的小程序部分客服能力受限也会出现这个问题。解决先在微信公众平台小程序后台“客服”菜单添加客服人员然后让客服人员在微信里打开“小程序客服助手”保持在线最后在代码里检查按钮适配有的老版本基础库对contact支持不完整。5.5 access_token没缓存导致频繁报错现象分享海报、客服消息等功能偶尔好偶尔坏报错“invalid credential, access_token is invalid or not latest”。原因微信access_token有效期7200秒每天调用次数有限。很多源码把获取逻辑直接写在业务方法里每次都重新请求旧的还没过期就被新token覆盖其他还在用旧token的业务全部失效。解决做本地缓存。token存Redis或数据库key固定过期时间7200秒获取时先查缓存没有或过期再调微信接口public synchronized String getAccessToken() { String token cacheService.get(wx_access_token); if (token ! null) { return token; } // 调微信接口获取新token String newToken wxService.fetchAccessToken(); cacheService.set(wx_access_token, newToken, 7000); // 比7200少200秒留余量 return newToken; }用数据库做缓存时注意加锁防止多线程同时刷新。从那以后我所有微信项目都强制检查access_token缓存这一步。6. 把源码改造成自己的项目包名替换、打包部署、HTTPS校验一条龙6.1 全局包名替换三步走源码里的包名通常是com.shop或com.mall是上一手项目留下的痕迹。改包名不只是为了好看是避免以后别人一眼看出这是拿现成代码改的。先在文件管理器里复制整个项目一份再动不要改造原目录。然后分三步走第一步在IDEA里选中java根目录右键Refactor - Rename把所有包名改成你的域名反写第二步全局替换import语句和XML里出现的旧包名第三步改pom.xml里finalName和你自己的artifactId。# 全局替换示例适合在服务器上快速替换配置文件里的旧包名 grep -rl com.shop --include*.java --include*.xml . | xargs sed -i s/com.shop/com.yourdomain.mall/g6.2 Maven打包与Tomcat部署打包命令mvn clean package -DskipTests产物在target目录下是个war包。把它放进Tomcat的webapps目录启动Tomcat看日志。部署后验证三件事数据库连接是否通、启动日志有没有SQL异常、接口是否返回正常数据。curl -X GET http://127.0.0.1:8080/shop/api/goods/list?page1size10返回JSON数据说明后端正常。如果404或500先看catalina.out日志大部分问题是root路径和context-path不对齐。6.3 合法域名与HTTPS转发校验上线前最后一步在微信公众平台小程序后台把request合法域名配成正式域名。微信要求必须是HTTPS所以服务器要配SSL证书常见做法用Nginx做443端口转发到Tomcat的8080。这里有一个容易出事的点Nginx转发后支付回调路径变了。配置location规则时路径要完整透传不要重写。server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /data/ssl/yourdomain.pem; ssl_certificate_key /data/ssl/yourdomain.key; location /shop-api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; } }配置完用浏览器访问一下HTTPS地址确认证书有效再去小程序后台保存合法域名整个链路才算闭合。微信小程序商城这套源码说到底是一个能跑通的骨架真正值钱的是你在上面填的细节优惠券怎么防超发、图片上传怎么不拖垮数据库、支付回调怎么保证幂等。上面这些坑是我前后两次接手同类项目攒下来的血泪经验。从那以后我每次拿到这类源码项目都强制先走一遍环境对齐、数据链路梳理、避坑清单排查这三步不急着跑业务逻辑。按这套流程去复现这份源码你踩的坑会少一大半。希望帮到你。本文还有配套的精品资源点击获取
返回列表