news 2026/9/24 19:54:38

苍穹外卖DAY6:微信小程序登录与商品浏览实现详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
苍穹外卖DAY6:微信小程序登录与商品浏览实现详解

都在说苍穹外卖这种练手项目难度不够、没什么含金量,但真到了DAY6你会发现,这一天几乎是整个项目里最容易卡住的一天。前面几天你都在SpringBoot管理端里自娱自乐,接口给前端调、数据从库里查,一切都挺顺手。到了微信小程序这块,事情突然变成了"前端调你、你调微信、微信再给你回数据"的三方协作,HttpClient、微信登录、小程序页面全部搅在一起,稍不留神就卡在某个莫名其妙的报错上出不来。

这篇文章就把DAY6的完整实现过程捋一遍。从后端为什么要用HttpClient,到小程序怎么搭基础页面,再到微信登录的完整链路、商品浏览功能的代码导入,每个环节都给出可落地的代码和注意事项,希望对正在敲这个项目的同学有帮助。

1. 项目概述与DAY6的阶段目标

1.1 苍穹外卖项目到底在做什么

苍穹外卖是一个对标美团、饿了么的外卖平台练手项目,整体分成管理端和用户端两大块。管理端跑在浏览器里,是给平台运营和商家用的,负责员工管理、分类管理、菜品管理、订单处理这些后台操作;用户端则是微信小程序,给真实消费者用的,用户打开小程序浏览商品、下单、支付、查看订单。

DAY6是整个项目的一个分水岭。前面5天你折腾的基本都是管理端,技术栈相对纯粹:SpringBoot、MyBatis、Redis、JWT这些后端常规操作。但到了DAY6,视角一下子切换到用户端,而且第一次引入微信生态,处理的问题从"管理数据"变成了"跟微信服务器打交道"。

这一天要做的核心事情可以概括成一句话:让用户打开小程序后,能够通过微信身份完成登录,然后顺畅地浏览商品分类和商品列表。听起来简单,实际做起来牵扯的东西可不少——后端要主动发起HTTP请求去调微信接口,前端要搭建小程序页面框架,两边还要通过一套登录流程打通身份验证。

1.2 DAY6的核心任务拆解

我把DAY6的内容拆成了四个相互关联的子任务,按照开发顺序来看会非常清晰:

  • 引入HttpClient并在后端调用微信接口:小程序端通过wx.login只能拿到一个临时的code,后端必须拿这个code去请求微信的jscode2session接口,才能换回用户的openid。这个"后端主动请求第三方接口"的能力,就是HttpClient要解决的。
  • 搭建微信小程序开发环境和页面框架:小程序端不是零基础上手,需要熟悉它的目录结构、页面组成、以及和传统网页开发的差异。基础架子搭得好,后面写业务页面会快很多。
  • 实现微信登录的完整闭环:从前端wx.login拿code,到后端换openid、查库建用户、签发JWT,再到前端保存token、后续请求携带token,这条链路是用户端所有业务功能的地基。
  • 导入商品浏览功能代码:把管理端已经建好的分类和商品数据,通过新的接口维度暴露给小程序端,让用户能按分类浏览启售状态的商品列表。

这四块内容环环相扣,登录是前提,商品浏览是第一个真正面向用户的功能页面,HttpClient则是连接前后端和微信三方的一座桥。

2. HttpClient:后端调用第三方接口的敲门砖

2.1 为什么后端需要HttpClient

前5天写SpringBoot后端,你接触最多的是"别人请求我"——管理端前端发HTTP请求,后端Controller接住、处理、返回。但DAY6突然出现了一个相反的需求:后端需要主动去"请求别人",这里的"别人"就是微信服务器。

场景是这样的:用户在小程序里点击登录,小程序端调用wx.login()拿到一个临时凭证code。但这个code本身没有任何用户身份信息,想拿到用户的openid,必须由后端拿着这个code去请求微信官方接口。也就是说,后端这时候要扮演"客户端"的角色,主动发一次HTTP请求。

Java里发起HTTP请求的方式其实不少,原生的HttpURLConnection能用但代码啰嗦,RestTemplate功能强但配置略重。苍穹外卖项目里用的是Hutool工具包中的HttpUtil,它是对Apache HttpClient的深度封装,把HTTP请求简化到了极致——不需要创建复杂的配置对象,不需要管理连接池,一个静态方法调用就能搞定GET或POST请求。

我在其他项目里也经常用Hutool的HttpUtil,最大的感受就是它对新手特别友好,不用理解HTTP协议细节就能上手。但后面我会单独说,这种好用是有代价的,等到了生产环境你还是要回来理解底层原理。

2.2 引入依赖与基础配置

首先要在sky-server模块的pom.xml里引入Hutool依赖:

<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.16</version> </dependency>

引入依赖后,先做一个最基础的自测。随便找一个公开接口,用HttpUtil发起一次GET请求,确认环境没问题:

String url = "https://www.example.com/api"; String result = HttpUtil.get(url); System.out.println(result);

这里有一个很容易踩的坑:Hutool的HttpUtil默认使用的是HTTP协议,如果url没写协议头会直接报错。另外微信的接口都是HTTPS的,Hutool会自动处理SSL证书验证,这一点比原生的HttpURLConnection省心很多——原生方式调用HTTPS接口经常会遇到证书信任问题,还得手动绕过证书验证,那代码写起来简直灾难。

2.3 调用微信jscode2session接口的实现

DAY6里HttpClient的核心应用是调用微信的登录凭证校验接口,也就是jscode2session。接口信息如下:

  • 请求地址:https://api.weixin.qq.com/sns/jscode2session
  • 请求方式:GET
  • 请求参数:appid(小程序唯一标识)、secret(小程序密钥)、js_code(前端传来的临时凭证)、grant_type(固定值authorization_code)
  • 返回结果:openid(用户唯一标识)、session_key(会话密钥)、errcode和errmsg(异常时返回)

后端代码实现如下:

// 根据code调用微信接口,获取openid private String getOpenid(String code) { // 构造请求参数 Map<String, Object> paramMap = new HashMap<>(); paramMap.put("appid", weChatProperties.getAppid()); paramMap.put("secret", weChatProperties.getSecret()); paramMap.put("js_code", code); paramMap.put("grant_type", "authorization_code"); // 调用微信接口 String url = "https://api.weixin.qq.com/sns/jscode2session"; String json = HttpUtil.get(url, paramMap); // 解析返回结果 JSONObject jsonObject = JSONUtil.parseObj(json); String openid = jsonObject.getStr("openid"); return openid; }

这段代码看起来简单,但有几个细节必须注意:

  • appid和secret不要硬编码:项目里通常用@ConfigurationProperties方式注入WeChatProperties,配置写在application.yml里,方便不同环境切换。
  • 响应必须判空:如果code无效或者被使用过,微信接口返回的JSON里没有openid字段,而是有errcode和errmsg。直接取openid会拿到null。
  • code是一次性的:同一个code只能调用一次jscode2session,第二次调用会报40029或40163错误。所以前端拿code、传code、后端换openid这条链路要一气呵成。

2.4 HttpClient使用过程中的三个教训

Hutool的HttpUtil虽然上手简单,但用的时间长了我也踩过几次坑,这里拿出来给大家提个醒。

第一,超时一定要设置。HttpUtil.get默认是无限等待的,如果微信接口因为网络问题响应很慢,后端线程会一直被占用,并发高的时候很容易把线程池打满。我一般会通过配置HttpRequest来设置连接超时和读取超时:

String result = HttpRequest.get(url) .form(paramMap) .timeout(10000) // 连接和读取都设置10秒超时 .execute() .body();

第二,Hutool会自动做参数URL编码,这个在大部分场景下是好事,但如果你传的参数值里本身有已经编码好的内容,会出现双重编码问题。好在jscode2session接口的参数都是纯字母数字,目前不用操心这个。

第三,生产环境建议还是用Spring的RestTemplate或者OpenFeign,而不是直接散落调用Hutool。Hutool的HttpUtil不适合做连接池管理,每次请求重建连接,在高并发场景下性能和稳定性都不如专门的HTTP客户端。练手项目可以用它快速实现,但要有这个意识。

3. 微信小程序开发环境与项目搭建

3.1 小程序前端与传统网页开发的差异

很多同学第一次接触小程序就是在DAY6,如果之前只有Vue或React的基础,肯定会有一些不适应。小程序虽然也写JS和CSS,但有几个核心差异需要花点时间去适应:

  • 没有DOM和BOM:小程序运行在自己的渲染层,不能直接操作DOM节点,所有界面更新都是通过数据绑定自动完成的。你改数据,页面就变,这是"数据驱动视图"的思维。
  • 页面由四个文件组成:同一个页面目录下会有.wxml(模板)、.wxss(样式)、.js(逻辑)、.json(配置)四个文件,职责划分非常明确。
  • 样式单位用rpx:rpx是小程序特有的响应式像素单位,屏幕宽度固定为750rpx,不同机型会自动换算。写页面时用rpx可以省去大量适配工作。

3.2 小程序端目录结构与初始化配置

苍穹外卖的小程序前端,目录结构大致是这样的:

sky-take-out-miniprogram/ ├── pages/ │ ├── index/ // 首页 │ ├── category/ // 分类页面 │ ├── product/ // 商品详情 │ ├── login/ // 登录页 │ └── user/ // 个人中心 ├── utils/ │ └── request.js // 请求封装 ├── app.js // 全局入口 ├── app.json // 全局配置 └── project.config.json // 项目配置

有一个经常被忽略但很关键的文件是app.json,它配置了小程序的页面路由、窗口样式和tabBar。新增页面时必须在pages数组里注册,否则跳转会白屏。我当时在这个坑里浪费了不少时间,切换页面一直报"页面路径不正确",最后发现是忘记注册路由了。

小程序里每个页面的.js文件有一个核心配置对象Page(),包含data(页面数据)、onLoad(页面加载生命周期)、onShow(页面显示生命周期)以及各种事件处理函数。数据绑定则通过WXML里的模板语法完成,比如{{productList}},修改数据用this.setData({ productList: list })

3.3 封装小程序请求工具

小程序发送HTTP请求用的API是wx.request。但每个页面都直接写wx.request会非常痛苦——baseURL要写很多遍、token要手动加、错误要每个页面自己处理。项目里通常会在utils/request.js中统一封装一层。

我当时的封装思路是:把baseURL抽成常量,请求时自动从本地存储读取token并添加到header,响应时统一做code判断,失败弹Toast提示,关键是把整个请求封装成Promise,这样页面里就能用async/await写出非常清爽的代码:

// utils/request.js const BASE_URL = 'http://localhost:8080'; function request({ url, method = 'GET', data = {} }) { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token'); wx.request({ url: BASE_URL + url, method: method, data: data, header: { 'Content-Type': 'application/json', 'Authorization': token ? `Bearer ${token}` : '' }, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else if (res.statusCode === 401) { // token失效,跳转登录 wx.navigateTo({ url: '/pages/login/login' }); } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(res); } }, fail: (err) => { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); } module.exports = { request, BASE_URL };

这里有个开发环境的关键设置。用微信开发者工具调试本地后端接口时,默认会拦截非HTTPS域名。必须在"本地设置"里勾选"不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书",否则请求根本发不出去。这也是新手最容易卡住的地方,打开开发者工具第一件事就应该把这个选项勾上。

4. 微信登录从零到一完整实现

4.1 微信登录整体流程梳理

微信登录是整个DAY6最核心的部分,它把前端小程序、后端服务、微信服务器三者串在了一条链路上。我在纸上画了很多遍这个流程,这里用文字完整还原一遍:

  1. 用户打开小程序,前端调用wx.login(),微信返回一个临时凭证code。
  2. 前端把code通过wx.request发送到后端自己的接口,比如POST /user/login
  3. 后端收到code后,通过HttpClient调用微信的jscode2session接口,传入appid、secret、code。
  4. 微信服务器校验通过,返回这个用户对应的openid和session_key。
  5. 后端拿着openid去数据库的user表查记录。查不到就创建一个新用户,查到了就直接用。
  6. 后端根据用户ID生成JWT令牌,把openid和userId放到令牌负载里。
  7. 后端把JWT返回给小程序端。
  8. 小程序端拿到JWT存入本地缓存,之后每次请求都通过Authorization头携带这个token。

理解这个流程的关键在于:小程序端自始至终没有直接接触微信的用户身份数据,微信服务器只认后端,后端把自己的appid和secret作为"门禁卡"去换用户身份,再把身份转成自己系统的token发给前端。这套设计保证了secret不会暴露在小程序代码里,而小程序代码本质上是可以被完全逆向的。

4.2 后端登录接口完整实现

后端代码分成三层,以注释的方式体现关键逻辑:

@RestController @RequestMapping("/user") @Slf4j public class UserController { @Autowired private UserService userService; @PostMapping("/login") public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) { log.info("微信用户登录,code:{}", userLoginDTO.getCode()); // 1. 获取用户信息(openid) User user = userService.wxLogin(userLoginDTO); // 2. 生成JWT令牌 Map<String, Object> claims = new HashMap<>(); claims.put("userId", user.getId()); String token = JwtUtil.createJWT(claims); // 3. 封装返回结果 UserLoginVO userLoginVO = UserLoginVO.builder() .id(user.getId()) .openid(user.getOpenid()) .token(token) .build(); return Result.success(userLoginVO); } }

Service层的实现核心是微信接口调用和用户判断:

public User wxLogin(UserLoginDTO userLoginDTO) { // 1. 调用微信接口获取openid String openid = getOpenid(userLoginDTO.getCode()); if (openid == null) { throw new LoginFailedException("微信登录失败,请重试"); } // 2. 根据openid查询用户 User user = userMapper.getByOpenid(openid); if (user == null) { // 3. 新用户则自动注册 user = User.builder() .openid(openid) .createTime(LocalDateTime.now()) .updateTime(LocalDateTime.now()) .build(); userMapper.insert(user); } return user; }

有一个细节需要注意:Day6阶段新用户默认没有用户名、手机号、头像这些资料,先用openid创建一条最小记录,后续可以在个人中心里补充完善。不要想着一次性把用户信息全收齐,微信登录本来就允许用户先登录后完善资料。

4.3 JWT签发与登录状态管理

JWT在DAY6里扮演的角色非常关键。微信登录成功后,后端不再像管理端那样每次请求都带sessionId,而是把用户身份信息编码进一个自包含的令牌里,前端每次请求携带,后端校验通过就认为是可信用户。

JwtUtil工具类的核心方法:

public static String createJWT(Map<String, Object> claims) { // 设置签发时间、过期时间、签名算法 JwtBuilder builder = Jwts.builder() .setClaims(claims) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 7200000)) .signWith(SignatureAlgorithm.HS256, secretKey); return builder.compact(); } public static Claims parseJWT(String jwt) { return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwt) .getBody(); }

过期时间我见过设1小时的,也见过设7天的。外卖场景建议设2小时左右,配合前端的自动重新登录,兼顾体验和安全性。JWT的密钥一定要放在配置文件中,而且不要用默认的字符串,生产环境建议用足够长的随机字符串。

4.4 前端小程序登录逻辑

小程序端的登录逻辑放在utils里,核心代码如下:

function wxLogin() { return new Promise((resolve, reject) => { // 1. 获取微信登录code wx.login({ success: async (res) => { if (res.code) { try { // 2. 把code发给后端 const data = await request({ url: '/user/login', method: 'POST', data: { code: res.code } }); // 3. 保存token和用户信息 wx.setStorageSync('token', data.data.token); wx.setStorageSync('userInfo', data.data); resolve(data); } catch (err) { reject(err); } } }, fail: (err) => reject(err) }); }); }

很多同学会在登录时纠结:要不要在onLaunch里就调登录?我的做法是:不着急调,在用户第一次需要身份信息或后端返回401时再去调。Day6阶段商品浏览其实不需要登录就能看,但下单、购物车这些功能才必须登录。按需登录可以减少不必要的请求。另外有一个常见坑:wx.login的code是5分钟内有效且只能用一次,如果你在onLaunch里调了一次登录,后面某个页面又调了一次,第一个请求回来时发现code已经失效,就会报错。所以我建议用Promise封装登录方法,保证全局只有一个登录请求在进行。

5. 导入商品浏览功能代码解析

5.1 商品浏览的需求分析

商品浏览是DAY6里最"看得见"的功能,用户打开小程序就能看到。它的核心交互场景是:首页顶部展示所有启用的分类,用户点击左侧分类,右侧就展示该分类下所有启售中的商品。

这个功能在管理端已经有了基础的分类和商品管理接口,但用户端的接口设计和管理端有几个明显的不同:

  • 只查启用的分类:管理端可以查到禁用状态的分类,用户端必须过滤掉。
  • 只查启售的商品:如果商家把某个商品停售了,用户端不能再看到,更不能下单。
  • 返回结构更适合展示:用户端接口返回的商品信息,未必需要管理端那些复杂的字段,比如创建时间、更新时间、操作日志等,用户端只需要id、名称、图片、价格、描述、销量这些核心字段。

5.2 分类与商品查询接口实现

后端新增两个Controller:

CategoryController提供用户端分类查询接口:

@RestController @RequestMapping("/user/category") public class UserCategoryController { @Autowired private CategoryService categoryService; @GetMapping("/list") public Result<List<Category>> list(Integer type) { // 只查询启用状态的分类 List<Category> list = categoryService.listByStatus(1, type); return Result.success(list); } }

ProductController提供用户端商品列表查询接口:

@RestController @RequestMapping("/user/product") public class UserProductController { @Autowired private ProductService productService; @GetMapping("/list") public Result<List<ProductVO>> list(Long categoryId) { // 根据分类查启售商品 List<ProductVO> list = productService.listByCategoryId(categoryId); return Result.success(list); } }

Service层的关键SQL逻辑,用注解方式写在Mapper接口里:

@Select("SELECT * FROM product " + "WHERE category_id = #{categoryId} " + "AND status = 1 " + "ORDER BY sort DESC, create_time DESC") List<Product> listByCategoryId(Long categoryId);

这里有个特别容易被忽略的点:商品列表查出来之后,如果商品有口味(比如辣度、加料选择),需要在返回之前查询并封装进ProductVO。因为用户点商品的时候要选择口味才能加购物车,这一步不做,后面下单功能会很麻烦。

关联查询的代码一定要放在Service层,不能在Controller里写业务逻辑,否则后续加缓存、加校验、加日志的时候Controller会越来越臃肿。

5.3 小程序端商品展示实现

前端首页的商品展示结构,核心是一个scroll-view左右联动布局:左侧是分类列表,右侧是当前分类下的商品列表。交互逻辑集中在三个地方:

  • 进入页面时,先请求分类列表,默认选中第一个分类
  • 点击左侧分类,更新选中状态,重新请求右侧商品列表
  • 右侧商品列表支持下拉刷新,加载更多用分页

页面逻辑的核心代码:

Page({ data: { categories: [], currentCategoryId: null, products: [], loading: false }, onLoad() { this.loadCategories(); }, async loadCategories() { const res = await request({ url: '/user/category/list', data: { type: 1 } }); const categories = res.data; this.setData({ categories }); if (categories.length > 0) { // 默认选中第一个分类并加载商品 this.setData({ currentCategoryId: categories[0].id }); this.loadProducts(categories[0].id); } }, async loadProducts(categoryId) { this.setData({ loading: true }); try { const res = await request({ url: '/user/product/list', data: { categoryId } }); this.setData({ products: res.data }); } finally { this.setData({ loading: false }); } }, onCategoryTap(e) { const id = e.currentTarget.dataset.id; if (id === this.data.currentCategoryId) return; this.setData({ currentCategoryId: id }); this.loadProducts(id); } });

这里有一个非常重要的联调经验:商品图片如果在数据库里存的是后端服务器上的相对路径(比如/images/product/1.jpg),小程序端直接用是加载不出来的,必须拼接上后端的访问域名或IP。我当时在项目里是在后端返回时直接拼好完整URL路径,如果图片存的是MinIO或OSS,那也是同样处理,返回可访问的完整URL。这一步不做,页面渲染出来就是一堆裂图。

6. 常见问题与排查技巧实录

6.1 微信登录的经典报错与排查

微信登录这个功能,前端后端联调时绝对会让你掉几层头发。我挑几个最常见的错误码和排查思路:

错误码40029(code无效):这个code要么已经过期(5分钟有效期),要么已经被使用过一次。排查思路是先看是不是前端重复调用了wx.login,或者是同一个code被发送了多次请求。可以在后端加日志,打印收到的code和时间,跟微信返回的errcode对照着排查。

错误码40163(code已被使用过):出现这个错误说明同一个code被调用了两次jscode2session。为什么会调用两次?最常见的是前端发请求时网络超时,前端重试了一次,但第一次请求其实已经到达了后端并成功调用了微信接口。换句话说,重复请求导致code被消费了两次。解决办法是前端不要把登录请求做自动重试,如果用户手动触发多次登录,后端要做幂等处理。

返回openid为null:大概率是appid或secret配置错了,先核对application.yml里的配置和后端实际注入的值。还有一个小坑:复制粘贴配置文件时,YAML的缩进不对会导致配置解析不出来,检查一下appid、secret前面有没有对不齐的空格。

登录成功后查询用户信息时抛异常:新用户创建后,查询用户信息的时机不要太早。因为用户可能还没跑到个人中心,数据库里可能还没有完整的用户资料,查询时要做空值判断,或者干脆以openid为维度做最小化查询。

6.2 HttpClient和接口调用踩坑记录

后端调用微信接口这块,我总结了自己和身边同学踩过的三四次坑:

第一,响应解析时序问题。HttpUtil.get返回的JSON,解析时直接调getStr("openid"),如果微信返回了errcode但没返回openid,这里得到的就是null。不要在Controller层直接拿这个null去查库,否则MyBatis会报"无效的列类型"之类的错。正确的做法是在Service层判空,抛业务异常,由全局异常处理器统一返回友好提示。

第二,参数拼接问题。Hutool的HttpUtil.get支持传入Map参数自动拼URL,这个好用,但要注意Map的key不要写错。js_code这个参数名非常容易写成jsCode,微信接口是下划线风格,写错了直接返回400错误。小程序端传参时可以随便用驼峰,但后端调微信接口时必须按微信文档的参数名来。

第三,请求时机问题。微信接口的网络延迟不是我们能控制的,日志里一定要记录耗时。我遇到过一个问题:后端调用微信接口平均耗时1.5秒,前端请求超时设了2秒,结果用户感觉登录时好时坏。后来把超时时间放宽到10秒,并把wx.login和用户资料请求并行发起,体验才好起来。

6.3 小程序联调问题总结

小程序端的联调问题,一部分源于开发者工具使用不熟练,一部分源于前后端对接口的定义不一致。

开发者工具勾了"不校验合法域名"还是请求失败:检查一下你的请求URL是不是写错了协议头。本地后端是http,如果前面不小心写了https,本地开发时一样会失败。另外要确认后端启动的端口,苍穹外卖默认8080,如果被占用改成了别的端口,前端baseURL记得同步改。

请求报跨域错误:小程序平台的wx.request其实不存在传统浏览器的跨域限制,但你如果在开发者工具的"模拟器"里调试H5页面,或者把接口地址配到了web-view里,就会遇到跨域问题。后端Controller上加@CrossOrigin注解能解决大部分情况,但更规范的做法是在网关或Nginx层统一配置跨域。

商品图片裂图:前面说过,图片路径如果是相对路径,要拼接后端访问地址。还有一个坑是图片路径存的是D:\images\product\1.jpg这种本地绝对路径,这种在生产环境完全没法用。建议项目里统一封装一个方法处理图片URL,把本地存储、MinIO、OSS等不同情况的路径都转成可访问的完整URL,前端就不用关心图片存在哪了。

数据库中文乱码:这个坑与商品浏览功能密切相关。商品名称、分类名称这些中文数据,如果数据库连接的characterEncoding没配置utf8,小程序端就会显示乱码。检查一下数据库连接URL里有没有characterEncoding=utf8,以及数据库表本身是不是utf8mb4编码。之前排查一个商品名称显示一半的问题,就是字段长度不够,商品名称被截断了,报错信息在日志里不明显,最后看数据库才发现。

7. 商品浏览性能优化与后续扩展方向

7.1 加一层Redis缓存

商品浏览功能做完只是第一步。有些商品数据几乎不变,比如分类列表和商品名称、图片、价格这些基础信息,每次小程序端进入首页都实时查库,其实是浪费数据库连接。

我当时在商品浏览做完后,顺手加了一层Redis缓存。思路很清晰:查询分类或商品列表时,先查Redis,命中就直接返回;没有命中就去查MySQL,查完写入Redis并设置过期时间,比如分类缓存设1小时,商品列表缓存设30分钟。管理端如果修改了商品数据,主动删除对应缓存,保证用户端能尽快看到更新。引入缓存之后首页接口的响应时间从80毫秒降到了10毫秒以内,虽然数据量小效果不明显,但思路一定要有。

7.2 商品搜索与分页加载

Day6的商品浏览只做到了按分类查询,但真实外卖场景用户还需要搜索。给商品的name字段做模糊匹配很简单,但如果商品量大,直接用LIKE '%关键词%'会有性能问题。建议引入全文索引或者用Redis的倒排结构做简单搜索,企鹅文档里常见的方案是用MyBatis的动态SQL拼接,搜索条件和分类条件、启售状态组合查询。

另外Day6的商品列表是一次性查完返回的,商品数量少没问题,但后续肯定要做分页。小程序端用scroll-view触底加载更多,后端接口加page和pageSize参数,返回结果带total字段,前端判断是否还有下一页。

7.3 用户端后续功能的衔接

商品浏览是用户端的第一块拼图,后面接踵而来的购物车、下单、支付、订单列表、个人中心,全都依赖Day6打好的两个基础:登录态和商品数据接口。说句实在话,很多同学学到后面订单支付那块写不下去,回头发现最根本的原因是Day6登录链路没理解透——token不知道在哪存、请求头怎么加、后端拦截器怎么校验。所以Day6一定要吃透,宁可多花两天搞明白微信登录的原理,也不要囫囵吞枣往下冲。

我个人在实际操作中还有一个体会:DAY6的代码量不一定很大,但涉及的知识点非常杂,前后端分离的思路第一次真正体现出来。建议在动手写代码之前,先用文字把微信登录的完整流程画一遍,把每个节点涉及的角色、接口、参数、数据变化都写清楚。把这个流程吃透,后面前端联调会顺畅很多。如果中途卡住了,优先从网络请求这一步开始排查——先看小程序有没有发出请求,再看后端有没有收到,再看后端有没有成功调通微信接口,一层层往下找。最后再分享一个小技巧,把后端日志的SQL和执行时间打开,联调时能看到每次请求实际执行了什么语句,排查问题时效率直接翻倍。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 19:54:38

自我学习大模型

“自学习”是大模型领域一个非常重要且前沿的方向。目前&#xff0c;完全意义上的、能像人类一样自主规划并学习新知识的大模型还处于探索阶段&#xff0c;但已经有很多技术方向可以被视为“自学习”的雏形或组成部分。 以下是对“自学习大模型”不同层面的解读和当前主要的实…

作者头像 李华
网站建设 2026/9/24 19:53:31

YOLOv5-6.0吸烟检测实战:从训练到部署的避坑指南

简介&#xff1a;这份资源面向计算机视觉方向的学习者与开发者&#xff0c;提供基于YOLOv5-6.0训练完成的吸烟行为检测模型&#xff0c;可用于公共场所、工地、加油站等场景下的吸烟行为识别与预警。包内包含YOLOv5m与YOLOv5s两个已训练权重&#xff0c;目标类别为smoke&#x…

作者头像 李华
网站建设 2026/9/24 19:53:20

回归代码详解:从线性回归到XGBoost的实战指南

1. 内容整体设计与思路拆解1.1 为什么第五天必须讲回归&#xff0c;而且是代码优先先说一个我自己的观察。前四天学员还在跟数据结构、基础语法、可视化缠斗&#xff0c;到了第五天突然进入回归&#xff0c;很多人第一反应是&#xff1a;“是不是有点早&#xff1f;”但恰恰相反…

作者头像 李华
网站建设 2026/9/24 19:53:14

电脑蓝屏开不了机?5步自检法从蓝屏代码到DMP文件找出真凶

电脑蓝屏开不了机&#xff0c;这几年我帮身边朋友处理过至少几十次&#xff0c;说句实话&#xff0c;真正需要送修的重来不超过两成。系统崩溃、驱动打架、外设捣乱&#xff0c;这些软件层面的问题占了大多数&#xff0c;明明自己花半小时就能搞定&#xff0c;结果抱着主机去维…

作者头像 李华