news 2026/5/14 6:18:13

苍穹外卖 项目记录 第六天

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
苍穹外卖 项目记录 第六天

一 HttpClient

HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

HttpClient作用:

  • 发送HTTP请求

  • 接收响应数据

HttpClient的核心API:

  • HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。

  • HttpClients:可认为是构建器,可创建HttpClient对象。

  • CloseableHttpClient:实现类,实现了HttpClient接口。

  • HttpGet:Get方式请求类型。

  • HttpPost:Post方式请求类型。

HttpClient发送请求步骤:

  • 创建HttpClient对象

  • 创建Http请求对象

  • 调用HttpClient的execute方法发送请求

二 微信小程序开发

小程序是一种新的开放能力,开发者可以快速地开发一个小程序。可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

开发微信小程序之前需要做如下准备工作:

  • 注册小程序

  • 完善小程序信息

  • 下载开发者工具

1). 注册小程序

注册地址:https://mp.weixin.qq.com/wxopen/waregister?action=step1

2). 完善小程序信息

登录小程序后台:https://mp.weixin.qq.com/

完善小程序信息、小程序类目

在开发管理页面查看小程序的 AppID

3). 下载开发者工具

资料中已提供,无需下载,熟悉下载步骤即可。一定要在官网下载最新稳定版

下载地址: https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html

开发阶段,小程序发出请求到后端的Tomcat服务器,一定要勾选图中"不校验"的设置。若不勾选,请求发送失败。

调试基础库为较低的版本,建议使用2.25.xx 版本的

小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成,必须放在项目的根目录:

app.js:必须存在,主要存放小程序的逻辑代码

app.json:必须存在,小程序配置文件,主要存放小程序的公共配置

app.wxss:非必须存在,主要存放小程序公共样式表,类似于前端的CSS样式

每个小程序页面主要由四个文件组成:

js文件:必须存在,存放页面业务逻辑代码,编写的js代码。

wxml文件:必须存在,存放页面结构,主要是做页面布局,页面效果展示的,类似于HTML页面。

json文件:非必须,存放页面相关的配置。

wxss文件:非必须,存放页面样式表,相当于CSS文件。

发布小程序->审核版本->线上版本

三 微信登录

导入小程序代码

在导入时可能会自动填充APPID,请将其修改为自己的APPID,后端服务改为不使用云服务,修改为自己后端服务的ip地址和端口号(默认不需要修改)

微信登录流程

微信登录:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

步骤分析:

  1. 小程序端,调用wx.login()获取code,就是授权码。

  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。

  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。

  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。

  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。

  6. 小程序端,收到自定义登录态,存储storage。

  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。

  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。

  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

需求分析和设计

业务规则:

  • 基于微信登录实现小程序的登录功能

  • 如果是新用户需要自动完成注册

说明:请求路径/user/user/login,第一个user代表用户端,第二个user代表用户模块。

代码开发

定义相关配置

配置微信登录所需配置项:

application-dev.yml

sky: wechat: appid: 自己的小程序的id secret: 自己的小程序的密钥(在开发管理页面获取或重置)

application.yml

sky: wechat: appid: ${sky.wechat.appid} secret: ${sky.wechat.secret}
配置为微信用户生成jwt令牌时使用的配置项:

application.yml

sky: jwt: # 设置jwt签名加密时使用的秘钥 admin-secret-key: itcast # 设置jwt过期时间 admin-ttl: 7200000 # 设置前端传递过来的令牌名称 admin-token-name: token user-secret-key: itheima user-ttl: 7200000 user-token-name: authentication

Controller层

根据接口定义创建UserController的login方法:
@RestController @RequestMapping("/user/user") @Api(tags = "C端用户相关接口") @Slf4j public class UserController { @Autowired private UserService userService; @Autowired private JwtProperties jwtProperties; /** * 微信登录 * @param userLoginDTO * @return */ @PostMapping("/login") @ApiOperation("微信登录") public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){ log.info("微信用户登录:{}",userLoginDTO.getCode()); //微信登录 User user = userService.wxLogin(userLoginDTO);//后绪步骤实现 //为微信用户生成jwt令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.USER_ID,user.getId()); String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims); UserLoginVO userLoginVO = UserLoginVO.builder() .id(user.getId()) .openid(user.getOpenid()) .token(token) .build(); return Result.success(userLoginVO); } }

其中,JwtClaimsConstant.USER_ID常量已定义。

Service层接口

创建UserService接口:
public interface UserService { /** * 微信登录 * @param userLoginDTO * @return */ User wxLogin(UserLoginDTO userLoginDTO); }

Service层实现类

创建UserServiceImpl实现类:实现获取微信用户的openid和微信登录功能
@Service @Slf4j public class UserServiceImpl implements UserService { //微信服务接口地址 public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session"; @Autowired private WeChatProperties weChatProperties; @Autowired private UserMapper userMapper; /** * 微信登录 * @param userLoginDTO * @return */ public User wxLogin(UserLoginDTO userLoginDTO) { String openid = getOpenid(userLoginDTO.getCode()); //判断openid是否为空,如果为空表示登录失败,抛出业务异常 if(openid == null){ throw new LoginFailedException(MessageConstant.LOGIN_FAILED); } //判断当前用户是否为新用户 User user = userMapper.getByOpenid(openid); //如果是新用户,自动完成注册 if(user == null){ user = User.builder() .openid(openid) .createTime(LocalDateTime.now()) .build(); userMapper.insert(user);//后绪步骤实现 } //返回这个用户对象 return user; } /** * 调用微信接口服务,获取微信用户的openid * @param code * @return */ private String getOpenid(String code){ //调用微信接口服务,获得当前微信用户的openid Map<String, String> map = new HashMap<>(); map.put("appid",weChatProperties.getAppid()); map.put("secret",weChatProperties.getSecret()); map.put("js_code",code); map.put("grant_type","authorization_code"); String json = HttpClientUtil.doGet(WX_LOGIN, map); JSONObject jsonObject = JSON.parseObject(json); String openid = jsonObject.getString("openid"); return openid; } }

Mapper层

创建UserMapper接口:
@Mapper public interface UserMapper { /** * 根据openid查询用户 * @param openid * @return */ @Select("select * from user where openid = #{openid}") User getByOpenid(String openid); /** * 插入数据 * @param user */ void insert(User user); }
创建UserMapper.xml映射文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.sky.mapper.UserMapper"> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> insert into user (openid, name, phone, sex, id_number, avatar, create_time) values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime}) </insert> </mapper>

编写拦截器

编写拦截器JwtTokenUserInterceptor:统一拦截用户端发送的请求并进行jwt校验
/** * jwt令牌校验的拦截器 */ @Component @Slf4j public class JwtTokenUserInterceptor implements HandlerInterceptor { @Autowired private JwtProperties jwtProperties; /** * 校验jwt * * @param request * @param response * @param handler * @return * @throws Exception */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //判断当前拦截到的是Controller的方法还是其他资源 if (!(handler instanceof HandlerMethod)) { //当前拦截到的不是动态方法,直接放行 return true; } //1、从请求头中获取令牌 String token = request.getHeader(jwtProperties.getUserTokenName()); //2、校验令牌 try { log.info("jwt校验:{}", token); Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token); Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString()); log.info("当前用户的id:", userId); BaseContext.setCurrentId(userId); //3、通过,放行 return true; } catch (Exception ex) { //4、不通过,响应401状态码 response.setStatus(401); return false; } } }

在WebMvcConfiguration配置类中注册拦截器:

@Autowired private JwtTokenUserInterceptor jwtTokenUserInterceptor; /** * 注册自定义拦截器 * @param registry */ protected void addInterceptors(InterceptorRegistry registry) { log.info("开始注册自定义拦截器..."); //......... registry.addInterceptor(jwtTokenUserInterceptor) .addPathPatterns("/user/**") .excludePathPatterns("/user/user/login") .excludePathPatterns("/user/shop/status"); }

启动微信小程序编译时先检查:

1.检查后端有没有打开
2.检查redis有没有打开
3.调试基础库要选低版本的倒数几个2.xx的

四 导入商品浏览功能代码

需求分析和设计

接口设计:

  • 查询分类

  • 根据分类id查询菜品

  • 根据分类id查询套餐

  • 根据套餐id查询包含的菜品

分析每个接口,明确每个接口的请求方式、请求路径、传入参数和返回值。

代码导入

可按照mapper-->service-->controller依次导入,这样代码不会显示相应的报错。

进入到sky-server模块中

Mapper层

在SetmealMapper.java中添加list和getDishItemBySetmealId两个方法
/** * 动态条件查询套餐 * @param setmeal * @return */ List<Setmeal> list(Setmeal setmeal); /** * 根据套餐id查询菜品选项 * @param setmealId * @return */ @Select("select sd.name, sd.copies, d.image, d.description " + "from setmeal_dish sd left join dish d on sd.dish_id = d.id " + "where sd.setmeal_id = #{setmealId}") List<DishItemVO> getDishItemBySetmealId(Long setmealId);
创建SetmealMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.sky.mapper.SetmealMapper"> <select id="list" parameterType="Setmeal" resultType="Setmeal"> select * from setmeal <where> <if test="name != null"> and name like concat('%',#{name},'%') </if> <if test="categoryId != null"> and category_id = #{categoryId} </if> <if test="status != null"> and status = #{status} </if> </where> </select> </mapper>

Service层

创建SetmealService.java
public interface SetmealService { /** * 条件查询 * @param setmeal * @return */ List<Setmeal> list(Setmeal setmeal); /** * 根据id查询菜品选项 * @param id * @return */ List<DishItemVO> getDishItemById(Long id); }
创建SetmealServiceImpl.java
/** * 套餐业务实现 */ @Service @Slf4j public class SetmealServiceImpl implements SetmealService { @Autowired private SetmealMapper setmealMapper; @Autowired private SetmealDishMapper setmealDishMapper; @Autowired private DishMapper dishMapper; /** * 条件查询 * @param setmeal * @return */ public List<Setmeal> list(Setmeal setmeal) { List<Setmeal> list = setmealMapper.list(setmeal); return list; } /** * 根据id查询菜品选项 * @param id * @return */ public List<DishItemVO> getDishItemById(Long id) { return setmealMapper.getDishItemBySetmealId(id); } }
在DishService.java中添加listWithFlavor方法定义
/** * 条件查询菜品和口味 * @param dish * @return */ List<DishVO> listWithFlavor(Dish dish);
在DishServiceImpl.java中实现listWithFlavor方法
/** * 条件查询菜品和口味 * @param dish * @return */ public List<DishVO> listWithFlavor(Dish dish) { List<Dish> dishList = dishMapper.list(dish); List<DishVO> dishVOList = new ArrayList<>(); for (Dish d : dishList) { DishVO dishVO = new DishVO(); BeanUtils.copyProperties(d,dishVO); //根据菜品id查询对应的口味 List<DishFlavor> flavors = dishFlavorMapper.getByDishId(d.getId()); dishVO.setFlavors(flavors); dishVOList.add(dishVO); } return dishVOList; }

Controller层

创建DishController.java
@RestController("userDishController") @RequestMapping("/user/dish") @Slf4j @Api(tags = "C端-菜品浏览接口") public class DishController { @Autowired private DishService dishService; /** * 根据分类id查询菜品 * * @param categoryId * @return */ @GetMapping("/list") @ApiOperation("根据分类id查询菜品") public Result<List<DishVO>> list(Long categoryId) { Dish dish = new Dish(); dish.setCategoryId(categoryId); dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品 List<DishVO> list = dishService.listWithFlavor(dish); return Result.success(list); } }
创建CategoryController.java
@RestController("userCategoryController") @RequestMapping("/user/category") @Api(tags = "C端-分类接口") public class CategoryController { @Autowired private CategoryService categoryService; /** * 查询分类 * @param type * @return */ @GetMapping("/list") @ApiOperation("查询分类") public Result<List<Category>> list(Integer type) { List<Category> list = categoryService.list(type); return Result.success(list); } }
创建SetmealController.java
@RestController("userSetmealController") @RequestMapping("/user/setmeal") @Api(tags = "C端-套餐浏览接口") public class SetmealController { @Autowired private SetmealService setmealService; /** * 条件查询 * * @param categoryId * @return */ @GetMapping("/list") @ApiOperation("根据分类id查询套餐") public Result<List<Setmeal>> list(Long categoryId) { Setmeal setmeal = new Setmeal(); setmeal.setCategoryId(categoryId); setmeal.setStatus(StatusConstant.ENABLE); List<Setmeal> list = setmealService.list(setmeal); return Result.success(list); } /** * 根据套餐id查询包含的菜品列表 * * @param id * @return */ @GetMapping("/dish/{id}") @ApiOperation("根据套餐id查询包含的菜品列表") public Result<List<DishItemVO>> dishList(@PathVariable("id") Long id) { List<DishItemVO> list = setmealService.getDishItemById(id); return Result.success(list); } }

总结

今天主要学习了HttpClient的基础知识,简单的微信小程序开发,开发微信登录小程序的功能代码和导入商品浏览功能代码,学习了用户端的功能开发,完善了外卖项目。

靡不有初,鲜克有终。

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

Next.js国际化实战:i18next与next-i18next完整配置指南

1. 项目概述&#xff1a;为什么你的Next.js应用需要一个专业的国际化方案 如果你正在用Next.js开发一个面向全球用户的应用&#xff0c;那么“国际化”这个需求大概率已经摆在了你的面前。你可能已经尝试过一些简单的方法&#xff0c;比如在代码里硬编码不同语言的字符串&#…

作者头像 李华
网站建设 2026/5/14 6:17:46

哥斯达黎加:从热带雨林到高科技中心的崛起之路

1. 哥斯达黎加&#xff1a;一个被低估的高科技新星提起哥斯达黎加&#xff0c;你的第一反应是什么&#xff1f;是茂密的热带雨林、香醇的咖啡豆&#xff0c;还是悠闲的生态旅游&#xff1f;如果我告诉你&#xff0c;这个国土面积仅相当于美国新罕布什尔和佛蒙特两州之和的中美洲…

作者头像 李华
网站建设 2026/5/14 6:14:08

基于Next.js 14与Sanity构建高性能个人博客:全栈技术栈解析与实践

1. 项目概述&#xff1a;一个现代、高性能的个人博客系统 最近在折腾个人博客&#xff0c;发现了一个非常亮眼的开源项目——CaliCastle/cali.so。这不仅仅是一个博客模板&#xff0c;更是一个集成了当前前端最佳实践的完整个人网站解决方案。原作者Cali&#xff08;Calvin&am…

作者头像 李华
网站建设 2026/5/14 6:12:23

[Java+阿里云 SMS + Redis] 阿里云短信服务使用

一、阿里云短信SDK的申请 1.短信认证套餐的购买 要购买短信认证套餐&#xff0c;才能使用sdk发送验证码 1.1首先进入阿里云官网搜索短信服务 1.2进入控制台 作者只是简单的实现 发送验证码的功能实现&#xff0c;并不是实际的企业项目 如果想进行企业项目的开发&#xff0c;…

作者头像 李华
网站建设 2026/5/14 6:11:15

AI系统提示词安全防护:从泄露风险到后端代理实战

1. 项目概述&#xff1a;当系统提示词不再“秘密”最近在AI应用开发圈里&#xff0c;一个名为“asgeirtj/system_prompts_leaks”的项目引起了我的注意。这名字直译过来就是“系统提示词泄露”&#xff0c;听起来就有点意思。简单来说&#xff0c;这个项目收集并展示了在各种AI…

作者头像 李华
网站建设 2026/5/14 6:09:04

Attu终极指南:轻松掌握向量数据库的图形化管理神器

Attu终极指南&#xff1a;轻松掌握向量数据库的图形化管理神器 【免费下载链接】attu The Best GUI for Milvus 项目地址: https://gitcode.com/gh_mirrors/at/attu 还在为复杂的向量数据库命令行操作而头疼吗&#xff1f;想要一个直观的界面来管理你的Milvus实例吗&…

作者头像 李华