Java项目 苍穹外卖 黑马程序员


来源: 黑马程序员Java项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级Java项目实战

day1

一、项目效果展示

二、项目开发整体介绍

在这里插入图片描述

三、项目介绍

3.1 定位

在这里插入图片描述

3.2 功能架构

在这里插入图片描述

3.3 产品原型

html页面,包括管理端和用户端两种

3.4 技术选型

在这里插入图片描述

四、开发环境搭建

在这里插入图片描述

4.1 前端环境

前端工程是基于nginx运行的,已经是打包好的程序,直接双击执行exe文件,接着在浏览器访问localhost即可。
在这里插入图片描述
在这里插入图片描述

4.2 后端环境

后端工程是基于maven进行构建的,并且进行分模块开发
导入的时候,需要配置maven的settings文件中阿里云镜像
在这里插入图片描述
在这里插入图片描述

  1. 使用git进行版本控制:本地创建仓库,远程创建仓库,将本地仓库推送到远程
  2. 数据库环境搭建:导入数据库文件即可
  3. 前后端联调:通过断点调试,一步步理清登录功能。
1.获取前端对象(employeeLoginDTO)
2.使用DTO中username属性通过EmployeeMapper查询数据库,得到emplyee对象
3.判断密码是否一致,且用户状态是否为‘启用’,都是则登录成功
4.生成JWK令牌,通过配置文件获取密钥和过期时间,令牌名称为token
5.通过Builder注解的方法生成employeeLoginVO对象,包括主键值、用户名、姓名和jwt令牌
6.后端统一返回结果为Result<T>,封装了错误信息msg和返回的VO对象
7.另外,文件中定义了各种异常,继承自BaseException,它的父类为RuntimeException。通过全局处理器handler来捕获全部的异常,进行处理

在这里插入图片描述

  1. 前端发送的请求,如何请求到后端服务的?
    是通过nginx方向代理,将前端发送的动态请求有nginx转发到后端服务器
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  2. 完善登录功能
    在数据库中加密存储密码—> 在server登录功能处,将明文密码先加密在进行对比

五、导入接口文档

接口设计能力
前后端分离开发流程。使用apifox导入接口json文件。划分了两个文件,包括管理端和用户端。
在这里插入图片描述

六、Swagger

6.1 介绍

在这里插入图片描述

6.2 使用方式

在这里插入图片描述
在这里插入图片描述
最后去网页访问http://localhost:8080/doc.html,会出现下面的界面
在这里插入图片描述
在这里插入图片描述

6.3 常用注解

在这里插入图片描述

day2

一、新增员工

  1. 根据接口文档,写service。EmployeeController.java
	/**
     * 新增员工
     * @param employeeDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增员工")
    public Result save(@RequestBody EmployeeDTO employeeDTO){
        log.info("新增员工:{}", employeeDTO);
        employeeService.save(employeeDTO);
        return  Result.success();
    }
  1. 完善EmployeeService接口和EmployeeServiceImpl接口实现类的save方法。EmployeeServiceImpl.java
    /**
     * 新增员工
     * @param employeeDTO
     */
    public void save(EmployeeDTO employeeDTO) {
        // 对象的转换,将DTO转为实体对象
        Employee employee = new Employee();
        // 对象属性拷贝,将dto对象拷贝给entity。DTO和entity对象的属性名有一部分一致
        BeanUtils.copyProperties(employeeDTO,employee);

        // 对entity的其他属性初始化
        employee.setStatus(StatusConstant.ENABLE); //状态
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes())); //密码
        employee.setCreateTime(LocalDateTime.now()); //创建时间
        employee.setUpdateTime(LocalDateTime.now()); //修改时间
        // todo 后期需要改为当前用户的属性
        employee.setCreateUser(10L); //创建人
        employee.setUpdateUser(10L); //修改人

        employeeMapper.insert(employee);
    }
  1. 完善EmployeeMapper接口中的sql。EmployeeMapper.java
    /**
     * 插入员工数据
     * @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})")
    void insert(Employee employee);
  1. 通过接口文档测试功能(常用)或者是前后端联调测试。
    通过接口文档测试的时候,发出的请求需要带上token令牌。因此需要添加一个全局变量,名称为token。
    在这里插入图片描述
  2. 完善登录功能。重复录入,抛出异常没有处理;新增员工,创建人和修改人设置的是固定值。完善好仍要进行测试。
    在这里插入图片描述
    在这里插入图片描述
//1.重复录入,异常处理即可
//GlobalExceptionHandler.java
    /**
     * 处理sql异常
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        String message = ex.getMessage();
        if(message.contains("Duplicate entry")){
            String username = message.split(" ")[2];
            String msg = username + MessageConstant.ALREADY_EXISTS;
            return Result.error(msg);
        }else{
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }


//2.使用threadLocal方法解决获取当前用户id
//BaseContext.java
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();
    }

}
//JwtTokenAdminInterceptor.java
BaseContext.setCurrentId(empId);

//EmployeeServiceImpl.java
employee.setCreateUser(BaseContext.getCurrentId()); //创建人
employee.setUpdateUser(BaseContext.getCurrentId()); //修改人

二、员工分页查询

//EmployeeController.java
	/**
     * 员工分页查询
     * @param employeePageQueryDTO
     * @return
     */
    @GetMapping
    @ApiOperation("员工分页查询")
    public Result<PageResult> pageQuery(EmployeePageQueryDTO employeePageQueryDTO){
        log.info("员工分页查询,参数为:{}", employeePageQueryDTO);
        PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
        return Result.success(pageResult);
    }

//EmployeeServiceImpl.java
    /**
     * 员工分页查询
     * @param employeePageQueryDTO
     * @return
     */
    @Override
    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);
    }
  
//EmployeeMapper.java
    /**
     * 员工分页查询
     * @param employeePageQueryDTO
     * @return
     */
    Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);

//EmployeeMapper.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.EmployeeMapper">
    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from
        <where>
            <if test="name != null and name != ''">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>

完善代码:1时间的问题
在这里插入图片描述

    /**
     * 扩张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);
    }

三、启用禁用员工账号

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

//EmployeeServiceImpl.java
    /**
     * 启用禁用员工账号
     * @param status
     * @param id
     * @return
     */
    public void startOrStop(Integer status, Long id) {
        //update employee set status=? where id = ?
        //为了更通用的使用update方法,在mapper中定义的是通用的update方法,每次传递一个实体对象,而不是修改的属性

//        Employee employee = new Employee();
//        employee.setStatus(status);
//        employee.setId(id);

        Employee employee = Employee.builder()
                .status(status)
                .id(id)
                .build();
        employeeMapper.update(employee);
    }

//EmployeeMapper.xml
    <update id="update" parameterType="com.sky.entity.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="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>

四、编辑员工

两个接口,根据id查询员工,修改员工信息

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

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

//EmployeeServiceImpl.java
    /**
     * 根据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);
    }

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

五、导入分类模块功能代码

在这里插入图片描述

day3

一、公共字段自动填充

在这里插入图片描述

//AutoFill.java
package com.sky.annotation;

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 {
    //数据库操作类型:UPDATE INSERT
    OperationType value();
}

//AutoFullAspect.java
package com.sky.aspect;

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.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.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     * @param joinPoint
     */
    @Before(value = "autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("开始进行公共字段自动填充...");
        //获取当前被拦截方法的数据库操作类型
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();//方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得数据库操作类型

        //获取当前被拦截方法的参数--实体对象
        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0) return;
        Object entity = args[0];

        //为公共属性赋值,时间和当前用户的id【反射方式】
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();
        if(operationType == 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(operationType == 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();
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述

二、文件上传的接口开发

补充学习:阿里云OSS(对象存储服务OSS)【Java web2023版本 第148集】
在这里插入图片描述
配置alioss
在这里插入图片描述
在这里插入图片描述

//配置属性类 AliOssProperties.java
package com.sky.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

}
//配置类 OssConfiguration.java
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;

/**
 * 配置类,用于创建AliOssUtil对象
 */
@Configuration
@Slf4j
public class OssConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
        log.info("开始创建阿里云文件上传工具类对象:{}", aliOssProperties);
        return new AliOssUtil(aliOssProperties.getEndpoint(),
                aliOssProperties.getAccessKeyId(),
                aliOssProperties.getAccessKeySecret(),
                aliOssProperties.getBucketName());
    }
}

//CommonController.java
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;

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

    @Autowired
    private AliOssUtil aliOssUtil;

    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> upload(MultipartFile file){
        log.info("文件上传:{}",file);
        try {
            //原始文件名
            String originalFilename = file.getOriginalFilename();
            //截取原始文件名的后缀
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            //构造新文件名称
            String objectName = UUID.randomUUID().toString() + extension;
            //文件的请求路径
            String filePath = aliOssUtil.upload(file.getBytes(), objectName);
            return Result.success(filePath);
        } catch (IOException e) {
            log.error("文件上传失败:{}",e);
            e.printStackTrace();
        }
        return Result.error(MessageConstant.UPLOAD_FAILED);
    }
}

三、新增菜品

增加一条菜品记录的同时需要增加多条口味记录,用到事务和插入数据的id获取

//DishController.java
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {
    @Autowired
    private DishService dishService;

    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO){
        log.info("新增菜品:{}",dishDTO);
        dishService.saveWithFlavor(dishDTO);
        return Result.success();
    }
}

//DishServiceImpl.java
@Service
@Slf4j
public class DishServiceImpl implements DishService {
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;
    /**
     * 新增菜品和对应的口味
     * @param dishDTO
     */
    @Transactional
    public void saveWithFlavor(DishDTO dishDTO) {
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);
        //向菜品表插入一条数据
        dishMapper.insert(dish);
        //获取insert语句生成的主键值
        Long dishId = dish.getId();

        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0){
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishId);
            });
            //向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors);
        }

    }
}
//DishMapper.java
	/**
     * 增加菜品
     * @param dish
     */
    @AutoFill(value = OperationType.INSERT)
    void insert(Dish dish);

//DishMapper.xml
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into dish (name, category_id, price, image, description, status,
            create_time, update_time, create_user, update_user)
            values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status},
            #{createTime}, #{updateTime}, #{createUser}, #{updateUser})
    </insert>
 
//DishFavorMapper.java
    /**
     * 批量插入口味数据
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);

//DishFavorMapper.xml
    <insert id="insertBatch">
        insert into dish_flavor (dish_id, name, value) values
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>

四、菜品分页查询

根据页码展示信息,每页展示10条数据,可以根据需要输入菜品名称、分类、状态进行查询

//DishController.java
    /**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
        log.info("菜品分页查询:{}", dishPageQueryDTO);
        PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }

//DishServiceImpl.java
    /**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
        PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
        Page<DishVO> page =  dishMapper.pageQuery(dishPageQueryDTO);
        return new PageResult(page.getTotal(),page.getResult());
    }
    
//DishMapper.java
    /**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);
    
//DishMapper.xml
    <select id="pageQuery" resultType="com.sky.vo.DishVO">
        select d.*, c.name as categoryName from dish d LEFT OUTER JOIN category c on d.category_id = c.id
        <where>
            <if test="name != null">and d.name like concat('%',#{name},'%')</if>
            <if test="categoryId != null">and d.category_id like concat('%',#{categoryId},'%')</if>
            <if test="status != null">and d.status like concat('%',#{status},'%')</if>
        </where>
        order by d.create_time desc
    </select>

五、删除菜品

单个删除和批量删除;在售卖的菜品不能删除;被套餐关联的菜品不能删除;删除菜品后,口味也要删除;
【事务;批量查询语句;批量删除语句】

//DishMapperController.java
    /**
     * 批量删除菜品
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("批量删除菜品")
    public Result delete(@RequestParam List<Long> ids){
        log.info("批量删除菜品,参数:{}",ids);
        dishService.delete(ids);
        return Result.success();
    }

//DishServiceImpl.java
    /**
     * 批量删除菜品
     * @param ids
     */
    @Transactional
    public void delete(List<Long> ids) {
        //判断是否为售卖的菜品
        for (Long id : ids) {
            Dish dish = dishMapper.getById(id);
            if(dish.getStatus() == StatusConstant.ENABLE){
                throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
            }
        }
        //判断是否关联套餐
        List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);
        if(setmealIds != null && setmealIds.size()>0){
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
        }
        //删除菜品表中的菜品数据并删除菜品关联的口味数据
//        for (Long id : ids) {
//            dishMapper.deleteById(id);
//            //
//            dishFlavorMapper.deleteByDishId(id);
//        }
        //删除的时候可以直接批量删除,减少sql语句
        dishMapper.deleteByIds(ids);  //删除菜品
        dishFlavorMapper.deleteByDishIds(ids);  //删除口味
    }

//DishMapper.java
   /**
     * 根据主键查询菜品
     * @param id
     * @return
     */
    @Select("select * from dish where id = #{id}")
    Dish getById(Long id);

    /**
     * 根据主键删除菜品
     * @param id
     */
    @Delete("delete from dish where id = #{id}")
    void deleteById(Long id);

    /**
     * 根据主键批量删除菜品
     * @param ids
     */
    void deleteByIds(List<Long> ids);

//DishMapper.xml
    <delete id="deleteByIds">
        delete from dish where id in
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </delete>
    
//SetmealDishMapper.java
    /**
     * 根据多个菜品id查询对应的套餐id
     * @param dishIds
     * @return
     */
    List<Long> getSetmealIdsByDishIds(List<Long> dishIds);

//SetmealDishMapper.xml
    <select id="getSetmealIdsByDishIds" resultType="java.lang.Long">
        select setmeal_id from setmeal_dish where dish_id in
        <foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
            #{dishId}
        </foreach>
    </select>

//DishFlavorMapper.java
    /**
     * 根据菜品id删除口味数据
     * @param dishId
     */
    @Delete("delete from dish_flavor where dish_id = #{dishId}")
    void deleteByDishId(Long dishId);

    /**
     * 根据菜品id批量删除口味数据
     * @param dishIds
     */
    void deleteByDishIds(List<Long> dishIds);

//DishFlavorMapper.xml
    <delete id="deleteByDishIds">
        delete from dish_flavor where dish_id in
        <foreach collection="dishIds" open="(" close=")" separator="," item="dishId">
            #{dishId}
        </foreach>
    </delete>

六、修改菜品

查询用于回显数据;展示菜品分类;图片的重新上传;修改菜品

//DishController.java
    /**
     * 修改菜品
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO){
        log.info("修改菜品,参数为{}", dishDTO);
        dishService.updateWithFlavor(dishDTO);
        return Result.success();
    }
    
//DishServiceImpl.java
    /**
     * 根据id修改菜品和对应的口味信息
     * @param dishDTO
     */
    @Transactional
    public void updateWithFlavor(DishDTO dishDTO) {
        //修改菜品
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);
        dishMapper.update(dish);

        //口味表,先全部删除,在重新插入
        dishFlavorMapper.deleteByDishId(dishDTO.getId());
        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0){
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishDTO.getId());
            });
            //向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors);
        }
    }

//DishMapper.java
    /**
     * 修改菜品
     * @param dish
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Dish dish);
    
//DishMapper.xml
    <update id="update">
        update dish
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="categoryId != null">category_id = #{categoryId},</if>
            <if test="price != null">price = #{price},</if>
            <if test="image != null">image = #{image},</if>
            <if test="description != null">description = #{description},</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>

七、起售停售菜品

//DishController.java
    /**
     * 菜品的起售、停售
     * @param status
     * @param id
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("菜品的起售停售")
    public Result startOrStop(@PathVariable Integer status, Long id){
        log.info("菜品的起售、停售,参数为:{}",status);
        dishService.startOrStop(status, id);
        return Result.success();
    }

//DishServiceImpl.java
    /**
     * 菜品的起售、停售
     * @param status
     * @param id
     */
    public void startOrStop(Integer status, Long id) {
        Dish build = Dish.builder().status(status).id(id).build();
        dishMapper.update(build);
        // 停售菜品的时候,把含相关菜品的套餐也停售
        if (status == StatusConstant.DISABLE) {
            List<Long> dishIds = new ArrayList<>();
            dishIds.add(id);
            // select setmeal_id from setmeal_dish where dish_id in (?,?,?)
            List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(dishIds);
            if (setmealIds != null && setmealIds.size() > 0) {
                for (Long setmealId : setmealIds) {
                    Setmeal setmeal = Setmeal.builder()
                            .id(setmealId)
                            .status(StatusConstant.DISABLE)
                            .build();
                    setmealMapper.update(setmeal);
                }
            }
        }
    }

day4

项目实战—套餐管理

一、新增套餐

增加一条套餐记录,并返回主键;增加多条对应的套餐菜品数据;显示出菜品列表;上传文件

//SetmealController.java
    /**
     * 新增套餐
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    public Result save(@RequestBody SetmealDTO setmealDTO){
        log.info("新增套餐,参数为:{}", setmealDTO);
        setmeatService.saveWithDish(setmealDTO);
        return Result.success();
    }
    
//SetmealServiceImpl.java
    /**
     * 新增套餐和对应的菜品
     * @param setmealDTO
     */
    @Transactional
    public void saveWithDish(SetmealDTO setmealDTO) {
        Setmeal setmeal = new Setmeal();
        BeanUtils.copyProperties(setmealDTO,setmeal);
        //向套餐表插入一条数据
        setmealMapper.insert(setmeal);
        //获取insert语句生成的主键值
        Long setmealId = setmeal.getId();

        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        if(setmealDishes != null && setmealDishes.size()>0){
            setmealDishes.forEach(setmealDish -> {
                setmealDish.setSetmealId(setmealId);
            });
            //向套餐菜品表插入多条数据
            setmealDishMapper.insertBatch(setmealDishes);
        }
    }
    
//SetmealMapper.java
    /**
     * 增加套餐
     * @param setmeal
     */
    @AutoFill(value = OperationType.INSERT)
    void insert(Setmeal setmeal);

//SetmealMapper.xml
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into setmeal (category_id, name, price, status, description, image, create_time,
                update_time, create_user, update_user) VALUES
                (#{categoryId}, #{name}, #{price}, #{status}, #{description}, #{image}, #{createTime},
                 #{updateTime}, #{createUser}, #{updateUser})
    </insert>

//SetmealDishMapper.java
    /**
     * 批量插入菜品数据
     * @param setmealDishes
     */
    void insertBatch(List<SetmealDish> setmealDishes);
    
//SetmealDishMapper.xml
    <insert id="insertBatch">
        insert into setmeal_dish (setmeal_id, dish_id, name, price, copies) VALUES
        <foreach collection="setmealDishes" item="sd" separator=",">
            (#{sd.setmealId}, #{sd.dishId}, #{sd.name}, #{sd.price}, #{sd.copies})
        </foreach>
    </insert>
    
//DishMapper.java
    /**
     * 根据分类id查询菜品
     * @param categoryId
     * @return
     */
    @Select("select * from dish where category_id = #{categoryId}")
    List<Dish> getByCategoryId(Integer categoryId);

二、套餐分页查询

分页查询,使用PageHelper,返回类型为Page。需要进行多表查询,(套餐表和分类表)

//SetmealController.java
    /**
     * 套餐分页查询
     * @param setmealPageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("套餐分页查询")
    public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){
        log.info("套餐分页查询,参数为:{}",setmealPageQueryDTO);
        PageResult pageResult = setmeatService.pageQuery(setmealPageQueryDTO);
        return Result.success(pageResult);
    }
    
//SetmealServiceImpl.java
    /**
     * 套餐分页查询
     * @param setmealPageQueryDTO
     * @return
     */
    public PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {
        PageHelper.startPage(setmealPageQueryDTO.getPage(),setmealPageQueryDTO.getPageSize());
        Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO);
        return new PageResult(page.getTotal(),page.getResult());
    }
    
//SetmealMapper.java
    /**
     * 套餐分页查询
     * @param setmealPageQueryDTO
     * @return
     */
    Page<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
    
//SetmealMapper.xml
    <select id="pageQuery" resultType="com.sky.vo.SetmealVO">
        select s.*, c.name as categoryName from setmeal s LEFT OUTER JOIN category c on s.category_id = c.id
        <where>
            <if test="name != null">and s.name like concat('%',#{name},'%')</if>
            <if test="categoryId != null">and s.category_id like concat('%',#{categoryId},'%')</if>
            <if test="status != null">and s.status like concat('%',#{status},'%')</if>
        </where>
        order by s.create_time desc
    </select>

三、删除套餐

删除套餐的时候,起售中的不能删除。同时需要将相关的套餐菜品数据删除

//SetmealController.java
    /**
     * 批量删除套餐
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("批量删除套餐")
    public Result delete(@RequestParam List<Long> ids){
        log.info("批量删除套餐,参数为:{}",ids);
        setmeatService.delete(ids);
        return Result.success();
    }

//SetmealServiceImpl.java
    /**
     * 根据id批量删除套餐
     * @param ids
     */
    @Transactional
    public void delete(List<Long> ids) {
        //判断是否为售卖的套餐
        for (Long id : ids) {
            Setmeal setmeal = setmealMapper.getById(id);
            if (setmeal.getStatus() == StatusConstant.ENABLE){
                throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
            }
        }

        //删除套餐和套餐菜品数据,直接批量删除
        setmealMapper.deleteByIds(ids);
        setmealDishMapper.deleteBySetmealIds(ids);
    }

//SetmealMapper.java
    /**
     * 批量删除套餐
     * @param ids
     */
    void deleteByIds(List<Long> ids);

//SetmealMapper.xml
    <delete id="deleteByIds">
        delete from setmeal where id in
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </delete>

//SetmealDishMapper.java
    /**
     * 根据套餐id批量删除套餐菜品数据
     * @param setmealIds
     */
    void deleteBySetmealIds(List<Long> setmealIds);

//SetmealDishMapper.xml
    <delete id="deleteBySetmealIds">
        delete from setmeal_dish where setmeal_id in
        <foreach collection="setmealIds"  open="(" close=")" separator="," item="setmealId">
            #{setmealId}
        </foreach>
    </delete>

四、修改套餐

先根据id查询套餐用于数据回显,查询套餐,查询套餐菜品,将两者封装为VO对象返回给前端;
接着修改套餐表和套餐菜品表,套餐菜品表中的相关记录先全部删除,再全部插入

//SetmealController.java
    /**
     * 根据id查询套餐
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询套餐")
    public Result<SetmealVO> getById(@PathVariable Long id){
        log.info("根据id查询套餐,参数为:{}", id);
        SetmealVO setmealVO = setmeatService.getByIdWithSetmealDish(id);
        return Result.success(setmealVO);
    }

    /**
     * 修改套餐
     * @param setmealDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改套餐")
    public Result update(@RequestBody SetmealDTO setmealDTO){
        log.info("修改套餐,参数为:{}",setmealDTO);
        setmeatService.updateWithSetmealDish(setmealDTO);
        return Result.success();
    }
    
//SetmealServiceImpl.java
    /**
     * 根据id查询套餐和对应的套餐菜品
     * @param id
     * @return
     */
    public SetmealVO getByIdWithSetmealDish(Long id) {
        //查询套餐
        Setmeal setmeal = setmealMapper.getById(id);
        //查询套餐菜品
        List<SetmealDish> setmealDishes = setmealDishMapper.getByDishIds(id);
        //封装为VO
        SetmealVO setmealVO = new SetmealVO();
        BeanUtils.copyProperties(setmeal,setmealVO);
        setmealVO.setSetmealDishes(setmealDishes);
        return setmealVO;
    }

    /**
     * 修改套餐和对应的套餐菜品数据
     * @param setmealDTO
     */
    @Transactional
    public void updateWithSetmealDish(SetmealDTO setmealDTO) {
        Setmeal setmeal = new Setmeal();
        BeanUtils.copyProperties(setmealDTO,setmeal);
        setmealMapper.update(setmeal);

        //删除套餐中菜品数据
        setmealDishMapper.deleteBySetmealId(setmealDTO.getId());
        //将新的套餐菜品数据插入
        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        if(setmealDishes != null && setmealDishes.size()>0){
            setmealDishes.forEach(setmealDish -> {
                setmealDish.setSetmealId(setmealDTO.getId());
            });
            //向套餐菜品表插入多条数据
            setmealDishMapper.insertBatch(setmealDishes);
        }
    }

//SetmealMapper.java
    /**
     * 根据id查询数据
     * @param id
     * @return
     */
    @Select("select * from setmeal where id = #{id}")
    Setmeal getById(Long id);

    /**
     * 修改套餐数据
     * @param setmeal
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Setmeal setmeal);

//SetmealMapper.xml
    <update id="update">
        update setmeal
        <set>
            <if test="categoryId">category_id = #{categoryId},</if>
            <if test="name">name = #{name},</if>
            <if test="price">price = #{price},</if>
            <if test="status">status = #{status},</if>
            <if test="description">description = #{description},</if>
            <if test="image">image = #{image},</if>
            <if test="updateTime">update_time = #{updateTime},</if>
            <if test="updateUser">update_user = #{updateUser},</if>
        </set>
        where id = #{id}
    </update>

//SetmealDishMapper.java
    /**
     * 批量插入菜品数据
     * @param setmealDishes
     */
    void insertBatch(List<SetmealDish> setmealDishes);

    /**
     * 根据套餐id查询菜品
     * @param setmealId
     * @return
     */
    @Select("select * from setmeal_dish where setmeal_id = #{setmealId)}")
    List<SetmealDish> getByDishIds(Long setmealId);

    /**
     * 根据套餐id删除套餐菜品数据
     * @param setmealId
     */
    @Delete("delete from setmeal_dish where setmeal_id = #{setmealId}")
    void deleteBySetmealId(Long setmealId);

//SetmealDishMapper.xml
    <insert id="insertBatch">
        insert into setmeal_dish (setmeal_id, dish_id, name, price, copies) VALUES
        <foreach collection="setmealDishes" item="sd" separator=",">
            (#{sd.setmealId}, #{sd.dishId}, #{sd.name}, #{sd.price}, #{sd.copies})
        </foreach>
    </insert>

五、起售停售套餐

修改套餐中的状态即可

//SetmealController.java
    /**
     * 套餐起售、停售
     * @param status
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("套餐起售、停售")
    public Result startOrStop(@PathVariable Integer status, Long id){
        log.info("套餐起售、停售,参数为:{}",status);
        setmeatService.startOrStop(status,id);
        return Result.success();
    }
  
//SetmealServiceImpl.java
    /**
     * 套餐起售、停售
     * @param status
     * @param id
     */
    public void startOrStop(Integer status,Long id) {
        // 起售套餐要先判断所含菜品是否起售
        if(status == StatusConstant.ENABLE){
            // 根据套餐setmeal_id在setmeal_dish表中查询dish_id,接着根据菜品dish_id在dish表中查询status
            List<Long> dishIds = setmealDishMapper.getDishIdsBySetmealId(id);
            for (Long dishId : dishIds) {
                Dish dish = dishMapper.getById(dishId);
                if(dish.getStatus() == StatusConstant.DISABLE) {
                	// status = 0时,抛出异常
                    throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
                }
            }
        }
        Setmeal build = Setmeal.builder().status(status).id(id).build();
        setmealMapper.update(build);
    }

// SetmealDishMapper.java
    /**
     * 根据套餐id查询套餐菜品数据
     * @param setmealId
     */
    @Select("select dish_id from setmeal_dish where setmeal_id = #{setmealId)}")
    List<Long> getDishIdsBySetmealId(Long setmealId);

day5

一、Redis入门

1.1 Redis简介

  • 是一个基于内存的key-value结构数据库
  • 基于内存存储、读写性能高
  • 适合存储热点数据

1.2 下载与安装

windows安装包直接解压,就可直接使用
启动服务端:
在这里插入图片描述
客户端连接:
在这里插入图片描述

有密码的连接方式在这里插入图片描述
在配置文件中设置密码
在这里插入图片描述
也可以安装redis客户端another redis desktop manager,通过图像界面操作客户端。新建连接localhost,要确保redis服务器打开。点击就可以看到存储的键值对信息。
在这里插入图片描述

二、常用数据类型

在这里插入图片描述

三、常用命令

1. 字符串常用操作命令
set key value                          # 设置指定key的值
get key                                # 获取指定key的值
setex key seconds value                 # 设置指定key的值,并将key的过期时间设为seconds秒
setnx key value                        # 只有在key不存在时设置key的值

2. 哈希常用操作命令
hset key field value                   # 将哈希表key中的字段field的值设为value
hget key field                         # 获取存储在哈希表中指定字段的值
hdel key field                         # 删除存储在哈希表中指定字段
hkeys key                              # 获取哈希表中所有字段
hvals key                              # 获取哈希表中所有值

3. 列表常用操作命令
lpush key value1 [value2]              # 将1个或多个值插入到列表头部
lrange key start stop                  # 获取列表指定范围内的元素
rpop key                               # 移除并获取列表最后一个元素
llen key                               # 获取列表长度

4. 集合常用操作命令
sadd key member1 [member2]             # 向集合添加一个或多个成员
smembers key                           # 返回集合中的所有成员
scard key                              # 获取集合的成员数
sinter key1 [key2]                     # 返回给定所有集合的交集
sunion key1 [key2]                     # 返回所有给定集合的并集
srem key member1 [member2]             # 删除集合中一个或多个成员

5. 有序集合常用操作命令
zadd key score1 member1 [score2 member2]  # 向有序集合添加一个或多个成员
zrange key start stop [WITHSCORES]        # 通过索引区间返回有序集合中指定区间内的成员
zincrby key increment member              # 有序集合中对指定成员的分数加上增量increment
zrem key member [member]                  # 移除有序集合中的一个或多个成员

6. 通用命令
keys pattern                            # 查找所有符合给定模式(pattern)的key
exists key                              # 检查给定key是否存在
type key                                # 返回key所存储的值的类型
del key                                 # 在key存在的时候删除key

四、在java中操作Redis

4.1 Redis的java客户端

常用的有Jedis、Lettuce、Spring Data Redis
在这里插入图片描述

4.2 Spring Data Redis使用方式操作步骤:

  1. 导入Spring Data Redis 的maven坐标
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  1. 配置Redis数据源
spring:
  redis:
    host: ${sky.redis.host}
    port: ${sky.redis.port}
    password: ${sky.redis.password}
    database: ${sky.redis.database}
  1. 编写配置类,创建RedisTemplate对象
package com.sky.config;

@Configuration
@Slf4j
public class RedisConfiguration {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        log.info("开始创建redis模板对象...");
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

  1. 通过RedisTemplate对象操作Redis,这里使用springboot的测试类操作对象
package com.sky.test;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class SpringDataRedisTest {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate(){
        System.out.println(redisTemplate);
    }

    /**
     * 操作字符串类型的数据
     */
    @Test
    public void testString(){
        // set get setex setnx
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("name","小明");
        String name = (String)valueOperations.get("name");
        valueOperations.set("code","1234",3, TimeUnit.MINUTES);
        valueOperations.setIfAbsent("lock","1");
        valueOperations.setIfAbsent("lock","2");
        System.out.println(name);
    }

    /**
     * 操作哈希类型的数据
     */
    @Test
    public void testHash(){
        // hset hget hdel hkeys hvals
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.put("100","name","tom");
        hashOperations.put("100","age","20");

        String name = (String)hashOperations.get("100","name");
        System.out.println(name);

        Set keys = hashOperations.keys("100");
        System.out.println(keys);

        List values = hashOperations.values("100");
        System.out.println(values);

        hashOperations.delete("100","age");
    }

    /**
     * 操作列表类型的数据
     */
    @Test
    public void testList(){
        //lpush lrange rpop llen
        ListOperations listOperations = redisTemplate.opsForList();
        listOperations.leftPushAll("list","1","2","3","4","5");
        listOperations.leftPush("list","a");
        listOperations.leftPush("list","b");
        listOperations.leftPush("list","c");

        List list = listOperations.range("list",1, 2);
        System.out.println(list);

        Object value = listOperations.leftPop("list");
        System.out.println(value);

        Long len = listOperations.size("list");
        System.out.println(len);
    }


    /**
     * 操作集合类型的数据
     */
    @Test
    public void testSet(){
        //sadd smembers scard sinter sunion srem
        SetOperations setOperations = redisTemplate.opsForSet();
        setOperations.add("set","a","b","c","d");
        setOperations.add("set1","1","2","3","a");

        Set members = setOperations.members("set");
        System.out.println(members);

        Long len = setOperations.size("set");
        System.out.println(len);

        Set intersect = setOperations.intersect("set", "set1");
        System.out.println(intersect);

        Set union = setOperations.union("set", "set1");
        System.out.println(union);

        setOperations.remove("set","a","b");
    }

    /**
     * 操作有序集合类型的数据
     */
    @Test
    public void testzSet(){
        //zadd zrange zincrby zrem
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add("zset","3",0.1);
        zSetOperations.add("zset","2",0.8);
        zSetOperations.add("zset","8",0.7);

        Set zset = zSetOperations.range("zset", 1, 2);
        System.out.println(zset);

        zSetOperations.incrementScore("zset","3",0.8);

        zSetOperations.remove("zset","3");
    }

    /**
     * 操作通用命令
     */
    @Test
    public void testCommon(){
        //keys exists type del
        Set keys = redisTemplate.keys("*");
        System.out.println(keys);

        Boolean code = redisTemplate.hasKey("code");
        Boolean list = redisTemplate.hasKey("list");

        for (Object key : keys) {
            DataType type = redisTemplate.type(key);
            System.out.println(type.name());
        }

        redisTemplate.delete("list");
    }
}

五、店铺营业状态设置

5.1 代码实现

三个接口:设置店铺的营业状态;管理端获取店铺的营业状态;用户端获取店铺的营业状态。

//admin.ShopController.java
package com.sky.controller.admin;

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Slf4j
@Api(tags = "店铺相关接口")
public class ShopController {
    public static final String KEY = "SHOP_STATUS";
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 设置店铺的营业状态
     * @param status
     * @return
     */
    @PutMapping("/{status}")
    @ApiOperation("设置店铺的营业状态")
    public Result setStatus(@PathVariable Integer status){
        log.info("设置店铺营业状态为:{}",status == 1 ? "营业中" : "打样中");
        redisTemplate.opsForValue().set(KEY,status);
        return Result.success();
    }

    /**
     * 获取店铺的营业状态
     * @return
     */
    @GetMapping("/status")
    @ApiOperation("获取店铺的营业状态")
    public Result<Integer> getStatus(){
        Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
        log.info("获取店铺的营业状态为:{}", status == 1 ? "营业中" : "打样中");
        return Result.success(status);
    }
}

//user.ShopController
package com.sky.controller.user;

import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("userShopController")
@RequestMapping("/user/shop")
@Slf4j
@Api(tags = "店铺相关接口")
public class ShopController {
    public static final String KEY = "SHOP_STATUS";
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 获取店铺的营业状态
     * @return
     */
    @GetMapping("/status")
    @ApiOperation("获取店铺的营业状态")
    public Result<Integer> getStatus(){
        Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
        log.info("获取店铺的营业状态为:{}", status == 1 ? "营业中" : "打样中");
        return Result.success(status);
    }
}

5.2 接口文档实现分组管理

在配置文件WebConfiguration.java中,新增加docket()方法,并修改相应内容,使其扫描不同的包
在这里插入图片描述
效果为:
在这里插入图片描述

  • 32
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
黑马程序员苍穹外卖项目中的Nginx配置文件可以根据具体需求进行配置。根据引用\[1\]中的描述,可以通过双击nginx.exe启动Nginx,并在http://localhost/访问前端页面。这意味着Nginx的配置文件应该包含有关前端页面的相关配置。另外,根据引用\[2\]中的描述,Nginx还可以用作反向代理和负载均衡,因此配置文件还应包含有关反向代理和负载均衡的相关配置。最后,根据引用\[3\]中的描述,苍穹外卖项目还需要与第三方配送公司进行对接和管理,因此配置文件还应包含有关与第三方配送公司对接的相关配置。综上所述,黑马程序员苍穹外卖项目的Nginx配置文件应包含前端页面的相关配置、反向代理和负载均衡的相关配置以及与第三方配送公司对接的相关配置。 #### 引用[.reference_title] - *1* [黑马程序员_Java项目实战《苍穹外卖》_Day01_开发环境搭建](https://blog.csdn.net/BallerWang9/article/details/131824385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [最适合新手的SpringBoot+SSM项目苍穹外卖》实战—(一)项目概述](https://blog.csdn.net/qq_20185737/article/details/131575898)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值