# 苍穹外卖 - day06# 课程内容HttpClient 微信小程序开发 微信登录 导入商品浏览功能代码 功能实现:微信登录 、商品浏览
微信登录效果图:
商品浏览效果图:
# HttpClient# 介绍HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HttpClient 作用:
为什么要在 Java 程序中发送 Http 请求?有哪些应用场景呢?
HttpClient 应用场景:
当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时
其实,应用程序本身并未实现这些功能,都是在应用程序里访问提供这些功能的服务,访问这些服务需要发送 HTTP 请求,并且接收响应数据,可通过 HttpClient 来实现。
HttpClient 的 maven 坐标:
1 2 3 4 5 <dependency > <groupId > org.apache.httpcomponents</groupId > <artifactId > httpclient</artifactId > <version > 4.5.13</version > </dependency >
HttpClient 的核心 API:
HttpClient:Http 客户端对象类型,使用该类型对象可发起 Http 请求。 HttpClients:可认为是构建器,可创建 HttpClient 对象。 CloseableHttpClient:实现类,实现了 HttpClient 接口。 HttpGet:Get 方式请求类型。 HttpPost:Post 方式请求类型。 HttpClient 发送请求步骤:
创建 HttpClient 对象 创建 Http 请求对象 调用 HttpClient 的 execute 方法发送请求 # 入门案例对 HttpClient 编程工具包有了一定了解后,那么,我们使用 HttpClient 在 Java 程序当中来构造 Http 的请求,并且把请求发送出去,接下来,就通过入门案例分别发送 GET 请求 和 POST 请求 ,具体来学习一下它的使用方法。
# GET 方式请求正常来说,首先,应该导入 HttpClient 相关的坐标,但在项目中,就算不导入,也可以使用相关的 API。
因为在项目中已经引入了 aliyun-sdk-oss 坐标:
1 2 3 4 <dependency > <groupId > com.aliyun.oss</groupId > <artifactId > aliyun-sdk-oss</artifactId > </dependency >
上述依赖的底层已经包含了 HttpClient 相关依赖。
故选择导入或者不导入均可。
进入到 sky-server 模块,编写测试代码,发送 GET 请求。
实现步骤:
创建 HttpClient 对象 创建请求对象 发送请求,接受响应结果 解析结果 关闭资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.sky.test;import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest public class HttpClientTest { @Test public void testGET () throws Exception{ CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet ("http://localhost:8080/user/shop/status" ); CloseableHttpResponse response = httpClient.execute(httpGet); int statusCode = response.getStatusLine().getStatusCode(); System.out.println("服务端返回的状态码为:" + statusCode); HttpEntity entity = response.getEntity(); String body = EntityUtils.toString(entity); System.out.println("服务端返回的数据为:" + body); response.close(); httpClient.close(); } }
在访问 http://localhost:8080/user/shop/status 请求时,需要提前启动项目。
测试结果:
# POST 方式请求在 HttpClientTest 中添加 POST 方式请求方法,相比 GET 请求来说,POST 请求若携带参数需要封装请求体对象,并将该对象设置在请求对象中。
实现步骤:
创建 HttpClient 对象 创建请求对象 发送请求,接收响应结果 解析响应结果 关闭资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Test public void testPOST () throws Exception{ CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost ("http://localhost:8080/admin/employee/login" ); JSONObject jsonObject = new JSONObject (); jsonObject.put("username" ,"admin" ); jsonObject.put("password" ,"123456" ); StringEntity entity = new StringEntity (jsonObject.toString()); entity.setContentEncoding("utf-8" ); entity.setContentType("application/json" ); httpPost.setEntity(entity); CloseableHttpResponse response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); System.out.println("响应码为:" + statusCode); HttpEntity entity1 = response.getEntity(); String body = EntityUtils.toString(entity1); System.out.println("响应数据为:" + body); response.close(); httpClient.close(); }
测试结果:
# 微信小程序开发# 介绍小程序是一种新的开放能力,开发者可以快速地开发一个小程序。可以在微信内被便捷地获取和传播,同时具有出色的使用体验。
官方网址:
https://mp.weixin.qq.com/cgi-bin/wx?token=&lang=zh_CN
小程序主要运行微信内部,可通过上述网站来整体了解微信小程序的开发。
首先,在进行小程序开发时,需要先去注册一个小程序,在注册的时候,它实际上又分成了不同的注册的主体。我们可以以个人的身份来注册一个小程序,当然,也可以以企业政府、媒体或者其他组织的方式来注册小程序。那么,不同的主体注册小程序,最终开放的权限也是不一样的。比如以个人身份来注册小程序,是无法开通支付权限的。若要提供支付功能,必须是企业、政府或者其它组织等。所以,不同的主体注册小程序后,可开发的功能是不一样的。
然后,微信小程序我们提供的一些开发的支持,实际上微信的官方是提供了一系列的工具来帮助开发者快速的接入 并且完成小程序的开发,提供了完善的开发文档,并且专门提供了一个开发者工具,还提供了相应的设计指南,同时也提供了一些小程序体验 DEMO,可以快速的体验小程序实现的功能。
最后,开发完一个小程序要上线,也给我们提供了详细地接入流程。
# 准备工作开发微信小程序之前需要做如下准备工作:
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 服务器,若不勾选,请求发送失败。
# 入门案例实际上,小程序的开发本质上属于前端开发,主要使用 JavaScript 开发,咱们现在的定位主要还是在后端,所以,对于小程序开发简单了解即可。
# 小程序目录结构小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:
文件说明:
app.js:
必须存在,主要存放小程序的逻辑代码
app.json:
必须存在,小程序配置文件,主要存放小程序的公共配置
app.wxss:
非必须存在,主要存放小程序公共样式表,类似于前端的 CSS 样式
对小程序主体三个文件了解后,其实一个小程序又有多个页面。比如说,有商品浏览页面、购物车的页面、订单支付的页面、商品的详情页面等等。那这些页面会放在哪呢?会存放在 pages 目录。
每个小程序页面主要由四个文件组成:
文件说明:
js 文件:
必须存在,存放页面业务逻辑代码,编写的 js 代码。
wxml 文件:
必须存在,存放页面结构,主要是做页面布局,页面效果展示的,类似于 HTML 页面。
json 文件:
非必须,存放页面相关的配置。
wxss 文件:
非必须,存放页面样式表,相当于 CSS 文件。
# 编写和编译小程序1). 编写
进入到 index.wxml,编写页面布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <view class ="container" > <view > {{msg}}</view > <view > <button type ="default" bindtap ="getUserInfo" > 获取用户信息</button > <image style ="width: 100px;height: 100px;" src ="{{avatarUrl}}" > </image > {{nickName}} </view > <view > <button type ="primary" bindtap ="wxlogin" > 微信登录</button > 授权码:{{code}} </view > <view > <button type ="warn" bindtap ="sendRequest" > 发送请求</button > 响应结果:{{result}} </view > </view >
进入到 index.js,编写业务逻辑代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 Page ({ data :{ msg :'hello world' , avatarUrl :'' , nickName :'' , code :'' , result :'' }, getUserInfo :function ( ){ wx.getUserProfile ({ desc : '获取用户信息' , success :(res ) => { console .log (res) this .setData ({ avatarUrl :res.userInfo .avatarUrl , nickName :res.userInfo .nickName }) } }) }, wxlogin :function ( ){ wx.login ({ success : (res ) => { console .log ("授权码:" +res.code ) this .setData ({ code :res.code }) } }) }, sendRequest :function ( ){ wx.request ({ url : 'http://localhost:8080/user/shop/status' , method :'GET' , success :(res ) => { console .log ("响应结果:" + res.data .data ) this .setData ({ result :res.data .data }) } }) }})
2). 编译
点击编译按钮
3). 运行效果
点击获取用户信息
点击微信登录
点击发送请求
因为请求 http://localhost:8080/user/shop/status,先要启动后台项目。
注:
设置不校验合法域名,若不勾选,请求发送失败。
# 发布小程序小程序的代码都已经开发完毕,要将小程序发布上线,让所有的用户都能使用到这个小程序。
点击上传按钮:
指定版本号:
上传成功:
把代码上传到微信服务器就表示小程序已经发布了吗?
其实并不是
当前小程序版本只是一个开发版本。
进到微信公众平台,打开版本管理页面。
需提交审核,变成审核版本,审核通过后,进行发布,变成线上版本。
一旦成为线上版本,这就说明小程序就已经发布上线了,微信用户就可以在微信里面去搜索和使用这个小程序了。
# 微信登录# 导入小程序代码开发微信小程序,本质上是属于前端的开发,我们的重点其实还是后端代码开发。所以,小程序的代码已经提供好了,直接导入到微信开发者工具当中,直接来使用就可以了。
1). 找到资料
2). 导入代码
AppID:使用自己的 AppID
3). 查看项目结构
主体的文件:app.js app.json app.wxss 项目的页面比较多,主要存放在 pages 目录。
4). 修改配置
因为小程序要请求后端服务,需要修改为自己后端服务的 ip 地址和端口号 (默认不需要修改)
common-->vendor.js--> 搜索 (ctrl+f)-->baseUri
# 微信登录流程微信登录:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
流程图:
步骤分析:
小程序端,调用 wx.login () 获取 code,就是授权码。 小程序端,调用 wx.request () 发送请求并携带 code,请求开发者服务器 (自己编写的后端服务)。 开发者服务端,通过 HttpClient 向微信接口服务发送请求,并携带 appId+appsecret+code 三个参数。 开发者服务端,接收微信接口服务返回的数据,session_key+opendId 等。opendId 是微信用户的唯一标识。 开发者服务端,自定义登录态,生成令牌 (token) 和 openid 等数据返回给小程序端,方便后绪请求身份校验。 小程序端,收到自定义登录态,存储 storage。 小程序端,后绪通过 wx.request () 发起业务请求时,携带 token。 开发者服务端,收到请求后,通过携带的 token,解析当前登录用户的 id。 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。 接下来,我们使用 Postman 进行测试。
说明:
调用 wx.login() 获取 临时登录凭证 code ,并回传到开发者服务器。 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识 UnionID (若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key 。 之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
实现步骤:
1). 获取授权码
点击确定按钮,获取授权码,每个授权码只能使用一次,每次测试,需重新获取。
2). 明确请求接口
请求方式、请求路径、请求参数
3). 发送请求
获取 session_key 和 openid
若出现 code been used 错误提示,说明授权码已被使用过,请重新获取
# 需求分析和设计# 产品原型用户进入到小程序的时候,微信授权登录之后才能点餐。需要获取当前微信用户的相关信息,比如昵称、头像等,这样才能够进入到小程序进行下单操作。是基于微信登录来实现小程序的登录功能,没有采用传统账户密码登录的方式。若第一次使用小程序来点餐,就是一个新用户,需要把这个新的用户保存到数据库当中完成自动注册。
登录功能原型图:
业务规则:
基于微信登录实现小程序的登录功能 如果是新用户需要自动完成注册 # 接口设计通过微信登录的流程,如果要完成微信登录的话,最终就要获得微信用户的 openid。在小程序端获取授权码后,向后端服务发送请求,并携带授权码,这样后端服务在收到授权码后,就可以去请求微信接口服务。最终,后端向小程序返回 openid 和 token 等数据。
基于上述的登录流程,就可以设计出该接口的请求参数 和返回数据 。
说明:
请求路径 /user/user/login, 第一个 user 代表用户端,第二个 user 代表用户模块。
# 表设计当用户第一次使用小程序时,会完成自动注册,把用户信息存储到 user 表中。
字段名 数据类型 说明 备注 id bigint 主键 自增 openid varchar(45) 微信用户的唯一标识 name varchar(32) 用户姓名 phone varchar(11) 手机号 sex varchar(2) 性别 id_number varchar(18) 身份证号 avatar varchar(500) 微信用户头像路径 create_time datetime 注册时间
说明:
手机号字段比较特殊,个人身份注册的小程序没有权限获取到微信用户的手机号。如果是以企业的资质 注册的小程序就能够拿到微信用户的手机号。
# 代码开发# 定义相关配置配置微信登录所需配置项:
application-dev.yml
1 2 3 4 sky: wechat: appid: wxffb3637a228223b8 secret: 84311df9199ecacdf4f12d27b6b9522d
application.yml
1 2 3 4 sky: wechat: appid: ${sky.wechat.appid} secret: ${sky.wechat.secret}
配置为微信用户生成 jwt 令牌时使用的配置项:
application.yml
1 2 3 4 5 6 7 8 9 10 11 sky: jwt: admin-secret-key: itcast admin-ttl: 7200000 admin-token-name: token user-secret-key: itheima user-ttl: 7200000 user-token-name: authentication
# DTO 设计根据传入参数设计 DTO 类:
在 sky-pojo 模块,UserLoginDTO.java 已定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.sky.dto;import lombok.Data;import java.io.Serializable;@Data public class UserLoginDTO implements Serializable { private String code; }
# VO 设计根据返回数据设计 VO 类:
在 sky-pojo 模块,UserLoginVO.java 已定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.sky.vo;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;@Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserLoginVO implements Serializable { private Long id; private String openid; private String token; }
# Controller 层根据接口定义创建 UserController 的 login 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com.sky.controller.user;import com.sky.constant.JwtClaimsConstant;import com.sky.dto.UserLoginDTO;import com.sky.entity.User;import com.sky.properties.JwtProperties;import com.sky.result.Result;import com.sky.service.UserService;import com.sky.utils.JwtUtil;import com.sky.vo.UserLoginVO;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;import java.util.Map;@RestController @RequestMapping("/user/user") @Api(tags = "C端用户相关接口") @Slf4j public class UserController { @Autowired private UserService userService; @Autowired private JwtProperties jwtProperties; @PostMapping("/login") @ApiOperation("微信登录") public Result<UserLoginVO> login (@RequestBody UserLoginDTO userLoginDTO) { log.info("微信用户登录:{}" ,userLoginDTO.getCode()); User user = userService.wxLogin(userLoginDTO); 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 接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.sky.service;import com.sky.dto.UserLoginDTO;import com.sky.entity.User;public interface UserService { User wxLogin (UserLoginDTO userLoginDTO) ; }
# Service 层实现类创建 UserServiceImpl 实现类:
实现获取微信用户的 openid 和微信登录功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package com.sky.service.impl;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.sky.constant.MessageConstant;import com.sky.dto.UserLoginDTO;import com.sky.entity.User;import com.sky.exception.LoginFailedException;import com.sky.mapper.UserMapper;import com.sky.properties.WeChatProperties;import com.sky.service.UserService;import com.sky.utils.HttpClientUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.time.LocalDateTime;import java.util.HashMap;import java.util.Map;@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; public User wxLogin (UserLoginDTO userLoginDTO) { String openid = getOpenid(userLoginDTO.getCode()); 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; } private String getOpenid (String code) { 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 接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.sky.mapper;import com.sky.entity.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;@Mapper public interface UserMapper { @Select("select * from user where openid = #{openid}") User getByOpenid (String openid) ; void insert (User user) ; }
创建 UserMapper.xml 映射文件:
1 2 3 4 5 6 7 8 9 10 11 <?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 校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.sky.interceptor;import com.sky.constant.JwtClaimsConstant;import com.sky.context.BaseContext;import com.sky.properties.JwtProperties;import com.sky.utils.JwtUtil;import io.jsonwebtoken.Claims;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Component @Slf4j public class JwtTokenUserInterceptor implements HandlerInterceptor { @Autowired private JwtProperties jwtProperties; public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true ; } String token = request.getHeader(jwtProperties.getUserTokenName()); 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); return true ; } catch (Exception ex) { response.setStatus(401 ); return false ; } } }
在 WebMvcConfiguration 配置类中注册拦截器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Autowired private JwtTokenUserInterceptor jwtTokenUserInterceptor; protected void addInterceptors (InterceptorRegistry registry) { log.info("开始注册自定义拦截器..." ); registry.addInterceptor(jwtTokenUserInterceptor) .addPathPatterns("/user/**" ) .excludePathPatterns("/user/user/login" ) .excludePathPatterns("/user/shop/status" ); }
# 功能测试重新编译小程序,进行登录,获取到 openid 和 token 数据
查看后台日志
查看数据库 user 表,第一次登录,会自动注册
# 导入商品浏览功能代码# 需求分析和设计# 产品原型用户登录成功后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息,需要展示 按钮,否则显示 按钮。
菜品列表效果图 菜品口味效果图
套餐列表效果图 套餐详情效果图
# 接口设计根据上述原型图先粗粒度 设计接口,共包含 4 个接口。
接口设计:
查询分类 根据分类 id 查询菜品 根据分类 id 查询套餐 根据套餐 id 查询包含的菜品 接下来细粒度 分析每个接口,明确每个接口的请求方式、请求路径、传入参数和返回值。
1). 查询分类
2). 根据分类 id 查询菜品
3). 根据分类 id 查询套餐
4). 根据套餐 id 查询包含的菜品
# 代码导入导入资料中的商品浏览功能代码即可
可按照 mapper-->service-->controller 依次导入,这样代码不会显示相应的报错。
进入到 sky-server 模块中
# Mapper 层在 SetmealMapper.java 中添加 list 和 getDishItemBySetmealId 两个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 List<Setmeal> list (Setmeal setmeal) ; @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 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.sky.service;import com.sky.dto.SetmealDTO;import com.sky.dto.SetmealPageQueryDTO;import com.sky.entity.Setmeal;import com.sky.result.PageResult;import com.sky.vo.DishItemVO;import com.sky.vo.SetmealVO;import java.util.List;public interface SetmealService { List<Setmeal> list (Setmeal setmeal) ; List<DishItemVO> getDishItemById (Long id) ; }
创建 SetmealServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.sky.service.impl;import com.sky.entity.Setmeal;import com.sky.mapper.DishMapper;import com.sky.mapper.SetmealDishMapper;import com.sky.mapper.SetmealMapper;import com.sky.service.SetmealService;import com.sky.vo.DishItemVO;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service @Slf4j public class SetmealServiceImpl implements SetmealService { @Autowired private SetmealMapper setmealMapper; @Autowired private SetmealDishMapper setmealDishMapper; @Autowired private DishMapper dishMapper; public List<Setmeal> list (Setmeal setmeal) { List<Setmeal> list = setmealMapper.list(setmeal); return list; } public List<DishItemVO> getDishItemById (Long id) { return setmealMapper.getDishItemBySetmealId(id); } }
在 DishService.java 中添加 listWithFlavor 方法定义
1 2 3 4 5 6 List<DishVO> listWithFlavor (Dish dish) ;
在 DishServiceImpl.java 中实现 listWithFlavor 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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); List<DishFlavor> flavors = dishFlavorMapper.getByDishId(d.getId()); dishVO.setFlavors(flavors); dishVOList.add(dishVO); } return dishVOList; }
# Controller 层创建 DishController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.sky.controller.user;import com.sky.constant.StatusConstant;import com.sky.entity.Dish;import com.sky.result.Result;import com.sky.service.DishService;import com.sky.vo.DishVO;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController("userDishController") @RequestMapping("/user/dish") @Slf4j @Api(tags = "C端-菜品浏览接口") public class DishController { @Autowired private DishService dishService; @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.sky.controller.user;import com.sky.entity.Category;import com.sky.result.Result;import com.sky.service.CategoryService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController("userCategoryController") @RequestMapping("/user/category") @Api(tags = "C端-分类接口") public class CategoryController { @Autowired private CategoryService categoryService; @GetMapping("/list") @ApiOperation("查询分类") public Result<List<Category>> list (Integer type) { List<Category> list = categoryService.list(type); return Result.success(list); } }
创建 SetmealController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.sky.controller.user;import com.sky.constant.StatusConstant;import com.sky.entity.Setmeal;import com.sky.result.Result;import com.sky.service.SetmealService;import com.sky.vo.DishItemVO;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController("userSetmealController") @RequestMapping("/user/setmeal") @Api(tags = "C端-套餐浏览接口") public class SetmealController { @Autowired private SetmealService setmealService; @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); } @GetMapping("/dish/{id}") @ApiOperation("根据套餐id查询包含的菜品列表") public Result<List<DishItemVO>> dishList (@PathVariable("id") Long id) { List<DishItemVO> list = setmealService.getDishItemById(id); return Result.success(list); } }
# 功能测试重启服务器、重新编译小程序
微信登录进入首页
菜品和套餐分类查询:
具体分类下的菜品查询:
菜品口味查询: