苍穹外卖 项目记录 day02

文章目录

  • 表数据加密
  • 文档与日志接入
    • swagger常用注解
  • 管理端 员工管理模块开发
    • 员工信息新增实现
  • 新增员工代码完善
  • ThreadLocal
  • 员工分页查询
  • 账户启用 禁用切换
  • 编辑员工
    • 1 回显员工信息功能
    • 2 修改员工信息功能
  • 导入分类模块功能代码


项目地址 https://gitee.com/lyh1999/minjiang-takeout

表数据加密

员工表中密码明文存储 需要加密 将密码加密后存储到数据库 使用MD5加密 (MD5信息摘要算法 )加密后生成一个32位的字符串

MD5 可以被破解 推荐使用SHA-2算法

完善登录

1 修改数据库中明文密码 改为MD5加密后的密文

2 修改JAVA代码 前端提交密码 进行MD5加密后与数据库中的密码密文对比

spring 提供的MD5加密

password = DigestUtils.md5DigestAsHex(password.getBytes())
    if(!password.equals(employee.getPassword(){
        //密码错误
        throw new PasswordErrorException(MessageConstant.PASSWORD_ERROE)
    }))

TODO 注释

spring进行的md5加密属于小写字母的md5加密

文档与日志接入

Swagger

生成接口文档和进行接口测试

swagger官网 https://swagger.io/

knife4j是 java MVC 框架集成swagger生成api文档的增强解决方案

在pom.xml导入

<dependency>
	<groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

knife4j使用方式

1 导入knife4j的maven坐标

2 在配置类中加入knife4j相关配置

3 设置静态资源映射

在配置类中(sky-server>com.sky/config/WebMvcConfiguration)

@Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("闽江外卖项目接口文档")
                .version("2.0")
                .description("闽江外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller")) // 指向扫描的包
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
    /**
     * 设置静态资源映射
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

启动项目后 访问 http://localhost:8080/doc.html

即可得到swagger在线文档

swagger常用注解

@Api 用在类上 对类做说明 如controller

@ApiModel 用在类上 entity DTO VO

@ApiModelProperty 用在属性上

@ApiOperation 用在方法上 如Controller的方法

@Api(tags = "员工相关接口")
public class EmployeeController {}

@PostMapping("/login")
    @ApiOperation(value = "员工登录")
@ApiOperation(value = "员工退出")
    @PostMapping("/logout")

@ApiModel(description = "员工登录时传递的数据模型")
public class EmployeeLoginDTO implements Serializable {

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("密码")
    private String password;

}

@ApiModel(description = "员工登录返回的数据格式")
public class EmployeeLoginVO implements Serializable {

    @ApiModelProperty("主键值")
    private Long id;

    @ApiModelProperty("用户名")
    private String userName;

    @ApiModelProperty("姓名")
    private String name;

    @ApiModelProperty("jwt令牌")
    private String token;

}

添加注解 能让knife4j生成的swagger文档更加清晰易懂


管理端 员工管理模块开发

新增员工 分页查询 启用禁用员工账户 员工信息编辑

新增员工 需求分析和设计 代码开发 功能测试 代码完善

员工信息新增实现

填写员工信息时后端校验

账号唯一性

手机号合法11位数据

身份证号合法18位数据

本项目约定 管理端请求 统一使用/admin做前缀

用户端 统一采用/user做前缀

前端提交数据和实体类对应属性差别较大时 封装DTO对象

@Data
public class EmployeeDTO implements Serializable {

    private Long id;

    private String username;

    private String name;

    private String phone;

    private String sex;

    private String idNumber;

}

save实现

  /**
     *
      * @param employeeDTO
     * @return
     */
    @PostMapping
    @ApiOperation(value = "新增员工")
    public Result save(@RequestBody  EmployeeDTO employeeDTO){
        log.info("新增员工:{}",employeeDTO);
        employeeService.save(employeeDTO);
        return Result.success();
    }


public interface EmployeeService {

    /**
     * 员工登录
     * @param employeeLoginDTO
     * @return
     */
    Employee login(EmployeeLoginDTO employeeLoginDTO);

    /**
     *
     * @param 新增员工
     */
    void save(EmployeeDTO employeeDTO);
}



@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;
    public void save(EmployeeDTO employeeDTO) {
        //调用持久层mapper  插入数据
        Employee employee = new Employee();

        //employee.setName(employeeDTO.getName());
        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);

//        设置账户状态 1 表示正常 0表示锁定
        employee.setStatus(StatusConstant.ENABLE);
//        设置账户密码 默认123456
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
//        设置当前记录创建时间和修改时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
//        设置当前记录人创建id和修改id
//        暂时写死
//        TODO 后期需要改为当前用户id值
        employee.setCreateUser(10L);
        employee.setUpdateUser(10L);

        employeeMapper.insert(employee);
    }
}


    /**
     *  插入员工数据
     * @param employee
     */
    @Insert("insert into employee(name,username,password,phone,sex,id_number,status,create_time,update_time,create_user,update_user)" +
        "values " +
        "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})"
    )
    @AutoFill(value = OperationType.INSERT)
    void insert(Employee employee);

EmployeeController =》 EmployeeService =》 EmployeeServiceImpl =》 employeeMapper

新增员工代码完善

1 录入用户名已存在 抛出异常后未处理

2 新增员工 创建id 和修改id设置的固定值

通过全局异常处理器来处理。

进入到sky-server模块,com.sky.hander包下,GlobalExceptionHandler.java添加方法

    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        String message = ex.getMessage();
        if(message.contains("Duplicate entry")){
//            返回的错误信息包含Duplicate entry 重复的条目
            String[] split = message.split(" ");
            String username = split[2] + MessageConstant.AlREADY_EXISTS;

            String msg = username + "已存在";
            return Result.error(msg);
        }else{
//            其他异常
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }

调试时 报401可能是token已过期 重启项目 token会更换

jwt流程

前端 用户认证 提交 用户名 和密码 后端认证通过 生成jwt token 前端 保存jwt token在本地

前端 每次请求 后端接口 在请求头中携带jwt token 后端 拦截请求验证 jwt token 通过 执行业务逻辑 返回数据 前端展示数据

后端不通过 返回错误信息 前端展示错误信息 并返回登录页面

新增员工时,创建人id和修改人id设置为固定值

package com.sky.controller.admin;
/**
 * 员工管理
 */
@RestController
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")
    @ApiOperation(value = "员工登录")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        //.........

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        //............

        return Result.success(employeeLoginVO);
    }

}

后续请求中,前端会携带JWT令牌,通过JWT令牌可以解析出当前登录员工id:

JwtTokenAdminInterceptor.java

package com.sky.interceptor;

/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenAdminInterceptor 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 {
       
		//..............
        
        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前员工id:", empId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

前端携带jwt令牌 通过jwt令牌 解析当前登录用户员工id

解析出登录员工id后,如何传递给Service的save方法?

通过ThreadLocal进行传递。

ThreadLocal

ThreadLocal 是 Java 提供的一种机制,它为每个线程创建独立的变量副本,使得同一个类的实例在不同线程中拥有不同的状态。这种方式非常适合用来存储与当前线程相关的数据,例如用户会话信息、事务上下文等。

在基于JWT的身份验证场景中,使用 ThreadLocal 的方式可以确保在请求处理的过程中,从解析出的JWT中提取到的员工ID(或其他用户相关信息)可以在整个请求链中被访问到,而无需通过参数传递。

ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

ThreadLocal 常用方法:

  • public void set(T value) 设置当前线程的线程局部变量的值
  • public T get() 返回当前线程所对应的线程局部变量的值
  • public void remove() 移除当前线程的线程局部变量

初始工程中已经封装了 ThreadLocal 操作的工具类:

在sky-common模块

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();
    }

}

在拦截器中解析出当前登录员工id,并放入线程局部变量中:

在sky-server模块中,拦截器:

@Component
@Slf4j
public class JwtTokenAdminInterceptor 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 {
        log.info("当前线程id值为"+ Thread.currentThread().getId());

        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前员工id:", empId);
            // 存入员工id到ThreadLocal
            BaseContext.setCurrentId(empId);

            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

在Service中获取线程局部变量中的值:

    public void save(@RequestBody EmployeeDTO employeeDTO) {
        //调用持久层mapper  插入数据
        Employee employee = new Employee();

        //employee.setName(employeeDTO.getName());
        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);

//        设置账户状态 1 表示正常 0表示锁定
        employee.setStatus(StatusConstant.ENABLE);
//        设置账户密码 默认123456
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
//        设置当前记录创建时间和修改时间
//        employee.setCreateTime(LocalDateTime.now());
//        employee.setUpdateTime(LocalDateTime.now());
//        设置当前记录人创建id和修改id
//        暂时写死
//        TODO 后期需要改为当前用户id值

        System.out.println("当前线程id值为"+ Thread.currentThread().getId());
        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());

        employeeMapper.insert(employee);
    }

测试:使用admin(id=1)用户登录后添加一条记录 新增用户

员工分页查询

  • 根据页码展示员工信息
  • 每页展示10条数据
  • 分页查询时可以根据需要,输入员工姓名进行查询

封装DTO

@Data
public class EmployeePageQueryDTO implements Serializable {

    //员工姓名
    private String name;

    //页码
    private int page;

    //每页显示记录数
    private int pageSize;

}

分页查询结果 不采用result对象 封装为 pageResult对象

/**
 * 封装分页查询结果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}

员工信息分页查询后端返回的对象类型为: Result

@Data
public class Result<T> implements Serializable {

    private Integer code; //编码:1成功,0和其它数字为失败
    private String msg; //错误信息
    private T data; //数据

    public static <T> Result<T> success() {
        Result<T> result = new Result<T>();
        result.code = 1;
        return result;
    }

    public static <T> Result<T> success(T object) {
        Result<T> result = new Result<T>();
        result.data = object;
        result.code = 1;
        return result;
    }

    public static <T> Result<T> error(String msg) {
        Result result = new Result();
        result.msg = msg;
        result.code = 0;
        return result;
    }

}

Controller层

    /**
     * 实现分页查询
     * @param
     */

    @GetMapping("/page")
    @ApiOperation(value = "员工分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
        log.info("员工分页查询:{}",employeePageQueryDTO);
        PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
        return Result.success(pageResult);
    }

Service层接口

在EmployeeService接口中声明pageQuery方法:

 /**
     * 分页查询方法
     * @param employeePageQueryDTO
     * @return
     */
    PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);

Service层实现类

    @Override
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//        底层基于数据库分页
//        select * from employee limit 0,10
//        这里采用mybatis的插件 pageHelper
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
//        返回page对象
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
//        获取总记录数
        long total = page.getTotal();
//        获取当前页的数据
        List<Employee> records = page.getResult();
        return new PageResult(total, records);
    }

在pom.xml文中添加依赖(初始工程已添加)

<dependency>
   <groupId>com.github.pagehelper</groupId>
   <artifactId>pagehelper-spring-boot-starter</artifactId>
   <version>${pagehelper}</version>
</dependency>

Mapper层

    /**
     * 分页查询方法
     * 动态sql 注解不适合 采用映射文件书写sql  resources/mapper/employeeMapper.xml
     * @param employeePageQueryDTO
     * @return
     */
    Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from employee
        <where>
            <if test="name!=null and name!=''">
        and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>

测试查询分页列表数据

代码完善 时间格式化 两种方式

1 在属性上加上注解,对日期进行格式化

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;

但这种方式,需要在每个时间属性上都要加上该注解,使用较麻烦,不能全局处理。

2 在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理

	/**
     * 扩展Spring MVC框架的消息转化器
     * @param converters
     */
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
        //创建一个消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转化器加入容器中
        converters.add(0,converter);
    }

在 JacksonObjectMapper 定义固定的时间对象

public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

账户启用 禁用切换

账户禁用时不能登录

传递status值 做路径参数 和员工id 做查询参数

 @PostMapping("/status/{status}")
    @ApiOperation(value = "启用禁用员工账户")
    public Result startOrStop(@PathVariable("status") Integer status, Long id){
        log.info("启用禁用员工账户:{},{}",status,id);
        employeeService.startOrStop(status,id);
        return Result.success();
    }


/**
     * 启用或禁用员工账户
     * @param status
     * @param id
     */
    void startOrStop(Integer status, Long id);


@Override
    public void startOrStop(Integer status, Long id) {
//        update employee set status = #{status} where id = #{id}
//        更新可能适配多种状态 直接传两个写死参数不适合 传递一个对象  第一种写法
//        Employee employee = new Employee();
//        employee.setStatus(status);
//        employee.setId(id);

//        第二种写法  因为 employee对象添加了@builder注解  建造者模式实现

        Employee employee = Employee.builder().status(status).id(id).build();

        employeeMapper.update(employee);
    }
<update id="update" parameterType="Employee">
        update employee
        <set>
            <if test="name!=null">
                name=#{name},
            </if>
            <if test="username!=null">
                username=#{username},
            </if>
            <if test="password!=null">
                password=#{password},
            </if>
            <if test="phone!=null">
                phone=#{phone},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="idNumber!=null">
                id_Number=#{idNumber},
            </if>
            <if test="updateTime!=null">
                update_Time=#{updateTime},
            </if>
            <if test="updateUser!=null">
                update_User=#{updateUser},
            </if>
            <if test="status!=null">
                status=#{status}
            </if>
        </set>
        where id=#{id}
    </update>

xml 的更新属性 只判空

编辑员工

根据id 查询员工信息 并回显

编辑员工信息

1 回显员工信息功能

Controller层

在 EmployeeController 中创建 getById 方法:

/**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询员工信息")
    public Result<Employee> getById(@PathVariable Long id){
        Employee employee = employeeService.getById(id);
        return Result.success(employee);
    }

Service层接口

在 EmployeeService 接口中声明 getById 方法:

/**
     * 根据id查询员工
     * @param id
     * @return
     */
    Employee getById(Long id);

Service层实现类

在 EmployeeServiceImpl 中实现 getById 方法:

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

Mapper层

在 EmployeeMapper 接口中声明 getById 方法:

/**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @Select("select * from employee where id = #{id}")
    Employee getById(Long id);

2 修改员工信息功能

Controller层

在 EmployeeController 中创建 update 方法:

/**
     * 编辑员工信息
     * @param employeeDTO
     * @return
     */
    @PutMapping
    @ApiOperation("编辑员工信息")
    public Result update(@RequestBody EmployeeDTO employeeDTO){
        log.info("编辑员工信息:{}", employeeDTO);
        employeeService.update(employeeDTO);
        return Result.success();
    }

Service层接口

在 EmployeeService 接口中声明 update 方法:

/**
* 编辑员工信息
* @param employeeDTO
*/
    void update(EmployeeDTO employeeDTO);

Service层实现类

在 EmployeeServiceImpl 中实现 update 方法:

 /**
     * 编辑员工信息
     * @param employeeDTO
     */
    public void update(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
//        使用BeanUtils的copyProperties方法将employeeDTO的属性复制到employee对象中
        BeanUtils.copyProperties(employeeDTO, employee);
//        设置更新时间
        employee.setUpdateTime(LocalDateTime.now());
//        设置更新人
        employee.setUpdateUser(BaseContext.getCurrentId());
//        调用mapper持久层的update方法
        employeeMapper.update(employee);
    }

导入分类模块功能代码

实现菜品分类和套餐分类

  • 新增分类
  • 分类分页查询
  • 根据id删除分类
  • 修改分类
  • 启用禁用分类
  • 根据类型查询分类

controller层

/**
 * 分类管理
 */
@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);
    }
}

service层

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实现类

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        //密码比对
        // TODO 后期需要进行md5加密,然后再进行比对
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

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

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

    /**
     * 新增员工
     *
     * @param employeeDTO
     */
    public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();

        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);
        //设置账号的状态,默认正常状态 1表示正常 0表示锁定
        employee.setStatus(StatusConstant.ENABLE);

        //设置密码,默认密码123456
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //设置当前记录的创建时间和修改时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        //设置当前记录创建人id和修改人id
        employee.setCreateUser(BaseContext.getCurrentId());//目前写个假数据,后期修改
        employee.setUpdateUser(BaseContext.getCurrentId());

        employeeMapper.insert(employee);
    }

    /**
     * 分页查询
     *
     * @param employeePageQueryDTO
     * @return
     */
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        // select * from employee limit 0,10
        //开始分页查询
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());

        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);

        long total = page.getTotal();
        List<Employee> records = page.getResult();

        return new PageResult(total, records);
    }

    /**
     * 启用禁用员工账号
     *
     * @param status
     * @param id
     */
    public void startOrStop(Integer status, Long id) {
        Employee employee = Employee.builder()
                .status(status)
                .id(id)
                .build();

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

    /**
     * 编辑员工信息
     *
     * @param employeeDTO
     */
    public void update(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO, employee);

        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(BaseContext.getCurrentId());

        employeeMapper.update(employee);
    }

}

Mapper层

@Mapper
public interface DishMapper {

    /**
     * 根据分类id查询菜品数量
     * @param categoryId
     * @return
     */
    @Select("select count(id) from dish where category_id = #{categoryId}")
    Integer countByCategoryId(Long categoryId);

}
@Mapper
public interface SetmealMapper {

    /**
     * 根据分类id查询套餐的数量
     * @param id
     * @return
     */
    @Select("select count(id) from setmeal where category_id = #{categoryId}")
    Integer countByCategoryId(Long id);

}
@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 * from category
        <where>
            <if test="name != null and name != ''">
                and 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="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="Category">
        select * from category
        where status = 1
        <if test="type != null">
            and type = #{type}
        </if>
        order by sort asc,create_time desc
    </select>
</mapper>

一个功能模块实现 往往是 controller层 到 service层 到 serviceImpl 层 到mapper层

底层还是sql实现 按公式做题

有创新 的思维写代码自然更好

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/949439.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java100道面试题

1.JVM内存结构 1. 方法区&#xff08;Method Area&#xff09; 方法区是JVM内存结构的一部分&#xff0c;用于存放类的相关信息&#xff0c;包括&#xff1a; 类的结构&#xff08;字段、方法、常量池等&#xff09;。字段和方法的描述&#xff0c;如名称、类型、访问修饰符…

【数电尾灯设计】2022-8-16

缘由数电尾灯设计问题&#xff0c;求解答--CSDN问答 从题目可以列出 000 100 010 111-----------4进制 000 100 010 110 001 101 011 111-----------8进制 由列出可知用16进制芯片的3个引脚可以获得8进制推导出4进制从而可用逻辑处理为4进制实现尾灯功能。之上第一步实现了尾灯…

安卓14无法安装应用解决历程

客户手机基本情况&#xff1a; 安卓14&#xff0c;对应的 targetSdkVersion 34 前天遇到了安卓14适配问题&#xff0c;客户发来的截图是这样的 描述&#xff1a;无法安装我们公司的B应用。 型号&#xff1a;三星google美版 解决步骤&#xff1a; 1、寻找其他安卓14手机测试…

【WRF数据准备】ECMWF 49r1: Soil Variables的变化及WRF模型修正

目录 ECMWF 49r1: Change in soil variablesECMWF 49r1更新的背景土壤变量的主要变化对WRF使用者的影响Github中描述ERA5 Vtable 下载另:原始 IFS 数据-ECMWF 服务器参考2024年12月12日,ECMWF 发布了 49r1 版本的业务 IFS。ECMWF在其49r1循环版本中对土壤变量进行了重要更新。…

一机多实例:如何在一台机器上高效运行多个 MySQL 服务

前言 在实际开发和测试环境中&#xff0c;我们经常需要运行多个 MySQL 实例来模拟不同的数据库环境。例如&#xff0c;在一台服务器上运行多个数据库服务以节约硬件资源&#xff0c;或者同时运行不同版本的 MySQL 进行功能兼容性测试。MySQL 本身支持通过配置多实例运行&#…

源代码编译安装X11及相关库、vim,配置vim(1)

一、目录结构 如下。 所有X11及相关库装到mybuild&#xff0c;源代码下载到src下&#xff0c;解压&#xff0c;进入&#xff0c;编译安装。编译时指定--prefix到相同的目录&#xff0c;即上图中mybuild。 ./configure --prefixpwd/../../mybuild [CFLAGS"-I/path/to/X11…

5. CSS引入方式

5.1 CSS的三种样式 按照 CSS 样式书写的位置(或者引入的方式)&#xff0c;CSS样式表可以分为三大类&#xff1a; 1.行内样式表&#xff08;行内式&#xff09; 2.内部样式表&#xff08;嵌入式&#xff09; 3. 外部样式表&#xff08;链接式&#xff09; 5.2 内部样式表 …

【C++】构造函数与析构函数

写在前面 构造函数与析构函数都是属于类的默认成员函数&#xff01; 默认成员函数是程序猿不显示声明定义&#xff0c;编译器会中生成。 构造函数和析构函数的知识需要建立在有初步类与对象的基础之上的&#xff0c;关于类与对象不才在前面笔记中有详细的介绍&#xff1a;点我…

WPF区域导航+导航参数使用+路由守卫+导航日志

背景&#xff1a;使用ContentControl控件实现区域导航是有Mvvm框架的WPF都能使用的&#xff0c;不限于Prism 主要是将ContenControl控件的Content内容在ViewModel中切换成不同的用户控件 下面是MainViewModel&#xff1a; private object body;public object Body {get { retu…

Unity中 Xlua使用整理(一)

1.安装: 从GitHub上下载Xlua源码 Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. (github.com) 下载Xlua压缩包&#xff0c;并解压将Aseet文件夹中的Xlua和Plugins文件夹复制到Unit…

Matlab仿真径向受压圆盘光弹图像

Matlab仿真径向受压圆盘光弹图像-十步相移法 主要参数 % 定义圆盘参数 R 15; % 圆盘半径&#xff0c;单位&#xff1a;mm h 5; % 圆盘厚度&#xff0c;单位&#xff1a;mm P 300; % 径向受压载荷大小&#xff0c;单位&#xff…

基于Django的学校智能图书馆借书归还订阅管理系统

完整源码项目包获取→点击文章末尾名片&#xff01;

【设计模式-2】23 种设计模式的分类和功能

在软件工程领域&#xff0c;设计模式是解决常见设计问题的经典方案。1994 年&#xff0c;Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides&#xff08;四人帮&#xff0c;GoF&#xff09;在《设计模式&#xff1a;可复用面向对象软件的基础》一书中系统性地总结了…

阿里云代理商热销产品推荐

在数字化浪潮的推动下&#xff0c;企业对于云计算的依赖日益加深。阿里云&#xff0c;作为中国领先的云计算服务提供商&#xff0c;为企业提供了丰富多样的云产品和服务。本文将聚焦于阿里云代理商热销产品推荐&#xff0c;探讨其如何帮助企业高效利用云资源&#xff0c;加速数…

[redux] 异步逻辑的两种写法

createAsyncThunk | Redux Toolkit 第一种, extraReducers 普通的reducers只能写同步代码 异步必须得用中间件的形式,就是异步代码调用完有结果了, 再调用同步的reducer, 大概这么理解, 第一种怎么用呢? 先用一个异步函数 const fetchUserById createAsyncThunk(users/fet…

在Java中使用有符号类型模拟无符号整数的技巧

有符号整数和无符号整数 有符号整数&#xff1a;可以表示正数、负数和零。例如&#xff0c;Java中的 byte 类型是有符号的&#xff0c;其范围是 -128 到 127.无符号整数&#xff1a;只能表示非负数&#xff08;即零和正数&#xff09;。例如&#xff0c;无符号 byte 应该表示的…

51单片机——8*8LED点阵

LED 点阵的行则为发光二极管的阳极&#xff0c;LED 点阵的列则为发光二极管的阴极 根据 LED 发光二极管导通原理&#xff0c;当阳极为高电平&#xff0c;阴极为低电平则点亮&#xff0c;否则熄灭。 因此通过单片机P0口可控制点阵列&#xff0c;74HC595可控制点阵行 11 脚 SR…

Flutter:邀请海报,Widget转图片,保存相册

记录下&#xff0c;把页面红色区域内的内容&#xff0c;转成图片后保存到相册的功能 依赖 # 生成二维码 qr_flutter: ^4.1.0 # 保存图片 image_gallery_saver_plus: ^3.0.5view import package:demo/common/index.dart; import package:ducafe_ui_core/ducafe_ui_core.dart; i…

C++Primer const限定符

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

C语言 游动的小球

代码如下&#xff1a; 在这里插入代码片#include<stdio.h> #include<stdlib.h> #include<windows.h>int main() {int i,j;int x 5;int y 10;int height 20;int velocity_x 1;int velocity_y 1;int left 0;int right 20;int top 0;int bottom 10;while(1){…