套餐业务开发
新增套餐
需求分析
新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmeal_dish表插入套餐和菜品关联数据。所以在新增套餐时,涉及到两个表:
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
- 实体类SetmealDish(直接从课程资料中导入即可,Setmeal实体前面+ 课程中已经导入过了)
- DTO SetmealDto(直接从课程资料中导入即可)
- Mapper接口SetmealDishMapper
- 业务层接口SetmealDishService
- 业务层实现类SetmealDishServicelmpl
- 控制层SetmealController
代码开发
在开发代码之前,需要梳理一下新增套餐时前端页面和服务端的交互过程:
1、页面(backend/ page/comboladd.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中
这个方法我们在前面在新增菜品获取菜品分类的时候我们已经写过了,只不过是请求参数不同而已。
2、页面发送ajax请求,请求服务端获取菜品分类数据并展示到添加菜品窗口中
3、页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中
前端页面可以根据菜品分类和菜品名称查找,这两个接口都是通用的,
根据菜品分类获取菜品信息:
根据菜品名称获取菜品信息:
const queryDishList = (params) => {
return $axios({
url: '/dish/list',
method: 'get',
params
})
}
代码编写:
@GetMapping("/list")
public Result<List<Dish>> getDishByCategory(Dish dish){
LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishLambdaQueryWrapper.eq(Dish::getStatus,1);
dishLambdaQueryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
dishLambdaQueryWrapper.like(dish.getName()!=null,Dish::getName,dish.getName());
dishLambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);
List<Dish> dishes = dishService.list(dishLambdaQueryWrapper);
return Result.success(dishes);
}
4、页面发送请求进行图片上传,请求服务端将图片保存到服务器
5、页面发送请求进行图片下载,将上传的图片进行回显
6、点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端
前端接口:
const addSetmeal = (params) => {
return $axios({
url: '/setmeal',
method: 'post',
data: { ...params }
})
}
前端请求的数据:
@Override
public void saveSetmealDto(SetmealDto setmealDto) {
this.save(setmealDto);
Long setmealId = setmealDto.getId();
List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
setmealDishes.forEach(setmealDish -> setmealDish.setSetmealId(setmealId));
setmealDishService.saveBatch(setmealDishes);
}
套餐分页查询
需求开发
前端接口:
const getSetmealPage = (params) => {
return $axios({
url: '/setmeal/page',
method: 'get',
params
})
}
代码开发
@GetMapping("page")
public Result<Page> page(@RequestParam(value = "page",defaultValue = "1") Integer page,
@RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize,
String name){
log.info("page={},pageSize={},name={}",page,pageSize,name);
Page<Setmeal> setmealPage = new Page<>();
//查询条件
LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealLambdaQueryWrapper.like(StringUtils.hasLength(name),Setmeal::getName,name);//按名称查找
setmealLambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);//按更新时间查找
setmealService.page(setmealPage,setmealLambdaQueryWrapper);
Page<SetmealDto> setmealDtoPage = new Page<>();
BeanUtils.copyProperties(setmealPage,setmealDtoPage,"records");
List<Setmeal> setmeals = setmealPage.getRecords();
List<SetmealDto> setmealDtos=new LinkedList<>();
setmeals.forEach(setmeal -> {
SetmealDto setmealDto = new SetmealDto();
//讲setmeal对象内容移到setmealDto
BeanUtils.copyProperties(setmeal,setmealDto);
//查找套装分类并给每个setmealDto的categoryName属性赋值
Long categoryId = setmealDto.getCategoryId();
Category category = categoryService.getById(categoryId);
if (category!=null){
setmealDto.setCategoryName(category.getName());
}
setmealDtos.add(setmealDto);
});
setmealDtoPage.setRecords(setmealDtos);
return Result.success(setmealDtoPage);
}
删除套餐
需求开发
前端接口:
const deleteSetmeal = (ids) => {
return $axios({
url: '/setmeal',
method: 'delete',
params: { ids }
})
}
代码开发
@Override
@Transactional
public void removeSetmealDtoBatch(List<Long> ids) {
List<Setmeal> setmeals = this.listByIds(ids);
setmeals.forEach(setmeal -> {
if(setmeal.getStatus()==1){
throw new SetmealException("启售中的套餐不能删除");
}
LambdaQueryWrapper<SetmealDish> setmealDishLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealDishLambdaQueryWrapper.eq(SetmealDish::getSetmealId,setmeal.getId());
setmealDishService.remove(setmealDishLambdaQueryWrapper);
});
this.removeByIds(ids);
setmeals.forEach(setmeal -> {
File file = new File(basePath + setmeal.getImage());
file.delete();
});
}
修改套餐信息
需求开发
在修改套装之前要获取套餐的信息
前端接口:
async init() {
querySetmealById(this.id).then((res) => {
if (String(res.code) === '1') {
this.ruleForm = res.data
this.ruleForm.status = res.data.status === '1'
this.ruleForm.price = res.data.price / 100
this.imageUrl = `/common/download?name=${res.data.image}`
this.checkList = res.data.setmealDishes
this.dishTable = res.data.setmealDishes
this.ruleForm.idType = res.data.categoryId
// this.ruleForm.password = ''
} else {
this.$message.error(res.msg || '操作失败')
}
})
}
const querySetmealById = (id) => {
return $axios({
url: `/setmeal/${id}`,
method: 'get'
})
}
代码开发
根据套餐ID获取套餐信息:
@Override
public SetmealDto getSetmealDtoById(Long id) {
Setmeal setmeal = this.getById(id);
SetmealDto setmealDto = new SetmealDto();
BeanUtils.copyProperties(setmeal,setmealDto);
LambdaQueryWrapper<SetmealDish> setmealDishLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealDishLambdaQueryWrapper.eq(SetmealDish::getSetmealId,id);
setmealDishLambdaQueryWrapper.orderByDesc(SetmealDish::getUpdateTime);
List<SetmealDish> setmealDishes = setmealDishService.list(setmealDishLambdaQueryWrapper);
setmealDto.setSetmealDishes(setmealDishes);
return setmealDto;
}
点击保存按钮之后,前端会发送以下请求给服务器,与添加套餐功能类似:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOSrnlxf-1678802048382)(null)]
@Override
@Transactional
public void updateSetmealDto(SetmealDto setmealDto) {
this.updateById(setmealDto);
Long setmealId = setmealDto.getId();
LambdaQueryWrapper<SetmealDish> setmealDishLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealDishLambdaQueryWrapper.eq(SetmealDish::getSetmealId,setmealId);
setmealDishService.remove(setmealDishLambdaQueryWrapper);
List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
setmealDishes.forEach(setmealDish -> setmealDish.setSetmealId(setmealId));
setmealDishService.saveBatch(setmealDishes);
}
手机验证码登录
短信发送
短信服务介绍
目前市面上有很多第三方提供的短信服务,这些第三方短信服务会和各个运营商(移动、联通、电信)对接,我们只需要注册成为会员并且按照提供的开发文档进行调用就可以发送短信。需要说明的是,这些短信服务一般都是收费服务。
常用短信服务:
- 阿里云
- 华为云
- 腾讯云
- 京东
- 梦网
- 乐信
阿里云短信服务-介绍
阿里云短信服务(Short Message Service)是广大企业客户快速触达手机用户所优选使用的通信能力。调用API或用群发助手,即可发送验证码、通知类和营销类短信;国内验证短信秒级触达,到达率最高可达99%;国际/港澳台短信覆盖200多个国家和地区,安全稳定,广受出海企业选用。
应用场景:
- 验证码
- 短信通知
- 推广短信
阿里云短信服务-注册账号
阿里云官网: https://www.aliyun.com/
点击官网首页注册按钮。
阿里云短信服务-设置短信签名
注册成功后,点击登录按钮进行登录。登录后进入短信服务管理页面,选择国内消息菜单:
短信签名是短信发送者的署名,表示发送方的身份。
阿里云短信服务-设置短信模板
切换到【模板管理】标签页:
短信模板包含短信发送内容、场景、变量信息。
阿里云短信服务-设置AccessKey
光标移动到用户头像上,在弹出的窗口中点击【AccessKey管理】∶
代码开发
在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:
1、在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信
2、在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求
开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
- 实体类User(直接从课程资料中导入即可)
- Mapper接口UserMapper
- 业务层接口UserService
- 业务层实现类UserServicelmpl
- 控制层UserController
- 工具类SMSutils、 ValidateCodeutils(直接从课程资料中导入即可)
前面我们已经完成了LogincheckFilter过滤器的开发,此过滤器用于检查用户的登录状态。我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行。
LoginCheckFilter过滤器添加
// 4-2、判断登录状态,如果已登录,则直接放行
if (request.getSession().getAttribute("user") != null) {
log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("user"));
Long userId= (Long) request.getSession().getAttribute("user");
BaseContext.setCurrentId(userId);
filterChain.doFilter(request, response);
return;
}
由于资料中代码不全login.js自行添加
function sendMsgApi(data) {
return $axios({
'url':'/user/sendMsg',
'method':'post',
data
})
}
login.html
// this.form.code = (Math.random()*1000000).toFixed(0)
sendMsgApi({phone:this.form.phone})
UserController处理post请求(发送验证码的请求)
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session){
//获取手机号
String phone=user.getPhone();
if(!StringUtils.isEmpty(phone)) {
//生成随机的4位验证码
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}",code);
//调用阿里云提供的短信服务API完成发送短信
//SMSUtils.sendMessage("瑞吉外卖","",phone,code);
//需要将生成的验证码保存到Session
session.setAttribute(phone,code);
return R.success("手机验证码短信发送成功");
}
return R.error("手机短信发送失败");
}
由于前端页面有部分代码缺失,建议拷贝资料中day05的front代码
在UserController编写login处理post请求
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session) {
log.info("map:{}", map.toString());
//获取手机号
String phone = map.get("phone").toString();
//获取验证码
String code = map.get("code").toString();
//从Session中获取保存的验证码
Object codeInSession = session.getAttribute(phone);
//进行验证码比对(页面提交的验证码和Session中保存的验证码比对)
if (codeInSession != null && codeInSession.equals(code)) {
//如果能够比对成功,说明登录成功
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone, phone);
User user = userService.getOne(queryWrapper);
if (user == null) {
//判断当前手机号是否为新用户,如果是新用户则自动完成注册
user = new User();
user.setPhone(phone);
user.setStatus(1);
userService.save(user);
}
session.setAttribute("user",user.getId());
return R.success(user);
}
return R.error("登陆失败");
}