1:添加员工
需求分析
产品原型:
账号必须是唯一的。
手机号必须是合法手机号
身份证号必须是合法身份证号
密码默认为123456
使用Json数据返回给前端,请求方式为post,后端响应给前端的数据应封装成一个标准的Result对象,包括code(成功,失败码),data(JSON格式的数据),msg(提示信息)
项目约定:
管理端发出的请求,统一使用/admin作为前缀
用户端发出的请求,统一使用/user作为前缀
用户表设计(employee):
字段名 | 数据类型 | 说明 | 备注 |
id | bigint | 主键 | 自增 |
name | varchar(32) | 姓名 | |
username | varchar(32) | 用户名 | 唯一 |
password | varchar(64) | 密码 | |
phone | varchar(11) | 手机号 | |
sex | varchar(2) | 性别 | |
id_number | varchar(18) | 身份证号 | |
status | int | 账号状态 | 1正常 0锁定 |
create_time | Datetime | 创建时间 | |
update_time | Datetime | 最后修改时间 | |
create_user | bigint | 创建人id | |
update_user | bigint | 最后修改人id |
代码开发
1.根据新增员工接口设计对应的DTO
1 当前端提交的数据与实体类中对应的字段差别比较大的时候,使用DTO来封装数据,提供前端所需要的字段。
2 实体类中除了前端传来的属性还有其他属性
3 创建EmployeeDTO来接收前端传来的数据
2.在新增员工的时候,要将DTO对象转换为实体对象,然后对实体对象进行赋值的时候,可以采用属性拷贝(前提条件:属性名必须是一致的)
3 在设置账号状态的时候,默认正常状态为1,0表示锁定,可以定义一个常量类来表示0和1,尽量不要使用魔法数字。
4 由于密码是默认123456,可以设置一个常量类
代码完善
程序存在的问题:
录入的用户名已存在,抛出异常之后没有处理
解决:
在handler中捕获相应的异常并且处理
/**
* 处理SQL异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
// Duplicate entry 'zhangsan' for key 'employee.idx_username'
String message = ex.getMessage();
if(message.contains("Duplicate entry")){
String[] split = message.split(" ");
String username = split[2] ;
// String msg = username + "已存在" ;
String msg = username + MessageConstant.ALREADY_EXISIS ;
return Result.error(msg) ;
}else{
return Result.error("未知错误");
}
}
新增员工时,创建人和修改人id设置为了固定值
ThreadLocal
解决:
从jwt中拿到用户信息:
后续请求中,前端会携带JWT令牌,通过JWT令牌(在拦截器哪里)可以解析出当前员工id
问题:解析出ID之后,如何将ID传给Service中的save方法呢?
使用ThreadLocal线程解决:
ThreadLocal并不是一个Thread,而是Thread的局部变量
ThreadLocal为每一个线程单独提供了一份存储空间,具有线程隔离效果,只有在线程内才能获取到对应的值,线程外不能访问。
客户端每发起一次请求,都会对应一个单独的线程。
可以通过下面代码获取当前线程ID;
System.out.println("当前线程ID : " + Thread.currentThread().getId());
ThreadLocal常用方法:
pubLic void set(T value):设置当前线程局部变量的值
public T get(): 返回当前线程对应的线程局部变量的值
public void remove():移除当前线程的线程局部变量
在sky-common中的context中包装了相应的方法:
package com.sky.context;
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
public static void removeCurrentId() {
threadLocal.remove();
}
}
然后直接在JwtTokenAdminIntercepter中设置ID,然后再ServiceImpl中获取即可
2.员工分页查询
需求分析与设计
业务规则:
根据页码展示员工信息
每页展示10条数据
分页查询可以根据需要,输入员工姓名进行查询
查询一般使用Get方法:
请求参数:
name:员工姓名
page:页码
pageSize:每页记录数
返回数据:
代码开发
后面所有的分页查询,都会一通封装成PageResult对象:
package com.sky.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 封装分页查询结果
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {
private long total; //总记录数
private List records; //当前页数据集合
}
员工分页查询后端返回的对象类型为: Result<PageResult>
Controller中:.
/**
* 分页查询
* @param employeePageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("员工分页查询")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
log.info("分页查询:{}",employeePageQueryDTO);
PageResult pageResult = employeeService.page(employeePageQueryDTO);
return Result.success(pageResult);
}
EmployeeService中添加方法:
PageResult page(EmployeePageQueryDTO employeePageQueryDTO);
在impl实现类中实现该方法
@Override
public PageResult page(EmployeePageQueryDTO employeePageQueryDTO) {
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
Page<Employee> page=employeeMapper.pageQuery(employeePageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
注意:
首先调用Mybatisz中的插件PageHelper实现分页的功能
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
实现的原理:
还是用到ThreadLocal原理,每次请求都会对应一个单独的线程,那么就可以将page和pagesize设置成当前线程的局部变量 , 后面就可以直接调用到这两个参数 ;
在EmployeeMapper文件中采用xml来编写对应的sql语句
Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
在对应的EmployeeMapper.xml中
<select id="pageQuery" resultType="com.sky.entity.Employee">
select <include refid="Base_Column_List"/>
from employee
<where>
<if test="name != null and name !=''">
name like concat('%',#{name},'%')
</if>
</where>
order by create_time desc
</select>
代码完善:
解决方式:
方式一:在属性上加上注解,对日期进行格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime
在WebMvcConfiguration中扩展Spring Mvc的消息转换器,统一对日期进行格式化处理
3 启用禁用员工账号
需求分析与设计
业务规则:
可以对状态为"启用“的员工账号进行"禁用”操作
可以对状态为"禁用“的员工账号进行"启用”操作
状态为禁用的员工账号不能登录系统
接口设计:
代码开发
本质上,是根据员工id,去修改Status
Controller
注意:
status是路径参数,需要加@PathVariable注解
Service
impl
Mapper
xml
这里使用动态sql编写sql语句
编辑员工:
需求分析
编辑员工功能设计到两个接口
根据id查询员工信息 get请求方式
编辑员工信息 put请求方式
代码开发
Controller:
Service
Impl
Mapper
xml
修改:
查询:
5 导入分类模块功能代码
需求分析:
后台系统中可以管理分类信息,分类包括两种类型,分别是菜品类和套餐类
分析菜品分类相关功能
新增菜品分类:当我们在后台系统中添加菜品的时候,需要选择一个菜品分类,在移动端也会按照菜品分类来展示对应的菜品
菜品分类分页查询:系统中的分类很多的时候,如果在一个页面中全部展示出来的话,会显得特别乱,不便于查看,所以一般都分页的方式来展示对应的列表数据
根据id删除菜品分类:在分类管理列表页面,可以对某个分类进行删除操作,当该分类关联了菜品或者套餐的时候,不允许删除。
修改菜品分类:在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息进行修改,最后确定按钮完成修改操作。
启用禁用菜品分类:在分类管理列表页面,可以对某个分类进行启用或者禁用操作
分类类型查询:当点击分类类型下拉框的时候,从数据库中查询所有的菜品分类数据进行展示
代码实现:
DishMapper
package com.sky.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface DishMapper {
/**
* 根据分类id查询菜品数量
* @param categoryId
* @return
*/
@Select("select count(id) from dish where category_id = #{categoryId}")
Integer countByCategoryId(Long categoryId);
}
SetmealMapper
package com.sky.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface SetmealMapper {
/**
* 根据分类id查询套餐的数量
* @param id
* @return
*/
@Select("select count(id) from setmeal where category_id = #{categoryId}")
Integer countByCategoryId(Long id);
}
CategoryMapper
package com.sky.mapper;
import com.github.pagehelper.Page;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CategoryMapper {
/**
* 插入数据
* @param category
*/
@Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)" +
" VALUES" +
" (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
void insert(Category category);
/**
* 分页查询
* @param categoryPageQueryDTO
* @return
*/
Page<Category> pageQuery(CategoryPageQueryDTO categoryPageQueryDTO);
/**
* 根据id删除分类
* @param id
*/
@Delete("delete from category where id = #{id}")
void deleteById(Long id);
/**
* 根据id修改分类
* @param category
*/
void update(Category category);
/**
* 根据类型查询分类
* @param type
* @return
*/
List<Category> list(Integer type);
}
CategoryMapper.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.CategoryMapper">
<select id="pageQuery" resultType="com.sky.entity.Category">
select id, type, name, sort, status,
create_time, update_time,
create_user, update_user from category
<where>
<if test="name != null and name != ''">
name like concat('%',#{name},'%')
</if>
<if test="type != null">
and type = #{type}
</if>
</where>
order by sort asc , create_time desc
</select>
<update id="update" parameterType="com.sky.entity.Category">
update category
<set>
<if test="type != null">
type = #{type},
</if>
<if test="name != null">
name = #{name},
</if>
<if test="sort != null">
sort = #{sort},
</if>
<if test="status != null">
status = #{status},
</if>
<if test="updateTime != null">
update_time = #{updateTime},
</if>
<if test="updateUser != null">
update_user = #{updateUser}
</if>
</set>
where id = #{id}
</update>
<select id="list" resultType="com.sky.entity.Category">
select * from category
where status = 1
<if test="type != null">
and type = #{type}
</if>
order by sort asc,create_time desc
</select>
</mapper>
Service
CategoryService
package com.sky.service;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.result.PageResult;
import java.util.List;
public interface CategoryService {
/**
* 新增分类
* @param categoryDTO
*/
void save(CategoryDTO categoryDTO);
/**
* 分页查询
* @param categoryPageQueryDTO
* @return
*/
PageResult pageQuery(CategoryPageQueryDTO categoryPageQueryDTO);
/**
* 根据id删除分类
* @param id
*/
void deleteById(Long id);
/**
* 修改分类
* @param categoryDTO
*/
void update(CategoryDTO categoryDTO);
/**
* 启用、禁用分类
* @param status
* @param id
*/
void startOrStop(Integer status, Long id);
/**
* 根据类型查询分类
* @param type
* @return
*/
List<Category> list(Integer type);
}
Impl
package com.sky.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.mapper.CategoryMapper;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetmealMapper;
import com.sky.result.PageResult;
import com.sky.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import java.time.LocalDateTime;
import java.util.List;
/**
* 分类业务层
*/
@Service
@Slf4j
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private SetmealMapper setmealMapper;
/**
* 新增分类
* @param categoryDTO
*/
public void save(CategoryDTO categoryDTO) {
Category category = new Category();
//属性拷贝
BeanUtils.copyProperties(categoryDTO, category);
//分类状态默认为禁用状态0
category.setStatus(StatusConstant.DISABLE);
//设置创建时间、修改时间、创建人、修改人
category.setCreateTime(LocalDateTime.now());
category.setUpdateTime(LocalDateTime.now());
category.setCreateUser(BaseContext.getCurrentId());
category.setUpdateUser(BaseContext.getCurrentId());
categoryMapper.insert(category);
}
/**
* 分页查询
* @param categoryPageQueryDTO
* @return
*/
public PageResult pageQuery(CategoryPageQueryDTO categoryPageQueryDTO) {
PageHelper.startPage( categoryPageQueryDTO.getPage(),
categoryPageQueryDTO.getPageSize());
//下一条sql进行分页自动加入limit关键字分页
Page<Category> page = categoryMapper.pageQuery(categoryPageQueryDTO);
return new PageResult(page.getTotal(), page.getResult());
}
/**
* 根据id删除分类
* @param id
*/
public void deleteById(Long id) {
//查询当前分类是否关联了菜品,如果关联了就抛出业务异常
Integer count = dishMapper.countByCategoryId(id);
if(count > 0){
//当前分类下有菜品,不能删除
throw new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_DISH);
}
//查询当前分类是否关联了套餐,如果关联了就抛出业务异常
count = setmealMapper.countByCategoryId(id);
if(count > 0){
//当前分类下有菜品,不能删除
throw new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);
}
//删除分类数据
categoryMapper.deleteById(id);
}
/**
* 修改分类
* @param categoryDTO
*/
public void update(CategoryDTO categoryDTO) {
Category category = new Category();
BeanUtils.copyProperties(categoryDTO,category);
//设置修改时间、修改人
category.setUpdateTime(LocalDateTime.now());
category.setUpdateUser(BaseContext.getCurrentId());
categoryMapper.update(category);
}
/**
* 启用、禁用分类
* @param status
* @param id
*/
public void startOrStop(Integer status, Long id) {
Category category = Category.builder()
.id(id)
.status(status)
.updateTime(LocalDateTime.now())
.updateUser(BaseContext.getCurrentId())
.build();
categoryMapper.update(category);
}
/**
* 根据类型查询分类
* @param type
* @return
*/
public List<Category> list(Integer type) {
return categoryMapper.list(type);
}
}
Controller
package com.sky.controller.admin;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.CategoryService;
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.*;
import java.util.List;
/**
* 分类管理
*/
@RestController
@RequestMapping("/admin/category")
@Api(tags = "分类相关接口")
@Slf4j
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
* 新增分类
* @param categoryDTO
* @return
*/
@PostMapping
@ApiOperation("新增分类")
public Result<String> save(@RequestBody CategoryDTO categoryDTO){
log.info("新增分类:{}", categoryDTO);
categoryService.save(categoryDTO);
return Result.success();
}
/**
* 分类分页查询
* @param categoryPageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("分类分页查询")
public Result<PageResult> page(CategoryPageQueryDTO categoryPageQueryDTO){
log.info("分页查询:{}", categoryPageQueryDTO);
PageResult pageResult = categoryService.pageQuery(categoryPageQueryDTO);
return Result.success(pageResult);
}
/**
* 删除分类
* @param id
* @return
*/
@DeleteMapping
@ApiOperation("删除分类")
public Result<String> deleteById(Long id){
log.info("删除分类:{}", id);
categoryService.deleteById(id);
return Result.success();
}
/**
* 修改分类
* @param categoryDTO
* @return
*/
@PutMapping
@ApiOperation("修改分类")
public Result<String> update(@RequestBody CategoryDTO categoryDTO){
categoryService.update(categoryDTO);
return Result.success();
}
/**
* 启用、禁用分类
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("启用禁用分类")
public Result<String> startOrStop(@PathVariable("status") Integer status, Long id){
categoryService.startOrStop(status,id);
return Result.success();
}
/**
* 根据类型查询分类
* @param type
* @return
*/
@GetMapping("/list")
@ApiOperation("根据类型查询分类")
public Result<List<Category>> list(Integer type){
List<Category> list = categoryService.list(type);
return Result.success(list);
}
}