news 2026/4/16 9:53:14

苍穹外卖项目复习笔记 (二) -- 小程序开发,redisTemplate,spring cache

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
苍穹外卖项目复习笔记 (二) -- 小程序开发,redisTemplate,spring cache

一、 微信小程序登录 (后端实现)

后端的核心任务只有一个 拿codeopenid

1. 流程详解后端不直接处理微信界面,而是提供一个接口给小程序调用。

  • 输入:小程序前端传来的临时登录凭证code
  • 处理:后端拿着这个code+appid+secret去找微信服务器核实。
  • 输出:生成一个我们可以控制的token(JWT) 返回给前端,用于后续业务鉴权。

2. 代码示例

Step 1: 用户JWT令牌校验拦截器

/** * jwt令牌校验的拦截器 */@Component@Slf4jpublicclassJwtTokenUserInterceptorimplementsHandlerInterceptor{@AutowiredprivateJwtPropertiesjwtProperties;publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(!(handlerinstanceofHandlerMethod)){returntrue;}// 1、从请求头中获取令牌Stringtoken=request.getHeader(jwtProperties.getUserTokenName());// 2、校验令牌try{log.info("jwt校验:{}",token);// 这里必须使用 UserSecret (用户端密钥) 进行解密,而不是 UserTokenNameClaimsclaims=JwtUtil.parseJWT(jwtProperties.getUserSecretKey(),token);// 这里必须获取 USER_ID,而不是 EMP_IDLonguserId=Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());// 3、设置到上下文BaseContext.setCurrentId(userId);log.info("当前用户id:{}",userId);returntrue;}catch(Exceptionex){log.error("用户端JWT校验失败",ex);// 建议打印异常日志以便调试response.setStatus(401);returnfalse;}}}

Step 2: Controller 层 (接收请求)

@RestController@RequestMapping("/user/user")@Slf4jpublicclassUserController{@AutowiredprivateUserServiceuserService;@PostMapping("/login")publicResult<UserLoginVO>wechatLogin(@RequestBodyUserLoginDTOuserLoginDO){log.info("微信登录,用户数据:{}",userLoginDO);returnuserService.wechatLogin(userLoginDO);}}

Step 3: Service 层 (核心逻辑)
这里模拟了通过HttpClient调用微信接口的过程。

@Service@Slf4jpublicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsUserService{privatefinalstaticStringWECHAT_LOGIN_URL="https://api.weixin.qq.com/sns/jscode2session";@AutowiredprivateJwtPropertiesjwtProperties;@AutowiredprivateWeChatPropertiesweChatProperties;@OverridepublicResult<UserLoginVO>wechatLogin(UserLoginDTOuserLoginDTO){Stringopenid=getOpenid(userLoginDTO);// 判断openid是否为空if(openid==null){thrownewLoginFailedException(MessageConstant.LOGIN_FAILED);}// 判断是否是新用户Useruser=lambdaQuery().eq(User::getOpenid,openid).one();// 如果是新用户,自动完成注册if(user==null){user=User.builder().openid(openid).build();save(user);}LonguserId=user.getId();// 创建jwt令牌Map<String,Object>map=newHashMap<>();map.put(JwtClaimsConstant.USER_ID,userId);Stringtoken=JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),map);// 封装vo对象UserLoginVOuserLoginVO=UserLoginVO.builder().id(userId).openid(userLoginDTO.getCode()).token(token).build();returnResult.success(userLoginVO);}privateStringgetOpenid(UserLoginDTOuserLoginDTO){// 调用微信接口实现登录Map<String,String>Login_Map=newHashMap<>();Login_Map.put("appid",weChatProperties.getAppid());Login_Map.put("secret",weChatProperties.getSecret());Login_Map.put("js_code",userLoginDTO.getCode());Login_Map.put("grant_type","authorization_code");Stringresult=HttpClientUtil.doGet(WECHAT_LOGIN_URL,Login_Map);log.info("微信接口返回数据:{}",result);// 解析JSONObjectjsonObject=JSON.parseObject(result);returnjsonObject.getString("openid");}}

二、 StringRedisTemplate 与 RedisTemplate

为了直观看到区别,我们对比一下如果不加配置直接使用,它们在 Redis 图形化工具(如 RDM)里长什么样。

1. RedisTemplate

(默认情况)Java 对象会被 JDK 序列化,变成二进制流。

@AutowiredprivateRedisTemplateredisTemplate;publicvoidtestRedisTemplate(){// 存一个简单的字符串redisTemplate.opsForValue().set("city","beijing");}

Redis 中实际存储的样子 (乱码):

  • Key:\xac\xed\x00\x05t\x00\x04city
  • Value:\xac\xed\x00\x05t\x00\x07beijing
  • 缺点: 此时如果你去 Redis 命令行执行get city,是查不到数据的,因为 Key 实际上包含了乱码前缀。

2. StringRedisTemplate (推荐)Key 和 Value 都是纯字符串,所见即所得。

@AutowiredprivateStringRedisTemplatestringRedisTemplate;publicvoidtestStringRedisTemplate(){// 存一个简单的字符串stringRedisTemplate.opsForValue().set("city","beijing");// 存一个对象 (通常配合 JSON 工具库)Useruser=newUser(1L,"Jack");StringjsonUser=JSON.toJSONString(user);// 手动转 JSONstringRedisTemplate.opsForValue().set("user:1",jsonUser);}

Redis 中实际存储的样子 (清晰):

  • Key:city
  • Value:beijing
  • Key:user:1
  • Value:{"id":1, "name":"Jack"}
  • 优点: 方便调试,多语言通用。

三、 Spring Cache 注解实战

Spring Cache 的目标是:把缓存逻辑从业务代码中剥离出来
在苍穹外卖中,主要用于C端(用户端)查询套餐时的缓存,提高响应速度。

1. 开启缓存功能在启动类上必须添加注解:

@SpringBootApplication@EnableCaching// <--- 开启注解缓存功能publicclassSkyApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SkyApplication.class,args);}}

###2. 查询时自动缓存 (@Cacheable)场景: 用户查询某个分类下的套餐列表。

@ServicepublicclassSetmealServiceImplimplementsSetmealService{// cacheNames (或 value): 缓存的名称,相当于 Redis 中的文件夹// key: 具体的键名。这里使用 SpEL 表达式,动态获取参数 categoryId// 最终 Redis Key 格式: setmealCache::1001 (假设 categoryId 为 1001)@Cacheable(cacheNames="setmealCache",key="#categoryId")publicList<Setmeal>list(LongcategoryId){// --- 下面的代码只有在缓存未命中时才会执行 ---Setmealsetmeal=newSetmeal();setmeal.setCategoryId(categoryId);setmeal.setStatus(StatusConstant.ENABLE);List<Setmeal>list=setmealMapper.list(setmeal);returnlist;// --- 方法结束后,Spring 会自动将 list 返回值写入 Redis ---}}

3. 修改时清理缓存 (@CacheEvict)场景: 管理员在后台修改了套餐,或者新增了套餐,必须删除缓存,防止用户看到旧数据。

精确清理 (删除特定 key):

// 当执行批量删除套餐时,我们很难知道具体的 categoryId 是多少,或者影响太大// 所以通常不精确删除,而是直接删除整个 setmealCache 下的所有数据

全部清理 (常用):

@ServicepublicclassSetmealServiceImplimplementsSetmealService{// allEntries = true: 表示删除 "setmealCache" 下所有的 key// 无论是 setmealCache::1001 还是 setmealCache::1002 统统删掉@CacheEvict(cacheNames="setmealCache",allEntries=true)publicvoidsave(SetmealDTOsetmealDTO){// 执行正常的数据库新增操作Setmealsetmeal=newSetmeal();BeanUtils.copyProperties(setmealDTO,setmeal);setmealMapper.insert(setmeal);// --- 方法执行完后,Spring 自动清空 setmealCache 下的所有缓存 ---}@CacheEvict(cacheNames="setmealCache",allEntries=true)publicvoidupdate(SetmealDTOsetmealDTO){// update 逻辑...}}

总结 Spring Cache 流程图

  1. 查询请求-> 拦截器 ->Redis 有吗?
  • -> 直接返回数据 (Service 方法体代码不执行)。
  • -> 执行 Service 方法 -> 查数据库 -> 返回结果 ->自动存入 Redis
  1. 修改请求-> 执行 Service 方法 -> 改数据库 ->自动删除 Redis 缓存
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/9 18:59:56

44、Unix文件系统深度解析

Unix文件系统深度解析 1. Unix文件系统简介 Unix文件系统是一种层次化的文件管理结构,它通过将文件分组到不同的目录中,有效地解决了大量文件管理的难题,避免了文件名冲突,并为文件提供默认属性。 在Unix中,目录可以任意嵌套,形成树状结构。根目录用斜杠(/)表示,它…

作者头像 李华
网站建设 2026/4/16 12:16:23

9、Docker网络配置全解析

Docker网络配置全解析 1. 使用pipework理解容器网络 在容器网络配置中, pipework 是一个强大的工具。当执行如下路由信息命令后: default 192.168.1.254 0.0.0.0 UG 0 0 0 eth1 192.168.1.0 * 255.255.255.0 U …

作者头像 李华
网站建设 2026/4/16 12:16:56

【R语言统计分析进阶】:农业产量差异显著性检验全解析

第一章&#xff1a;农业产量差异显著性检验概述在现代农业数据分析中&#xff0c;评估不同耕作条件、品种或管理策略对作物产量的影响至关重要。农业产量差异显著性检验是一种统计方法&#xff0c;用于判断两组或多组产量数据之间的差异是否具有统计学意义&#xff0c;而非由随…

作者头像 李华
网站建设 2026/4/14 18:18:25

7岁孩子弱视恢复,亲测有效的三大方法分享!

不知道有多少姐妹&#xff0c;和我们一样&#xff0c;才7岁就查出来弱视了&#xff01;而我在这之前甚至连弱视是什么都不知道&#xff01;然后医生给我说&#xff0c;可以恢复&#xff0c;但是要做好长期战的准备&#xff01;然后我们就开始了漫长的弱视恢复之路&#xff0c;这…

作者头像 李华
网站建设 2026/4/15 20:47:18

TEMU 采购困局突围:风控诱因拆解与信任重建方案

在TEMU的低价生态中&#xff0c;卖家常被高砍单率困扰&#xff0c;这并非简单的供需问题&#xff0c;而是一场与平台精密风控系统间的信任博弈&#xff0c;高砍单率背后&#xff0c;是平台严格的防滥用机制与卖家传统粗放模式间的冲突&#xff0c;需理解平台逻辑&#xff0c;并…

作者头像 李华