苍穹外卖学习笔记(三)

一.公共字段自动填充

方法一:使用 AOP 切面编程方式

自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法
自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值
在Mapper的方法上加入AutoFill注解
  1. AutoFill 注解类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.sky.enumeration.OperationType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 自动填充注解
*/
@Target(ElementType.METHOD)//方法上
@Retention(RetentionPolicy.RUNTIME)//运行时
public @interface AutoFill {
//数据库操作类型
OperationType value();
}
  1. AutoFillAspect 切面类
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
import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
* 自动填充切面
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

/**
* 切入点
*/
@Pointcut("execution(* com.sky.service.impl.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void AutoFillPointcut() {
}

/**
* 前置通知
*/
@Before("AutoFillPointcut()")
public void autoFill(JoinPoint joinPoint) {
log.info("自动填充切面执行");
//获取当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取当前被拦截方法的签名
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取当前被拦截方法的方法名
OperationType value = autoFill.value();//获取当前被拦截方法的数据库操作类型
//获取当前被拦截方法的参数-实体对象
Object[] args = joinPoint.getArgs();//获取当前被拦截方法的参数
if (args == null || args.length == 0) {
return;
}
Object entity = args[0];
//准备填充的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//填充数据,通过反射
if (value == OperationType.INSERT) {
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

//通过反射调用方法
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
e.printStackTrace();
}
} else if (value == OperationType.UPDATE) {
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

//通过反射调用方法
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
  1. impl
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package com.sky.service.impl;

import com.alibaba.druid.util.Utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sky.annotation.AutoFill;
import com.sky.constant.MessageConstant;
import com.sky.constant.PasswordConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.EmployeeDTO;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.dto.EmployeeUppswdDTO;
import com.sky.entity.Employee;
import com.sky.enumeration.OperationType;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.EmployeeMapper;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import javax.swing.text.Utilities;
import java.time.LocalDateTime;
import java.util.Base64;

import java.security.SecureRandom;
import java.util.List;

@Slf4j
@Service
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
private EmployeeMapper employeeMapper;

private static final SecureRandom RANDOM = new SecureRandom();//随机数生成器
private static final int SALT_LENGTH = 16;//盐的长度

/**
* 员工登录
*
* @param employeeLoginDTO
* @return
*/


public Employee login(EmployeeLoginDTO employeeLoginDTO) {
String username = employeeLoginDTO.getUsername();
String password = employeeLoginDTO.getPassword();

//1、根据用户名查询数据库中的数据
// mybatis-plus的方法
log.info("username:{}", username);
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername, username);
Employee employee = employeeMapper.selectOne(queryWrapper);
log.info("employee:{}", employee);

//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
if (employee == null) {
//账号不存在
throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
}

//密码比对
//对前端传来的密码进行md5加盐处理,然后再进行比对
String salt = employee.getSalt();//盐
String hashedPassword = hashPassword(password, salt);//加密后的密码

if (!hashedPassword.equals(employee.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}

if (employee.getStatus() == StatusConstant.DISABLE) {
//账号被锁定
throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
}

//3、返回实体对象
return employee;
}

//对密码进行加密
private String hashPassword(String password, String salt) {
String saltedPassword = salt + password;
return DigestUtils.md5DigestAsHex(saltedPassword.getBytes());
}

/**
* 添加员工
*
* @param employeeDTO
* @return
*/
@Transactional
@Override
// @AutoFill(OperationType.INSERT)
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
BeanUtils.copyProperties(employeeDTO, employee);
employee.setStatus(StatusConstant.ENABLE);//默认启用
//生成盐
byte[] salt = new byte[SALT_LENGTH];
RANDOM.nextBytes(salt);
employee.setSalt(Base64.getEncoder().encodeToString(salt));
//对密码进行加密,默认密码为123456
String password = hashPassword(PasswordConstant.DEFAULT_PASSWORD, employee.getSalt());
employee.setPassword(password);
//通过注释的方式进行填充
// //设置创建时间和更新时间
// employee.setCreateTime(LocalDateTime.now());
// employee.setUpdateTime(LocalDateTime.now());
// //设置创建人和更新人
// employee.setCreateUser(BaseContext.getCurrentId());
// employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.insert(employee);
}

/**
* 分页查询员工
*
* @param employeePageQueryDTO
* @return
*/
@Transactional
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
// 创建分页对象
Page<Employee> page = new Page<>(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
// 创建查询条件
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
// 排除姓名为null的记录
queryWrapper.isNotNull(Employee::getName);
// 使用like方法进行模糊查询,参数化防止SQL注入
if (StringUtils.isNotBlank(employeePageQueryDTO.getName())) {
queryWrapper.like(Employee::getName, employeePageQueryDTO.getName());
}
// 根据创建时间降序排序
queryWrapper.orderByDesc(Employee::getCreateTime);
// 执行分页查询
Page<Employee> employeePage = employeeMapper.selectPage(page, queryWrapper);
// 提取结果
long total = employeePage.getTotal();
List<Employee> records = employeePage.getRecords();
// 返回结果
return new PageResult(total, records);
}

/**
* 修改员工状态
*
* @param id
* @param status
* @return
*/
@Override
@AutoFill(OperationType.UPDATE)
public void startOrStop(Long id, Integer status) {
// Employee employee = new Employee();
// employee.setId(id);
// employee.setStatus(status);
// employee.setUpdateTime(LocalDateTime.now());
// employee.setUpdateUser(BaseContext.getCurrentId());
Employee employee = Employee.builder()
.id(id)
.status(status)
// .updateTime(LocalDateTime.now())
// .updateUser(BaseContext.getCurrentId())
.build();
employeeMapper.updateById(employee);
}

/**
* 根据id查询员工
*
* @param id
* @return
*/
@Override
public Employee getById(Long id) {
Employee employee = employeeMapper.selectById(id);
employee.setPassword("********");
return employee;
}

/**
* 修改员工
*
* @param employeeDTO
*/
@Override
@AutoFill(OperationType.UPDATE)
public void update(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
BeanUtils.copyProperties(employeeDTO, employee);
// employee.setUpdateTime(LocalDateTime.now());
// employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.updateById(employee);
}

/**
* 修改密码
*
* @param employeeUppswdDTO
*/
@Override
@AutoFill(OperationType.UPDATE)
public void editPassword(EmployeeUppswdDTO employeeUppswdDTO) {
//1.根据id查询员工
Employee employee = employeeMapper.selectById(employeeUppswdDTO.getEmpId());
if (employee == null) {
throw new AccountNotFoundException("Employee not found");
}
//2.对原密码进行加密
String oldPassword = hashPassword(employeeUppswdDTO.getOldPassword(), employee.getSalt());
//3.比对原密码是否正确
if (!oldPassword.equals(employee.getPassword())) {
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
//4.对新密码进行加密
String newPassword = hashPassword(employeeUppswdDTO.getNewPassword(), employee.getSalt());
//5.修改密码
employee.setPassword(newPassword);
// employee.setUpdateTime(LocalDateTime.now());
// employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.updateById(employee);
}
}

由于我前面使用的 MP,所以注解需要放在 impl 中,并且由于 DTO 没有对应属性,需要在 controller 中将 DTO 参数换成 entity 实体才行,我这里不再详细写出

方法二:使用 mybatis-plus 自带的公共字段自动填充

1. 创建实体类并使用注解指定需要自动填充的字段
2. 创建自定义的字段填充处理器
注入Bean然后就可以自动填充了(不需要标注解哦 是不是方便许多😘)
  1. employee
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
package com.sky.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("employee")
public class Employee implements Serializable {

private static final long serialVersionUID = 1L;

@TableId(value = "id", type = IdType.AUTO)
private Long id;

private String username;

private String name;

private String password;

private String phone;

private String sex;

private String idNumber;

private Integer status;

// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;

@TableField(fill = FieldFill.INSERT)
private Long createUser;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;

private String salt;

}

  1. MyMetaObjectHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("开始插入填充...");
this.strictInsertFill(metaObject, AutoFillConstant.CREATE_TIME, LocalDateTime.class, LocalDateTime.now());//参数:实体类属性名,属性类型,属性值
this.strictInsertFill(metaObject, AutoFillConstant.CREATE_USER, Long.class, BaseContext.getCurrentId());
this.strictInsertFill(metaObject, AutoFillConstant.UPDATE_TIME, LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, AutoFillConstant.UPDATE_USER, Long.class, BaseContext.getCurrentId());
}

@Override
public void updateFill(MetaObject metaObject) {
log.info("开始更新填充...");
this.strictUpdateFill(metaObject, AutoFillConstant.UPDATE_TIME, LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, AutoFillConstant.UPDATE_USER, Long.class, BaseContext.getCurrentId());
}
}

二.新增菜品

1.图片上传

这里采用了阿里云 oss 对象存储服务

  1. application.yml
1
2
3
4
5
alioss:
endpoint: ${sky.alioss.endpoint}
access-key-id: ${sky.alioss.access-key-id}
access-key-secret: ${sky.alioss.access-key-secret}
bucket-name: ${sky.alioss.bucket-name}
  1. OssConfiguration 用来初始化,具体的配置文件在 common 中 properties 中
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
package com.sky.config;

import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* 阿里云OSS配置
*/
@Configuration
@Slf4j
public class OssConfiguration {

@Bean
@ConditionalOnMissingBean//当容器里没有指定的Bean的情况下创建该对象
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
log.info("初始化阿里云OSS配置");
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}
  1. CommonController.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
package com.sky.controller.admin;

import com.sky.constant.MessageConstant;
import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.UUID;

@Api(tags = "通用接口")
@RestController
@RequestMapping("/admin/common")
@Slf4j
public class CommonController {

@Autowired
private AliOssUtil aliOssUtil;

/**
* 文件上传
*
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result<String> upload(MultipartFile file) {
log.info("文件上传:{}", file);
try {
String originalFilename = file.getOriginalFilename();
//截取文件后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
String objectName = UUID.randomUUID().toString() + suffix;
String url = aliOssUtil.upload(file.getBytes(), objectName);
return Result.success(url);
} catch (IOException e) {
log.error("文件上传失败: {}", e.getMessage(), e);
}
return Result.error(MessageConstant.UPLOAD_FAILED);
}
}

2.具体新增菜品

  1. DishController
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
package com.sky.controller.admin;

import com.sky.dto.DishDTO;
import com.sky.result.Result;
import com.sky.service.DishService;
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;

/**
* 菜品管理
*/
@RestController
@RequestMapping("/admin/dish")
@Slf4j
@Api(tags = "菜品管理")
public class DishController {

@Autowired
private DishService dishService;

/**
* 新增菜品
*/
@PostMapping
@ApiOperation("新增菜品")
public Result save(@RequestBody DishDTO dishDTO) {
log.info("新增菜品:{}", dishDTO);
dishService.saveWithFlavor(dishDTO);
return Result.success();
}
}
  1. DishService
1
2
3
4
5
6
7
public interface DishService {

/**
* 新增菜品和口味
*/
void saveWithFlavor(DishDTO dishDTO);
}
  1. DishServiceImpl
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
package com.sky.service.impl;

import com.baomidou.mybatisplus.core.batch.MybatisBatch;
import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

@Service
@Slf4j
public class DishServiceImpl implements DishService {

@Autowired
private DishMapper dishMapper;

@Autowired
private SqlSessionFactory sqlSessionFactory;

/**
* 新增菜品和口味
*/
@Override
@Transactional
public void saveWithFlavor(DishDTO dishDTO) {
//向菜品表插入一条数据
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO, dish);
dishMapper.insert(dish);
dishMapper.selectById(dish.getId()); // 获取插入后的主键值
//获取菜品id
Long dishId = dish.getId();
//向口味表插入多条数据
List<DishFlavor> flavors = dishDTO.getFlavors();
if (flavors != null && !flavors.isEmpty()) {
// 设置 dishId
flavors.forEach(flavor -> flavor.setDishId(dishId));
// 批量插入
MybatisBatch<DishFlavor> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, flavors);
MybatisBatch.Method<DishFlavor> method = new MybatisBatch.Method<>(DishFlavorMapper.class);
mybatisBatch.execute(method.insert());
}
}
}

  1. DishMapper
1
2
3
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}
  1. DishFlavorMapper
1
2
3
4
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {

}
  1. 注意需要在实体类中添加主键自增策略,也可以配置主键全局自增,如果还是默认的雪花算法,需要把数据库重建
1
2
3
4
5
6
7
8
9
10
# mybatis-plus配置
mybatis-plus:
configuration:
# 驼峰命名
map-underscore-to-camel-case: true
# 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto

三.菜品分页查询

  1. Controller
1
2
3
4
5
6
7
8
9
10
/**
* 分页查询菜品
*/
@GetMapping("/page")
@ApiOperation("分页查询菜品")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {
log.info("分页查询菜品:{}", dishPageQueryDTO);
PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
return Result.success(pageResult);
}
  1. Service
1
2
3
4
/**
* 分页查询菜品
*/
PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);
  1. Impl
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
/**
* 分页查询菜品
*/
@Override
@Transactional
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
// 创建分页对象
Page<DishVO> page = new Page<>(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
// 创建查询条件包装器
QueryWrapper<Dish> queryWrapper = new QueryWrapper<>();
queryWrapper.isNotNull("d.name");
// 如果提供了名称,则添加模糊查询条件
if (StringUtils.isNotBlank(dishPageQueryDTO.getName())) {
queryWrapper.like("d.name", dishPageQueryDTO.getName());
}
// 如果提供了分类id,则添加等值查询条件
if (dishPageQueryDTO.getCategoryId() != null) {
queryWrapper.eq("d.category_id", dishPageQueryDTO.getCategoryId());
}
// 如果提供了状态,则添加等值查询条件
if (dishPageQueryDTO.getStatus() != null) {
queryWrapper.eq("d.status", dishPageQueryDTO.getStatus());
}
// 按创建时间降序排序
queryWrapper.orderByDesc("d.create_time");
// 执行查询
System.out.println(queryWrapper.getCustomSqlSegment());
Page<DishVO> dishVOPage = dishMapper.selectPage(page, queryWrapper);
log.info("分页查询菜品:{}", dishVOPage);
// 获取总记录数和记录列表
long total = dishVOPage.getTotal();
List<DishVO> records = dishVOPage.getRecords();
// 返回分页结果
return new PageResult(total, records);
}
  1. mapper
1
2
3
4
5
6
7
8
9
@Mapper
public interface DishMapper extends BaseMapper<Dish> {

/**
* 分页查询菜品
*/
@Select("SELECT d.*, c.name AS categoryName FROM dish d LEFT JOIN category c ON d.category_id = c.id ${ew.customSqlSegment}")
Page<DishVO> selectPage(Page<DishVO> page, @Param(Constants.WRAPPER) QueryWrapper<Dish> queryWrapper);
}

四.删除菜品

业务规则:

  1. 可以一次删除一个菜品,也可以一次删除多个菜品
  2. 起售中的菜品不能删除
  3. 被套餐关联得菜品不能删除
  4. 删除菜品后,关联得口味数据也需要删除掉
    一共需要操作三个表,注意加@Transactional 事物注解
  5. Controller
1
2
3
4
5
6
7
8
9
10
/**
* 删除菜品
*/
@DeleteMapping
@ApiOperation("删除菜品")
public Result delete(@RequestParam List<Long> ids) {//@RequestParam接收请求参数
log.info("删除菜品:{}", ids);
dishService.deleteBatch(ids);
return Result.success();
}
  1. Service
1
2
3
4
/**
* 删除菜品
*/
void deleteBatch(List<Long> ids);
  1. Impl
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
/**
* 删除菜品
*/
@Override
@Transactional
public void deleteBatch(List<Long> ids) {
// 判断当前菜品是否可以删除-是否存在启售菜品
List<Dish> dishes = dishMapper.selectBatchIds(ids);
for (Dish dish : dishes) {
if (dish.getStatus().equals(StatusConstant.ENABLE)) {
throw new RuntimeException(MessageConstant.DISH_ON_SALE);
}
}
// 判断当前菜品是否可以删除-是否存在套餐
List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);
if (setmealIds != null && !setmealIds.isEmpty()) {
throw new RuntimeException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
// 删除菜品
dishMapper.deleteBatchIds(ids);
// 删除口味
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(DishFlavor::getDishId, ids);
dishFlavorMapper.delete(queryWrapper);
}
  1. mapper
1
2
3
4
5
6
7
8
@Mapper
public interface SetmealDishMapper extends BaseMapper<SetmealDish> {

@Select.List({
@Select("SELECT setmeal_id FROM setmeal_dish WHERE dish_id IN (#{ids})")
})
List<Long> getSetmealIdsByDishIds(List<Long> ids);
}

五.修改菜品

  1. 根据 id 查询菜品
  2. 根据类型查询分类(已实现)
  3. 文件上传(已实现)
  4. 修改菜品
    加上@Transactional 事务注解
  5. Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 根据id查询菜品
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询菜品")
public Result<DishVO> getById(@PathVariable Long id) {//@PathVariable接收请求路径中的参数
log.info("根据id查询菜品:{}", id);
DishVO dishVO = dishService.getByIdWithFlavor(id);
return Result.success(dishVO);
}

/**
* 修改菜品
*/
@PutMapping
@ApiOperation("修改菜品")
public Result update(@RequestBody DishDTO dishDTO) {
log.info("修改菜品:{}", dishDTO);
dishService.updateWithFlavor(dishDTO);
return Result.success();
}
  1. Service
1
2
3
4
5
6
7
8
9
/**
* 根据id查询菜品和口味
*/
DishVO getByIdWithFlavor(Long id);

/**
* 更新菜品和口味
*/
void updateWithFlavor(DishDTO dishDTO);
  1. Impl
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
/**
* 根据id查询菜品和口味
*/
@Override
@Transactional
public DishVO getByIdWithFlavor(Long id) {
// 查询菜品
Dish dish = dishMapper.selectById(id);
// 查询口味
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId, id);
List<DishFlavor> flavors = dishFlavorMapper.selectList(queryWrapper);
// 封装结果
DishVO dishVO = new DishVO();
BeanUtils.copyProperties(dish, dishVO);
dishVO.setFlavors(flavors);
return dishVO;
}

/**
* 更新菜品和口味
*/
@Override
@Transactional
public void updateWithFlavor(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO, dish);
dishMapper.updateById(dish);
// 删除原有口味
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId, dish.getId());
dishFlavorMapper.delete(queryWrapper);
// 插入新口味
List<DishFlavor> flavors = dishDTO.getFlavors();
if (flavors != null && !flavors.isEmpty()) {
flavors.forEach(flavor -> flavor.setDishId(dish.getId()));
// 批量插入
MybatisBatch<DishFlavor> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, flavors);
MybatisBatch.Method<DishFlavor> method = new MybatisBatch.Method<>(DishFlavorMapper.class);
mybatisBatch.execute(method.insert());
}
}

六. 状态

  1. Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 修改菜品状态
*/
@PostMapping("/status/{status}")
@ApiOperation("修改菜品状态")
public Result updateStatus(@RequestParam Long id, @PathVariable Integer status) {//RequestParam接收请求参数,PathVariable接收请求路径中的参数
log.info("修改菜品状态:{}", id);
dishService.updateStatus(id, status);
return Result.success();
}

/**
* 根据分类id查询菜品
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<Dish>> listResult(@RequestParam Long categoryId) {
log.info("根据分类id查询菜品:{}", categoryId);
List<Dish> list = dishService.listByCategoryId(categoryId);
return Result.success(list);
}
  1. Service
1
2
3
4
5
6
7
8
9
/**
* 更新菜品状态
*/
void updateStatus(Long id, Integer status);

/**
* 根据分类id查询菜品
*/
List<Dish> listByCategoryId(Long categoryId);
  1. Impl
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
/**
* 更新菜品状态
*/
@Override
@Transactional
public void updateStatus(Long id, Integer status) {
Dish dish = dishMapper.selectById(id);
if (dish == null) {
throw new RuntimeException(MessageConstant.DISH_NOT_FOUND);
}
dish.setStatus(status);
dishMapper.updateById(dish);

if (Objects.equals(status, StatusConstant.DISABLE)){
// 如果是停售操作,还需要将包含当前菜品的套餐也停售
List<Long> ids = new ArrayList<>();
ids.add(id);
// 查询包含当前菜品的套餐
LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SetmealDish::getDishId, ids);
List<Long> setmealIds = setmealDishMapper.selectList(queryWrapper).stream().map(SetmealDish::getSetmealId).distinct().toList();
if (!setmealIds.isEmpty()) {
throw new RuntimeException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
}
}

/**
* 根据分类id查询菜品
*/
@Override
@Transactional
public List<Dish> listByCategoryId(Long categoryId) {
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Dish::getCategoryId, categoryId);
List<Dish> dishes = dishMapper.selectList(queryWrapper);
log.info("根据分类id查询菜品:{}", dishes);
return dishes;
}