chenjunda
长期主义

苍穹外卖day06

2025-07-30 javaWeb

苍穹外卖day06

1. HttpClient

1.1 介绍

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

image-20221203185003231

HttpClient作用:

  • 发送HTTP请求
  • 接收响应数据

为什么要在Java程序中发送Http请求?有哪些应用场景呢?

HttpClient应用场景:

当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时

其实,应用程序本身并未实现这些功能,都是在应用程序里访问提供这些功能的服务,访问这些服务需要发送HTTP请求,并且接收响应数据,可通过HttpClient来实现。

image-20221203185427462

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方法发送请求

1.2 入门案例

对HttpClient编程工具包有了一定了解后,那么,我们使用HttpClient在Java程序当中来构造Http的请求,并且把请求发送出去,接下来,就通过入门案例分别发送GET请求POST请求,具体来学习一下它的使用方法。

1.2.1 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请求。

实现步骤:

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接受响应结果
  4. 解析结果
  5. 关闭资源
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 {

/**
* 测试通过httpclient发送GET方式的请求
*/
@Test
public void testGET() throws Exception{
//创建httpclient对象
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请求时,需要提前启动项目。

测试结果:

image-20221203195917572

1.2.2 POST方式请求

在HttpClientTest中添加POST方式请求方法,相比GET请求来说,POST请求若携带参数需要封装请求体对象,并将该对象设置在请求对象中。

实现步骤:

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接收响应结果
  4. 解析响应结果
  5. 关闭资源
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
/**
* 测试通过httpclient发送POST方式的请求
*/
@Test
public void testPOST() throws Exception{
// 创建httpclient对象
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();
}

测试结果:

image-20221203201023925

2. 微信小程序开发

2.1 介绍

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

image-20221203204712437

**首先,**在进行小程序开发时,需要先去注册一个小程序,在注册的时候,它实际上又分成了不同的注册的主体。我们可以以个人的身份来注册一个小程序,当然,也可以以企业政府、媒体或者其他组织的方式来注册小程序。那么,不同的主体注册小程序,最终开放的权限也是不一样的。比如以个人身份来注册小程序,是无法开通支付权限的。若要提供支付功能,必须是企业、政府或者其它组织等。所以,不同的主体注册小程序后,可开发的功能是不一样的。

image-20221203210640473

**然后,**微信小程序我们提供的一些开发的支持,实际上微信的官方是提供了一系列的工具来帮助开发者快速的接入
并且完成小程序的开发,提供了完善的开发文档,并且专门提供了一个开发者工具,还提供了相应的设计指南,同时也提供了一些小程序体验DEMO,可以快速的体验小程序实现的功能。

image-20221203211226920

**最后,**开发完一个小程序要上线,也给我们提供了详细地接入流程。

image-20221203211535565

2.2 准备工作

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

  • 注册小程序
  • 完善小程序信息
  • 下载开发者工具

3. 微信登录

3.1 导入小程序代码

开发微信小程序,本质上是属于前端的开发,我们的重点其实还是后端代码开发。所以,小程序的代码已经提供好了,直接导入到微信开发者工具当中,直接来使用就可以了。

3.2 微信登录流程

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

流程图:

image-20221204211800753

步骤分析:

  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. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

接下来,我们使用Postman进行测试。

说明:

  1. 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

实现步骤:

1). 获取授权码

点击确定按钮,获取授权码,每个授权码只能使用一次,每次测试,需重新获取。

image-20221204222008130

2). 明确请求接口*

请求方式、请求路径、请求参数

image-20221204222434054

3). 发送请求

获取session_key和openid

image-20221204223956568

3.3 需求分析和设计

3.3.1 产品原型

用户进入到小程序的时候,微信授权登录之后才能点餐。需要获取当前微信用户的相关信息,比如昵称、头像等,这样才能够进入到小程序进行下单操作。是基于微信登录来实现小程序的登录功能,没有采用传统账户密码登录的方式。若第一次使用小程序来点餐,就是一个新用户,需要把这个新的用户保存到数据库当中完成自动注册。

登录功能原型图:

image-20221205173711304

业务规则:

  • 基于微信登录实现小程序的登录功能
  • 如果是新用户需要自动完成注册

3.3.2 接口设计

通过微信登录的流程,如果要完成微信登录的话,最终就要获得微信用户的openid。在小程序端获取授权码后,向后端服务发送请求,并携带授权码,这样后端服务在收到授权码后,就可以去请求微信接口服务。最终,后端向小程序返回openid和token等数据。

基于上述的登录流程,就可以设计出该接口的请求参数返回数据

image-20221205175429394

image-20221205175441256

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

3.3.3 表设计

当用户第一次使用小程序时,会完成自动注册,把用户信息存储到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 注册时间

**说明:**手机号字段比较特殊,个人身份注册的小程序没有权限获取到微信用户的手机号。如果是以企业的资质
注册的小程序就能够拿到微信用户的手机号。

3.4 代码开发

3.4.1 定义相关配置

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

application-dev.yml

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

3.4.2 DTO设计

根据传入参数设计DTO类:

image-20221205183625049

在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;

/**
* C端用户登录
*/
@Data
public class UserLoginDTO implements Serializable {

private String code;

}

3.4.3 VO设计

根据返回数据设计VO类:

image-20221205183923272

在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;

}

3.4.4 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;

/**
* 微信登录
* @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常量已定义。

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

登录功能首先就是要找到user获取信息,然后生成一个令牌,令牌需要一个密钥(配置文件已写好),一个时长,一个存储信息的map,信息就是一个id

然后手动构建VO

3.4.5 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 {

/**
* 微信登录
* @param userLoginDTO
* @return
*/
User wxLogin(UserLoginDTO userLoginDTO);
}

3.4.6 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;

/**
* 微信登录
* @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;
}
}

获取User的逻辑做重要的是获取用户唯一的openid,所以先使用HttpCliehttpclienntUtil发送链接到服务器获取json数据中的openid(发送的数据要保存到map里),知道openid了就可以去数据库查用户了

3.4.7 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 {

/**
* 根据openid查询用户
* @param openid
* @return
*/
@Select("select * from user where openid = #{openid}")
User getByOpenid(String openid);

/**
* 插入数据
* @param user
*/
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>

如果在 <insert> 标签中没有显式写 parameterType,MyBatis 会根据方法签名中的参数类型自动推断。

只有当参数类型不明确时(比如多个参数,或者需要传递 MapList 等复杂类型时),才需要显式指定 parameterType

3.4.8 编写拦截器

**编写拦截器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;

/**
* 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配置类中注册拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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");
}

4. 导入商品浏览功能代码

4.1 需求分析和设计

4.1.1 产品原型

4.1.2 接口设计

4.2.1 Mapper层

在SetmealMapper.java中添加list和getDishItemBySetmealId两个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 动态条件查询套餐
* @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文件

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>

4.2.2 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 {

/**
* 条件查询
* @param setmeal
* @return
*/
List<Setmeal> list(Setmeal setmeal);

/**
* 根据id查询菜品选项
* @param id
* @return
*/
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;

/**
* 条件查询
* @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方法定义

1
2
3
4
5
6
/**
* 条件查询菜品和口味
* @param dish
* @return
*/
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
/**
* 条件查询菜品和口味
* @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;
}

4.2.3 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;

/**
* 根据分类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

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;

/**
* 查询分类
* @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

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;

/**
* 条件查询
*
* @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);
}
}

4.3 功能测试

重启服务器、重新编译小程序

微信登录进入首页

菜品和套餐分类查询:

image-20221205205634465

具体分类下的菜品查询:

image-20221205205935019

菜品口味查询:

image-20221205210121546

4.4 代码提交

Author: chenjunda

Link: http://example.com/2025/07/30/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96day06/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
diary2025-8-2
NextPost >
苍穹外面day05
CATALOG
  1. 1. 苍穹外卖day06
    1. 1.1. 1. HttpClient
      1. 1.1.1. 1.1 介绍
      2. 1.1.2. 1.2 入门案例
        1. 1.1.2.1. 1.2.1 GET方式请求
        2. 1.1.2.2. 1.2.2 POST方式请求
    2. 1.2. 2. 微信小程序开发
      1. 1.2.1. 2.1 介绍
      2. 1.2.2. 2.2 准备工作
    3. 1.3. 3. 微信登录
      1. 1.3.1. 3.1 导入小程序代码
      2. 1.3.2. 3.2 微信登录流程
      3. 1.3.3. 3.3 需求分析和设计
        1. 1.3.3.1. 3.3.1 产品原型
        2. 1.3.3.2. 3.3.2 接口设计
        3. 1.3.3.3. 3.3.3 表设计
      4. 1.3.4. 3.4 代码开发
        1. 1.3.4.1. 3.4.1 定义相关配置
        2. 1.3.4.2. 3.4.2 DTO设计
        3. 1.3.4.3. 3.4.3 VO设计
        4. 1.3.4.4. 3.4.4 Controller层
        5. 1.3.4.5. 3.4.5 Service层接口
        6. 1.3.4.6. 3.4.6 Service层实现类
        7. 1.3.4.7. 3.4.7 Mapper层
        8. 1.3.4.8. 3.4.8 编写拦截器
    4. 1.4. 4. 导入商品浏览功能代码
      1. 1.4.1. 4.1 需求分析和设计
        1. 1.4.1.1. 4.1.1 产品原型
        2. 1.4.1.2. 4.1.2 接口设计
        3. 1.4.1.3. 4.2.1 Mapper层
        4. 1.4.1.4. 4.2.2 Service层
        5. 1.4.1.5. 4.2.3 Controller层
      2. 1.4.2. 4.3 功能测试
      3. 1.4.3. 4.4 代码提交