ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java微信小程序商城实战:从架构到支付回调的完整指南

Java微信小程序商城实战:从架构到支付回调的完整指南 简介一份基于Java与微信小程序构建的移动购物商城项目面向具备Java基础或小程序入门经验的开发者用于学习电商系统前后端整合与完整业务流程。压缩包共98个文件包括JavaScript逻辑文件、WXML结构文件、WXSS样式文件、JSON配置文件以及PNG/JPG/GIF图片素材整体仅138KB目录按页面、组件、工具模块划分层级清晰便于快速定位与二次开发。预览显示包含登录、首页、订单、购物车、地址、会员等核心页面覆盖商品浏览、购物车操作、订单提交与支付等关键环节也涉及用户鉴权与数据绑定结合Spring Boot风格的后端接口与JWT身份认证思路可帮助理解真实商城的数据交互与状态管理。已有1431人学习下载适合作为课程设计、毕业设计或自学实战的参考模板既能补全前端页面交互也能对照后端接口梳理电商业务逻辑。1. 为什么说商城系统里最难的不是页面很多团队第一次做“基于JAVA开发的微信小程序购物商城”时把八成精力放在了页面上结果交付时才发现真正决定能不能上线的是另外两件事订单在并发下有没有错乱、支付回调到底会不会丢单。这个标题拆开看是一个完整的前后端闭环微信小程序负责展示和交互Java 后端负责账号、商品、订单与支付二者通过 HTTPS 接口衔接。对中小团队、外包交付以及准备拿它当作品集的 Java 工程师这套东西的难点不在 Demo 跑通而在边界库存、状态机、回调幂等。下面按我实际做项目的顺序从技术选型、小程序端链路、Java 后端设计到上线前的踩坑把能复现的细节都摊开讲。2. 先把架构框住为什么这套商城选择 Java 后端2.1 技术栈怎么定Spring Boot MyBatis-Plus 的取舍做微信小程序购物商城前端基本没得选原生小程序或者 uni-app后端技术栈选择余地就大了Node、Python、Go 都能做。可一旦落到真实的商城业务我会优先选 Java准确说是 Spring Boot MyBatis-Plus。理由并不玄学首先是生态成熟。商城涉及微信支付、分账、优惠券、物流接口几乎每个环节都有现成的 Java SDK 和踩坑文档遇到问题搜 “java 微信支付回调” 能找到大量真实案例这在项目排期紧张时不只省事还省人。其次是招人好招Java 工程师供给量大哪怕是临时接手的外包团队也能快速补位不至于一个核心开发请假整个项目停摆。MyBatis-Plus 相较 Hibernate 的优势在于 SQL 可控。商城商品列表的查询条件特别多分类、价格区间、销量排序、库存过滤用 Hibernate 的 Criteria 写出来可读性很差而且容易出现 n1 查询——列表页带了 50 个商品每个商品又去查 3 张关联表一个接口就变成几十条 SQL。MyBatis-Plus 的 Wrapper 条件构造器在处理这种场景时直观得多复杂查询还能直接手写 XML怎么查、查几行、怎么连表都由开发者掌握。我一般会把工程按下面的目录组织src/main/java/com/example/mall/ ├── controller/ # HTTP 入口只做参数校验和结果包装 ├── service/ # 业务逻辑所有事务都写在这一层 ├── mapper/ # MyBatis-Plus 的 Mapper 接口 ├── entity/ # 数据表对应的实体类 ├── config/ # 小程序配置、分页插件、Redis 客户端 └── utils/ # JWT、微信签名、金额处理工具 src/main/resources/ ├── application.yml # 数据源与 Redis 配置 └── mapper/ # XML 里的复杂 SQL这个分层的核心原则是 controller 保持轻薄业务逻辑收拢在 service。很多新手把 SQL 写在 controller 里接口一多就变成一锅粥后面加一个字段要改三处地方。实体类不要直接返回给前端而是用 VO视图对象做字段裁剪避免把数据库字段泄露到响应里也方便屏蔽敏感信息。2.2 单体优先商城在什么阶段才需要拆微服务接触过很多打着“微服务架构”旗号的商城项目点进去一看订单服务、用户服务、商品服务各自独立部署但业务上并没有那么大的流量反而被跨服务事务折磨得不轻。我自己的经验是商城未必要从第一天就拆微服务单体优先是一条更务实的路线。单体阶段一个 Spring Boot 应用把用户、商品、订单、购物车都装下事务边界就是一个Transactional注解的事。用户下单整个流程——校验库存、扣减库存、创建订单、清空购物车——在同一个事务里任何一步失败都能完整回滚这在鱼和熊掌问题上是最简单可靠的解法。什么时候才值得拆当团队规模变大、部署频率互相牵制或者某个模块的流量明显高于其他模块时再动手。最常见的一个拆机信号是订单和商品模块需要独立扩缩容比如大促时订单模块需要 20 个节点而商品模块 3 个节点就够了。到了这个阶段优先拆出独立的订单服务同时保留用户和商品在一起都可以。做单体和微服务对比时可以参考这几个维度对比维度单体优先一上来就微服务事务处理本地事务回滚简单分布式事务需引入 Seata 或消息对账调试排错一个进程日志集中跨服务链路需要接链路追踪部署成本一个 Jar 包多服务构建、配置中心、网关团队协作代码冲突相对集中服务边界清晰但沟通成本高需要注意单体不代表代码可以乱写。模块之间要用 Java 包名做物理隔离比如com.example.mall.order和com.example.mall.user禁止跨模块直接调用对方的 Mapper。这样将来拆微服务时把包挪出去就是天然的实现类不用推倒重来。2.3 小程序端选原生还是 uni-app一次打包对比后的取舍小程序端的两条路子我用同一个登录功能分别踩过一遍结论很明确购物商城这种强依赖微信原生能力的项目用原生小程序开发是更省心的选择。原生开发在微信开发者工具里调试登录、支付、手机号验证这些能力都是直接调官方接口所见即所得。uni-app 的优势是“一套代码多端复用”如果团队同时要出支付宝小程序和抖音小程序它能省不少工作量。但在购物商城里两个端的能力差异会把你拖回现实微信端的 getPhoneNumber 返回的是动态令牌支付宝端返回的是不一样的结构支付回调的报文格式也不相同所谓“一套代码多端复用”真到了业务层七成代码还是得写 if-else 分支。从工程角度看原生小程序还有一个好处跟随微信的更新节奏。每次微信公布新的接口能力原生模板能立刻用上uni-app 则需要等框架作者适配插件。购物商城对微信支付、分享、客服消息的依赖度极高这种“慢半拍”在版本迭代时很掉节奏。所以我最终的方案是小程序端用原生开发Java 后端只维护一套 API同时预留支付宝端的适配层。如果以后要加支付宝小程序后端接口不用动前端再加一套页面即可。3. 小程序端三大链路登录、导航栏、列表加载3.1 微信小程序登录获取手机号code 换 token 的正确顺序购物商城的小程序端登录链条比普通网页登录多两个环节先要拿wx.login返回的 code 去后端换业务 token再按需获取用户手机号。很多新手把手机号获取和登录混在一起做用户还没看到商品首页就被拦在授权弹窗前流失率高得离谱。正确的做法是页面加载时先用wx.login静默登录让用户先逛起来等到下单或核销这类需要手机号的环节再触发手机号授权。这套顺序对转化率的影响非常大商城里手机号不是必需品商品浏览和加购都不需要它。小程序的登录页核心代码是// pages/login/login.js const login () { // 1. 获取微信登录凭证 code有效期 5 分钟且只能使用一次 wx.login({ success: async (res) { if (!res.code) return // 2. 把 code 交给后端后端通过接口换取 openid 并签发业务 token const { token } await request.post(/api/auth/code2session, { code: res.code }) // 3. 保存登录态后续请求统一在 header 里带上 Authorization wx.setStorageSync(token, token) } }) } // 手机号快速验证组件必须用户主动点击 button 才能触发 const onGetPhoneNumber (e) { if (e.detail.errMsg ! getPhoneNumber:ok) return // 把一次性动态令牌交给后端由后端换取真实手机号 request.post(/api/auth/phone, { phoneCode: e.detail.code }) }这段代码要特别注意两点wx.login返回的 code 只能用一次后端换完 openid 后这个 code 立即失效所以登录逻辑要统一在一个入口里管理不能既在启动页调一次又在 tabBar 页调一次手机号授权这里拿到的是e.detail.code不是手机号明文后端要用 appid 和 secret 去微信接口换取真实手机号这个换号动作绝不能放在小程序端做否则 secret 会被扒光。对应的 Java 后端接口长这样RestController RequestMapping(/api/auth) public class AuthController { private final AuthService authService; PostMapping(/code2session) public ResultString code2session(RequestBody MapString, String req) { // 1. 调用微信接口用 code 换取 openid 和 session_key // 2. session_key 只保存在服务端不下发给小程序 String openid authService.code2Session(req.get(code)); // 3. 用 openid 查找用户不存在则先注册 String token authService.loginOrRegister(openid); return Result.ok(token); } PostMapping(/phone) public ResultString bindPhone(RequestBody MapString, String req) { // phoneCode 换手机号同时校验手机号是否已被其他账号绑定 String phone authService.getPhoneByCode(req.get(phoneCode)); authService.bindPhone(phone); return Result.ok(); } }这里的核心原则是openid 是用户的唯一身份标识手机号只是附属信息。session_key 牵扯到后续的加密数据解密必须留在服务端绝不能通过接口返回给小程序。3.2 自定义顶部导航栏高度适配全面屏的通用写法购物商城为了品牌感一般不会用微信默认导航栏而是通过配置navigationStyle: custom把导航栏区域变成页面的一部分。这个页面自定义导航栏一开问题就来了不同机型的顶部安全区域不一样胶囊按钮的位置也不一样写死一个高度必然在某台机器上翻车。我调试出来的通用方案是取胶囊按钮的位置反推出导航栏高度。胶囊按钮是微信官方渲染的它的位置能反映当前机型的安全区域和状态栏高度利用它做相对定位兼容性最好。核心代码如下// utils/navbar.js const getNavBarHeight () { const system wx.getSystemInfoSync() const capsule wx.getMenuButtonBoundingClientRect() // 胶囊顶部到状态栏底部的距离上下留白保持一致 const gap capsule.top - system.statusBarHeight // 导航栏高度 状态栏高度 胶囊高度 上下两段留白 const navBarHeight system.statusBarHeight capsule.height gap * 2 return { statusBarHeight: system.statusBarHeight, navBarHeight: navBarHeight, menuButton: capsule } }这段逻辑里最关键的参数是capsule.top和system.statusBarHeight的差值。这个差值就是胶囊按钮上方留白把它镜像到胶囊下方就能保证导航栏内容垂直居中且视觉平衡。在 iPhone 上状态栏高度一般是 44 或 47Android 系统从 20 到 35 不等写死任何一个都会偏移。还有两个边界情况要留意iPad 上getMenuButtonBoundingClientRect()返回的宽度和高度与手机不同单个页面如果开启了横屏导航栏高度要按横屏重新计算。另外在页面 JSON 里设置custom后页面的safe-area适配不会自动生效底部如果也要自定义记得给底部留出env(safe-area-inset-bottom)的空间。3.3 页面列表加载更多触底事件与分页参数的配合商品列表和订单列表都有一个绕不开的交互页面列表加载更多。这个在微信小程序页面列表加载更多里最常见的翻车姿势是把整个列表一次性查出来渲染。数据量小的时候看不出来等到商品几百个时会明显卡顿真机上的表现就是滚到列表底部卡一下。分页加载的标准实现是依靠小程序的onReachBottom触底事件配合加载状态和结束状态// pages/goods/list.js let pageNo 1 let loading false let finished false onReachBottom() { // 防止触底事件在请求未返回时反复触发 if (loading || finished) return loading true request.get(/api/goods/list, { pageNo: pageNo, pageSize: 10 }).then((res) { this.setData({ goodsList: this.data.goodsList.concat(res.list) }) pageNo finished res.list.length 10 }).finally(() { loading false }) }这段代码里最重要的变量是loading。小程序端的触底事件触发非常频繁特别是快速滑动时如果不在请求期间加锁会出现同一个接口同时发出去好几个请求数据一样的重复渲染。等接口返回后把loading置回false下一次触底才能继续加载。finished的判断用的是“返回行数小于 pageSize”而不是“等于之后再去查一次”。用返回行数判断能省一次多余请求同时也能兼容最后一页不足 10 条的情况。分页参数我用pageNo/pageSize而不用page/size因为在 MyBatis-Plus 里Page对象默认就是current和size后端少做一层字段转换。上拉加载还有个体验细节onReachBottom默认触底距离是 50px如果页面底部有 tabBar 的遮挡要把触底距离调大否则用户还没看到列表完全展开就已经在加载了。4. Java 后端业务设计订单状态机、库存、支付回调4.1 订单状态机用枚举管住 6 个状态和 5 次流转购物商城的订单状态看似简单实际是后端最容易写乱的地方。常见的脏写法是在业务代码里直接写魔法数if (order.status 1)一个数字代表什么全靠记忆后面接手的人根本不敢动。我一般会用枚举把状态界清晰定义出来并在枚举里附上状态描述和允许流转的映射public enum OrderStatus { WAIT_PAY(0, 待支付), PAID(10, 已支付), WAIT_SHIP(20, 待发货), SHIPPED(30, 待收货), FINISHED(40, 已完成), CANCELED(50, 已取消); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } // 判断状态是否可以流转到目标状态 public boolean canTransferTo(OrderStatus target) { switch (this) { case WAIT_PAY: return target PAID || target CANCELED; case PAID: return target WAIT_SHIP || target CANCELED; case WAIT_SHIP: return target SHIPPED; case SHIPPED: return target FINISHED; default: return false; } } }这里用数字编码而不是连续数字是故意留出扩展位。比如以后要加“售后中”或者“退款中”状态可以在 40 和 50 之间插一个 45不用改动其他状态编码对已落库的订单数据也友好。流转规则集中在一个canTransferTo方法里任何一处的非法状态流转都会被拦在这一层。实际的状态流转路径是当前状态可流转到的状态触发动作待支付已支付 / 已取消支付回调 / 用户取消或超时已支付待发货 / 已取消商家发货 / 商家取消退款待发货待收货商家发货待收货已完成用户确认收货已完成无终态已取消无终态在service层做状态变更时我会加一层乐观锁控制更新语句带上WHERE status #{oldStatus}影响行数为 0 就说明状态已被其他请求改过直接提示“订单状态已更新请刷新后再试”。这个做法能防止用户连续点击按钮带来的重复提交问题。4.2 库存扣减与超卖条件更新与锁边界商城里最经典的并发问题就是超卖商品只剩 10 件100 个人同时下单结果卖出去了 120 件。一个不成熟的方案是先查库存再判断扣减这在并发下必然出问题两次请求同时读到剩余 10 件都认为可以扣于是双双成功。正确的扣减逻辑是在数据库层面做条件更新-- 库存充足才扣减影响行数 0 表示库存不足 UPDATE goods_stock SET stock stock - #{count} WHERE goods_id #{goodsId} AND stock #{count}配合的 Java 代码如下Service public class StockService { private final StockMapper stockMapper; Transactional(rollbackFor Exception.class) public void deduct(Long goodsId, Integer count) { // 条件更新行数 0 说明库存不足或商品不存在 int rows stockMapper.deduct(goodsId, count); if (rows 0) { throw new BizException(库存不足); } } }这个方案的精髓在于stock #{count}这个条件。它把“检查库存”和“扣减库存”合并成一条原子 SQL数据库的行锁天然保证同一时刻只有一个请求能成功扣减其余请求要么等锁要么直接行数为 0。比起先 SELECT 再 UPDATE 的两步操作这里根本不存在窗口期。如果碰到秒杀场景单个商品的并发远高于普通下单我会在扣减库存之前再加一道 Redis 预扣逻辑先在 Redis 里用DECR预扣库存预扣成功后再走数据库扣减失败直接返回“已售罄”。这样数据库层承受的压力被削掉了一大截Redis 单线程的原子性在这里刚好派上用场。这里还有一个容易忽略的坑用户下单后取消订单库存要回补。回补操作同样要用条件更新不能直接stock stock #{count}而是要给库存表加一个锁定字段或者用乐观锁版本号防止重复回补。同一个订单号只能回补一次最稳妥的办法是订单表加一个“是否已回补库存”的标记位在回补事务里一起更新。4.3 支付回调验签、幂等与异步处理微信支付回调是所有商城项目里最考验细节的地方。支付平台会把支付结果以异步通知的形式 POST 到你配置的回调地址这个通知最多会重试 24 小时直到你的服务返回约定报文。如果回调处理不规范轻则重复发货重则订单状态错乱。一个规范的回调接口长这样PostMapping(/api/pay/wx/notify) public String wxNotify(RequestBody String xmlBody) { // 1. 验签不通过直接拒绝避免伪造回调 if (!WxPayUtil.verifySign(xmlBody)) { return FAIL; } // 2. 解析本地订单号作为幂等键 String outTradeNo WxPayUtil.parseOutTradeNo(xmlBody); Order order orderService.getByOutTradeNo(outTradeNo); // 3. 已经处理过的订单直接返回成功不重复改状态 if (order.getStatus() OrderStatus.PAID.getCode()) { return SUCCESS; } // 4. 校验金额防止订单金额与支付金额不一致 Integer paidAmount WxPayUtil.parseAmount(xmlBody); if (!order.getAmount().equals(paidAmount)) { return FAIL; } // 5. 更新订单状态 扣减库存放在同一个事务里 orderService.paidByCallback(outTradeNo, paidAmount); return SUCCESS; }这个接口里有三个关键决策。第一是验签支付回调的报文带有支付平台的签名必须验证通过才继续处理否则任何人都能伪造一个“已支付”通知骗过后端。第二是幂等用本地订单号out_trade_no做幂等键同一笔订单被平台重复通知时第二次进来发现状态已经是已支付直接返回成功即可不做重复业务。第三是事务边界订单状态更新和库存扣减必须在同一事务里否则可能出现支付成功但库存没扣的脏数据。还有一层处理要放在回调之外回调接口的响应速度很重要平台要求快速返回约定的成功报文否则会判定为失败然后反复重推。所以回调里不要做发送短信、推送通知这类耗时操作把它们丢进消息队列或者定时任务中去处理。我在项目里习惯用 Spring 的Async把通知动作拆出去回调接口只做最少必要操作整个接口控制在 200 毫秒内响应完。5. 避坑记录商城上线前最容易翻车的 5 个实际问题这一章记的全是我陪跑过的商城项目里反复出现的坑每个都能复现也都给出了修复路径。踩坑不可怕同样的坑踩两次才亏。踩坑一登录态反复失效用户刚进商城就被弹出页面现象用户从商品页进入购物车页自动登录明明成功了过几分钟又提示登录过期退出登录再重新登录后端报错code been used。原因wx.login返回的 code 只能用一次而且同一时间只能发起一个登录请求。很多开发在启动页调一次登录、tabBar 页面又调一次两个请求拿着同一份 code 去换 token后发的那次必然失败。还有一种情况是全局封装的请求库在 401 时自动重试登录重复调用了wx.login。解决登录动作收敛成一个单例模块全局只保留一个登录入口。请求发起前先检查wx.getStorageSync(token)存在且未过期就直接用不重复走wx.login。后端给 token 设置合理的有效期商城场景建议 7 天用户在活跃期内不被打扰。踩坑二手机号快速验证组件从免费变收费体量上来后成本失控现象测试阶段手机号授权一切正常上线一周后接到财务通知云账号里多出一笔手机号验证费用单个还没多少钱一天几千次授权就很肉疼。原因平台把手机号快速验证组件调整成了按次计费的模式用户每点一次授权弹窗都会产生一次调用费用。很多商城把手机号授权放在登录第一步所有用户无差别触发成本就这么涨上去了。解决登录主流程改成 openid 业务 token手机号变成可选绑定项。只有在下单核销、申请售后、账号换绑这类必须要联系用户的环节才触发手机号验证。如果预算紧张也可以用表单填写 短信验证码的方式做兜底把套餐打包进营销预算里成本更可控。这是我做了几个商城后最想提前告诉你的一个坑。踩坑三图片全是裂图开发者工具正常真机全挂现象开发者工具里商品图片显示得好好的预览到真机上全部变成灰块点击加载也出不来。原因小程序对网络图片校验了域名白名单。开发者工具默认勾选了“不校验合法域名”选项掩盖了这个问题真机上则严格执行白名单策略不在白名单里的图片域名一律拒绝加载。解决在微信公众平台配置后台的downloadFile合法域名把图片服务的地址加进去。要注意图片服务如果有重定向最终落地域名也要在白名单里否则还是被拦。上线前把开发者工具的“不校验合法域名”关掉用体验版做整体回归把这类问题在测试阶段就暴露出来。踩坑四支付回调一直报失败订单被反复推送几小时现象日志里同一个回调地址每隔几分钟就被支付平台重推一次持续几个小时后才停导致 order 表中部分已支付订单被重复处理开发者反复排查却找不到原因。原因回调接口没有按平台约定的格式返回成功报文。常见两种错误一种是返回了 JSON 对象而不是约定的成功字符串另一种是接口处理时间过长超过平台等待阈值被误判为失败。只要返回的内容或超时不达标平台就会判定本次通知失败进入重试队列。解决回调接口严格按照平台报文要求的格式原样返回成功标识不要包一层自定义 JSON。同时把回调逻辑瘦身验签、查单、改状态三步走完就立即返回耗时操作全部丢到异步线程里。这样既满足响应速度又不影响后续业务执行是根治反复推送的办法。踩坑五自定义导航栏真机偏移华为和 iPhone 不一样现象自定义导航栏在开发工具里用 iPhone X 模拟正常真机测试时部分 Android 机器上的返回按钮位置偏上或偏下标题也歪了。原因不同机型的statusBarHeight返回值差异较大写死一个高度只能适配一种机型。胶囊按钮的位置也随微信版本和机型变化用固定的44或64这类数字去适配必然出问题。解决回到 3.2 节的方案用getMenuButtonBoundingClientRect()动态计算导航栏高度运行期根据胶囊位置实时算出安全间距。上线前准备一套真机测试矩阵至少覆盖 iPhone 主流机型、一台 Android 全面屏、一台 iPad确认后再提审。这个坑属于典型的“开发工具骗了你真机才是真相”。6. 把商城接口响应从两秒压到 700 毫秒缓存预热与耗时排查前一天还觉得响应挺快第二天用户反馈商品列表转圈圈打开日志一看列表接口平均耗时 1.8 秒。这不是接口本身写了什么慢 SQL而是商品表到了几千条数据后全表扫描叠加连表查询延迟就这么上来了。做性能优化我一般先做减法从三个老生常谈的元凶看起一是商品列表接口一次性查了全量字段连商品详情富文本都塞进了列表响应二是图片用了原图没有走裁剪缩略图三是热门商品没有缓存每次请求都打到数据库。这三件事做完响应时间普遍能降到 800 毫秒以内。缓存是商城性能优化的核心手段但缓存空窗期的问题更值得花心思。常见的做法是在 Spring Boot 启动时做一次缓存预热把热门商品提前加载到 Redis 里而不是等用户第一次访问时才去建缓存Component public class GoodsCacheWarmer { private final GoodsService goodsService; private final StringRedisTemplate redisTemplate; PostConstruct public void warmUp() { // 启动时加载前 20 个热门商品序列化后写入 Redis ListGoodsVO hotGoods goodsService.listHotGoods(20); String json JSON.toJSONString(hotGoods); redisTemplate.opsForValue() .set(mall:goods:hot, json, 30, TimeUnit.MINUTES); } }这段代码里几个参数按业务量级来调热门商品条数我一般取 20 到 100超过这个量级序列化和存储成本都划不来过期时间 30 分钟是比较折中的选择既能保证数据相对新鲜又能避免缓存雪崩时大批 key 同时失效。预热逻辑还可以用定时任务在每天零点和促销开始前再刷一次避免活动刚上线时缓存里还是旧数据。做完缓存后接口性能通常能有个明显的改善。这里附一张我在性能排查时常用的对照表排查点常见脏写法推荐做法列表查询查全部字段只查列表页需要的字段富文本单独接口图片加载直接返回原图 URL接入图片处理服务按尺寸裁剪热门商品每次请求查数据库Redis 预热 30 分钟过期列表分页一次查 500 条pageSize 10-20配合触底加载最后一个我做过很多次才回头的教训缓存没命中时不要直接查库要学会用互斥锁做缓存重建。否则热点商品在缓存过期瞬间会有几百个请求同时打到数据库上数据库崩了缓存的热闹也就看不成。用定时任务主动刷新缓存也好用分布式锁控制重建也好核心是别让数据库独自扛下所有流量。至于那些玄学般的偶发延迟比如某个接口时快时慢我会保留线上日志的慢查询记录按耗时倒序拉出最慢的几个接口逐个加耗时埋点排查。商城项目的并发量通常到不了需要上微服务的程度把缓存用好、把慢 SQL 改掉响应压到 700 毫秒以内是能稳定达到的。这套 Java 微信小程序的组合在我手里跑了两年的实践核心业务都在这些细节上稳住希望帮到你。本文还有配套的精品资源点击获取
返回列表