当前位置: 首页 > news >正文

苍穹外卖 项目记录 第六天

一 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的基础知识,简单的微信小程序开发,开发微信登录小程序的功能代码和导入商品浏览功能代码,学习了用户端的功能开发,完善了外卖项目。

靡不有初,鲜克有终。

http://www.jsqmd.com/news/813545/

相关文章:

  • srcdoc属性怎么内嵌HTML_iframe直接注入【技巧】
  • EDA数据管理难题的通用解法:规则引擎驱动的设计对象抽象
  • 深耕高性价比多模型聚合平台赛道,这些企业值得重点关注
  • 扼流圈GNSS监测站
  • SkillsOver:AI代理安全审计工具,防御HTML注入与供应链攻击
  • -g安装和不使用-g安装的区别,本地开发环境和生产环境
  • 安培匝数抵消法:精准测量大直流偏置下微小电流纹波的工程实践
  • 图片怎么去水印?2026图片去水印方法实测 + 好用工具推荐
  • 3步解锁全功能:Cursor Free VIP智能加速方案指南
  • [Java+阿里云 SMS + Redis] 阿里云短信服务使用
  • 金融机器学习实战:从特征工程到投资组合优化的完整工具库解析
  • 深入Android系统源码:screencap命令背后,SurfaceFlinger如何“画”出一张图?
  • DeepSeek模型观测从黑盒到透明:手把手搭建Grafana可观测性看板(含Prometheus采集全链路)
  • 从嵌入式到FPGA:思维转变、实战入门与软硬件协同设计指南
  • Next.js国际化实战:i18next与next-i18next完整配置指南
  • 【干货】SFP连接器选型指南:笼子与连接器怎么配?光口速率、散热结构、压力配合技巧全解析 | VOOHU 沃虎电子
  • 掌握RCTCOE与12种核心模式,解锁高效AI提示词工程实战
  • 从零到一:我的Elsevier期刊LaTeX投稿实战与避坑指南
  • 粒子物理模拟的GPU加速与NLO计算优化
  • 大语言模型应用揭秘:从摘要引擎到AI Agents的演进之路!
  • 汽车智能座舱演进:从手机映射到原生系统的交互革命
  • ARM架构缓存维护指令详解与应用实践
  • 开发者工作流自动化:从零构建标准化项目脚手架与质量守护体系
  • 半导体创业融资新路径:产业资本联盟与轻量化创新模式探索
  • 六要素超声波气象站:告别传统机械风杯与翻斗雨量计
  • 芯片制造回流:数据驱动良率提升与智能运营的实践路径
  • 神经网络训练核心:梯度下降及其变体详解,数据挖掘深度学习课程(附代码和实战)
  • 郑州金橙智能嵌入式培训实战能力深度评测
  • 构建支持多模型切换的智能内容审核与打标系统
  • Python使用3种方法实现数据采集