news 2026/8/24 11:06:34

苍穹外卖项目复习笔记 (二) -- 小程序开发,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/8/23 17:46:03

Kafka 技术架构与核心原理深度解析

本文将深入探讨 Apache Kafka 的核心概念、架构设计以及其在消息处理方面的优势。 1. Kafka 简介 Kafka 是一个高性能的分布式流媒体平台。它作为集群运行在多台服务器上&#xff0c;提供极高的可用性和容错性。 在 Kafka 中&#xff0c;数据是以**流&#xff08;Stream&#x…

作者头像 李华
网站建设 2026/8/24 6:41:25

【资深架构师亲授】:Rust-PHP扩展多版本适配的7大黄金法则

第一章&#xff1a;Rust-PHP扩展多版本适配的核心挑战在构建基于 Rust 编写的 PHP 扩展时&#xff0c;实现对多个 PHP 版本的兼容性支持是一项关键且复杂的技术任务。由于不同 PHP 版本&#xff08;如 7.4、8.0、8.1 及更高版本&#xff09;在 Zend 引擎 API 层面存在结构性差异…

作者头像 李华
网站建设 2026/8/24 14:45:08

Redis在秒杀业务中的应用

总结&#xff1a;本文探讨了Redis在秒杀业务中的应用&#xff0c;重点介绍了全局唯一ID生成方案和分布式锁的实现。首先提出基于Redis的全局ID生成器设计方案&#xff0c;通过时间戳序列号的组合方式保证ID唯一性。针对秒杀业务中的库存超卖问题&#xff0c;分析了悲观锁和乐观…

作者头像 李华
网站建设 2026/8/23 20:08:46

GPT-5.2震撼发布:职场AI新标杆,效率提升40%,收藏必学!

OpenAI发布GPT-5.2模型&#xff0c;回应Google Gemini竞争压力。模型分三版&#xff0c;专注职场实用主义。GPT-5.2 Thinking在44个职业任务中达到或超过人类专家水平&#xff0c;编程能力创业界新高&#xff0c;幻觉率降低30%&#xff0c;长文本处理接近完美&#xff0c;数学科…

作者头像 李华
网站建设 2026/8/24 14:45:08

Java学习日记——DAY9

今天学习了Java中的String类&#xff0c;学习内容如下&#xff1a;1.String类创建对象的两种方法&#xff1a;&#xff08;1&#xff09;静态创建&#xff1a;String s1 "abc";&#xff08;2&#xff09;动态创建&#xff1a;String s2 new String("abc"…

作者头像 李华