苍穹外卖
本文最后更新于46 天前,其中的信息可能已经过时,如有错误请发送邮件到1169063119@qq.com

软件开发流程

需求分析:需求规格说明书、产品原型

设计:ui设计、数据库设计、接口设计

编码:项目代码、单元测试

测试:测试用例、测试报告

上线运维:软件环境安装、配置

角色分工

项目经理:对整个项目负责,任务分配、把控进度

产品经理:进行需求调研,输出需求调研文档、产品原型等

ui设计师:根据产品原型输出界面效果图

架构师:项目整体架构设计、技术选型等

开发工程师:代码实现

测试工程师:编写测试用例,输出测试报告

运维工程师:软件环境搭建、项目上线

软件环境

开发环境:开发人员在开发阶段使用的环境,一般外部用户无法访问

测试环境:专门给测试人员使用的环境,用于测试项目,一般外部用户无法访问

生产环境:就是线上环境,正式提供对外服务的环境

苍穹外卖项目介绍

定位:专门为餐饮企业定制的一款软件产品

管理端:外卖商家使用

员工管理 分类管理、菜品管理、套餐管理、订单管理、工作台、数据统计、来单提醒

用户端:点餐用户使用

微信登录、商品浏览、购物车、用户下单、微信支付、历史订单、地址管理、用户催单

功能架构:体现项目中的业务功能模块

产品原型:用于展示项目的业务功能

技术选型:展示项目中使用到的技术框架和中间件等

用户层:node.js、VUE.js、ElementUI、微信小程序、apache echart

网关层:nginx

应用层:Spring Boot、Spring MVC、Spring Task、httpclient、Spring Cache、JWT、阿里云OSS、Swagger、POI、WebSocket

数据层:MySQL、Redis、mybatis、pagehelper、spring data redis

工具:Git、maven、Junit、postman

完善登录功能-密码加密

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

修改Java代码,前端提交的密码进行MD5加密后再跟数据库中密码比对

Swagger

使用Swagger只需要按照他的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口测试页面

Swagger在开发阶段使用的框架,帮助后端开发人员做后端的接口测试

使用方式:

  • 导入knife4j的maven坐标
  • 在配置类中加入knife4j相关配置
  • 设置静态资源映射,否则接口文档页面无法访问

功能测试

1.通过接口文档测试

2.通过前后端联调测试

由于开发阶段前端和后端是并行开发的,后端完成某个功能后,此时前端对应的功能可能还没有开发完成,导致无法进行前后端联调测试。所以在开发阶段,后端测试主要以接口文档测试为主。

新增员工部分代码

    @PostMapping()
    @ApiOperation("新增员工") //添加swagger的接口描述
    public Result save(@RequestBody EmployeeDTO employeeDTO){ //如果是json格式的数据,需要加上@RequestBody注解
        log.info("新增员工:{}",employeeDTO);  //{}是占位符
        employeeService.save(employeeDTO);
        return Result.success();
    }
    public void save(EmployeeDTO employeeDTO) {
        Employee  employee = new Employee();

        //通过set方法设置属性过于繁琐
//        employee.setName(employeeDTO.getName());

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

        //设置账号状态,默认正常状态,1表示正常,0表示锁定
        employee.setStatus(StatusConstant.ENABLE);

        //设置密码,对密码进行md5加密处理
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //设置创建时间、更新时间
//        employee.setCreateTime(LocalDateTime.now());
//        employee.setUpdateTime(LocalDateTime.now());

        //设置创建人、更新人
//        employee.setCreateUser(BaseContext.getCurrentId());
//        employee.setUpdateUser(BaseContext.getCurrentId());

        employeeMapper.insert(employee);
    }

员工分页查询部分代码

    @PostMapping("/page")
    @ApiOperation("员工分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO  employeePageQueryDTO){
    log.info("员工分页查询,参数:{}",employeePageQueryDTO);
    PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO); //调用emloyeeService,返回PageResult对象
        return Result.success(pageResult); //返回结果并封装到Result中
    }
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//select * from employee limit 0,10 底层是基于limit关键字来查询
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO); //底层基于MyBatis的分页插件进行分页查询
//Page对象包含了分页查询的所有数据

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

return new PageResult(total,records);
}

启用禁用员工账号部分代码

@PostMapping("/status/{status}")
    @ApiOperation("启用禁用员工账号")
    public Result startOrStop(@PathVariable Integer status, Long id){
        log.info("启用或禁用员工账号{},{}",status,id);
        employeeService.startOrstop(status,id);
        return Result.success();
    }
    @Override
public void startOrstop(Integer status, Long id) {
//update employee set status = ? where id = ?

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

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

//update语句设置为动态的,根据传进来的参数的不同,可以修改多个字段
employeeMapper.update(employee);
}

根据id查询员工信息

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

编辑员工信息

    @PutMapping
    @ApiOperation("编辑员工信息")
    public Result update(@RequestBody EmployeeDTO employeeDTO){
        log.info("编辑员工信息:{}",employeeDTO);
        employeeService.update(employeeDTO);
        return Result.success();A
    }
    public void update(EmployeeDTO employeeDTO) {
Employee employee = new Employee(); // 接受到的是emploeeDTO,我们要的是employee,所以要对数据进行转换
BeanUtils.copyProperties(employeeDTO,employee); //属性拷贝

//DTO中没有修改时间及修改者,所以要单独设置
// employee.setUpdateTime(LocalDateTime.now());
// employee.setUpdateUser(BaseContext.getCurrentId());

employeeMapper.update(employee);
}

自定义注解

用于标识某个方法需要进行功能字段自动填充处理

AutoFill

@Target(ElementType.METHOD) //指定当前注解会加在什么位置,指定我们的方法只能加在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型:update insert两种,查询操作不需要设置
    OperationType value();
}

AutoFillAspect

@Aspect //表示这是一个切面类
@Component
@Slf4j
public class AutoFillAspect {
    /**
     * 切入点,即对哪些类的哪些方法进行拦截
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

        /**
         * 前置通知,在通知中进行公共字段的赋值
         */
        @Before("autoFillPointCut()")
        public void autoFill(JoinPoint joinPoint){
            log.info("开始进行公共字段的填充");

            //获取到当前被拦截的方法上的数据库操作类型
            MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //方法签名对象
            AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); //获取方法上的注解对象
            OperationType operationType = autoFill.value(); //获取数据库操作类型

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

            //准备赋值的数据
            LocalDateTime now = LocalDateTime.now();
            Long currentId = BaseContext.getCurrentId();

            //根据当前不同的操作类型,为对应的属性通过反射来赋值
            if (operationType == OperationType.INSERT){
                //为4个公共字段赋值
                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){
                //为2个公共字段赋值
                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();
                }
            }

        }

}

新增菜品

DishController.java

@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

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

}

DishService.java

 public void saveWithFlavor(DishDTO dishDTO); //除了菜品信息外,还要保存菜品口味数据

DishServiceImpl.java

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    /**
     * 新增菜品及对应的口味
     * @param dishDTO
     */
    @Override
    public void saveWithFlavor(DishDTO dishDTO) {

        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);

        //向菜品表插入1跳数据
        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)); // 设置菜品id
            //向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors); //  批量插入

        }

    }
}

DishDao.java

@Data
public class DishDTO implements Serializable {

    private Long id;
    //菜品名称
    private String name;
    //菜品分类id
    private Long categoryId;
    //菜品价格
    private BigDecimal price;
    //图片
    private String image;
    //描述信息
    private String description;
    //0 停售 1 起售
    private Integer status;
    //口味
    private List<DishFlavor> flavors = new ArrayList<>();

}

DishMapper.java

@Mapper
public interface DishMapper {

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

    /**
     * 插入菜品数据
     * @param dish
     */
    @AutoFill(value = OperationType.INSERT)
    void insert(Dish dish);
}

DishFlavorMapper.java

@Mapper
public interface DishFlavorMapper {

    /**
     * 批量插入口味数据
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);
}

DishFlavorMapper.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.DishFlavorMapper">
    <insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
/* useGeneratedKeys 添加后,mybatis 会自动填充主键
   keyProperty 添加后,mybatis 会将主键填充到实体类中*/
        insert into dish_flavor(dish_id, name, value) values
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>
</mapper>

DishMapper.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.DishMapper">

    <insert id="insert">
        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>
</mapper>

菜品分页查询部分代码

    @GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){ //菜品查询需要返回值
        log.info("菜品分页查询:{}", dishPageQueryDTO);
        PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }
@Override
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}

删除菜品部分代码

    @DeleteMapping()
    @ApiOperation("批量删除菜品")
    public Result delete(@RequestParam List<Long> ids){ //@RequestParam是告诉SpringMVC,这个参数的值是请求参数中的ids
        log.info("批量删除菜品:{}",ids);
        dishService.deleteBatch(ids); //批量删除
        return Result.success();
    }
 @Transactional //涉及多个表操作,要使用事务注解
 public void deleteBatch(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);
        }


    }

修改菜品部分代码

    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO){
        log.info("修改菜品:{}", dishDTO);
        dishService.updateWithFlavor(dishDTO);
        return Result.success();
    }
    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())); // 设置菜品id
            //向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors); //  批量插入

        }
    }

起售停售菜品部分代码

    @PostMapping("/status/{status}")
    @ApiOperation("起售、停售菜品")
    public Result startOrStop(@PathVariable Integer status, Long id){
        log.info("起售、停售菜品:{} {}", status,id);
        dishService.startOrStop(status,id);
        return Result.success();
    }
    @Transactional
    public void startOrStop(Integer status, Long id) {
        Dish dish = Dish.builder()
                .id(id)
                .status(status)
                .build();
        dishMapper.update(dish);

        if (status == StatusConstant.DISABLE){
            //如果停售了菜品,需要清理当前菜品的关联数据( dish_flavor、setmeal_dish)
            List<Long> dishIds = new ArrayList<>();
            dishIds.add(id);
            List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(dishIds);
            if (setmealIds != null && setmealIds.size() > 0){
                for (Long setmealId : setmealIds){
                    Setmeal setmeal = Setmeal.builder()
                            .id(setmealId)
                            .status(status)
                            .build();
                    setmealMapper.update(setmeal);
                }
            }
        }
    }

新增套餐部分代码

    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<Dish>> list(Long categoryId){
        log.info("根据分类id查询菜品:{}", categoryId.toString());
        List<Dish> list = dishService.list(categoryId);
        return Result.success(list);
    }
    public List<Dish> list(Long categoryId) {
        Dish dish = Dish.builder() // dish 继承了dishDTO
                .categoryId(categoryId) //  设置菜品分类id
                .status(StatusConstant.ENABLE) //  设置状态为起售状态
                .build(); // 构建查询条件
        return dishMapper.list(dish);
    }
    @PostMapping()
    @ApiOperation("新增套餐")
    public Result save(@RequestBody SetmealDTO setmealDTO){
        log.info("新增套餐:{}",setmealDTO);
        setmealService.saveSetMeal(setmealDTO);
        return Result.success();
    }
    @Transactional
    public void saveSetMeal(SetmealDTO setmealDTO) {
        //创建套餐对象
        Setmeal setmeal = new Setmeal();
        BeanUtils.copyProperties(setmealDTO,setmeal);

        //新增一份套餐
        setmealMapper.insert(setmeal);

        //获取生成的套餐id
        Long setmealId = setmeal.getId();

        //获取套餐中菜品信息
        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        setmealDishes.forEach(setmealDish -> { //遍历套餐中的菜品
            setmealDish.setSetmealId(setmealId); //设置套餐id
        });

        //保存套餐和菜品的关联关系
        setmealDishMapper.insertBatch(setmealDishes); //批量保存
    }

套餐分页查询部分代码

@GetMapping("/page")
@ApiOperation("分页查询")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO) {
    log.info("套餐分页查询:{}", setmealPageQueryDTO);
    PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);
    return Result.success(pageResult);
}
public PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {
    int pageNum = setmealPageQueryDTO.getPage();
    int pageSize = setmealPageQueryDTO.getPageSize();

    PageHelper.startPage(pageNum, pageSize);
    Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO);
    return new PageResult(page.getTotal(), page.getResult());
}

删除套餐部分代码

    @DeleteMapping
    @ApiOperation("批量删除套餐")
    public Result delete(@RequestParam List<Long> ids){
        log.info("批量删除套餐:{}", ids);
        setmealService.deleteBatch(ids);
        return Result.success();
    }
@Transactional
public void deleteBatch(List<Long> ids) {
    ids.forEach(id -> {
        Setmeal setmeal = setmealMapper.getById(id);
        if(StatusConstant.ENABLE == setmeal.getStatus()){
            //起售中的套餐不能删除
            throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
        }
    });

    ids.forEach(setmealId -> {
        //删除套餐表中的数据
        setmealMapper.deleteById(setmealId);
        //删除套餐菜品关系表中的数据
        setmealDishMapper.deleteBySetmealId(setmealId);
    });
}

修改套餐部分代码

    @GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    public Result<List<Setmeal>> list(Long categoryId) {
        Setmeal setmeal = new Setmeal();
        setmeal.setCategoryId(categoryId);
        setmeal.setStatus(StatusConstant.ENABLE);

        List<Setmeal> list = setmealService.list(setmeal);
        return Result.success(list);
    }
    @PutMapping
    @ApiOperation("修改套餐")
    public Result update(@RequestBody SetmealDTO setmealDTO){ //@RequestBody是告诉SpringBoot,这个参数是JSON数据,需要转换成对象
        log.info("修改套餐:{}", setmealDTO);
        setmealService.updatesetmeal(setmealDTO);
        return Result.success();
    }
    @Transactional
    public void updatesetmeal(SetmealDTO setmealDTO) {
        Setmeal setmeal = new Setmeal();
        BeanUtils.copyProperties(setmealDTO,setmeal);

        //修改套餐基本信息
        setmealMapper.update(setmeal);

        //获取套餐id
        Long setmealId = setmealDTO.getId();

        //删除原有的套餐数据
        setmealDishMapper.delectBySetmealId(setmealId);

        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        setmealDishes.forEach(setmealDish -> {
            setmealDish.setSetmealId(setmealId);
        });

        //重新插入新的套餐数据
        setmealDishMapper.insertBatch(setmealDishes);
    }
    public SetmealVO getByIdWithDish(Long id) {
        Setmeal setmeal = setmealMapper.getById(id); //  查询套餐数据
        List<SetmealDish> setmealDishes = setmealDishMapper.getBySetmealId(id); //  查询套餐菜品关系

        SetmealVO setmealVO = new SetmealVO(); //  封装套餐数据
        BeanUtils.copyProperties(setmeal,setmealVO);
        setmealVO.setSetmealDishes(setmealDishes);

        return setmealVO;
    }

起售停售套餐

    @PostMapping("/status/{status}")
    @ApiOperation("套餐起售停售")
    public Result startOrStop(@PathVariable Integer status, Long id){  //@PathVariable是告诉SpringBoot,这个参数是路径参数,需要从路径中获取
        log.info("套餐起售停售:{}", status);
        setmealService.startOrStop(status,id);
        return Result.success();
    }
    public void startOrStop(Integer status, Long id) {
        //起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"
        if (status ==  StatusConstant.ENABLE){
            List<Dish> dishList = dishMapper.getBySetmealId(id);
            if (dishList != null && dishList.size() > 0){ //  有菜品
                dishList.forEach(dish -> {
                    if (StatusConstant.DISABLE == dish.getStatus()) {
                        throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
                    }
                });
            }
        }

        Setmeal setmeal = Setmeal.builder()
                .id(id)
                .status(status)
                .build();
        setmealMapper.update(setmeal);
    }

店铺的营业状态

管理端

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {

    public static final String KEY = "SHOP_STATUS"; // Redis中存储店铺营业状态的key

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     *  设置店铺营业状态
     * @param status
     * @return
     */
    @PutMapping ("/{status}")
    @ApiOperation("设置店铺的营业状态")
    public Result setStatus(@PathVariable Integer status){ //@PathVariable注解用于获取url中的动态参数
        log.info("设置店铺营业状态为:{}",status == 1 ? "营业中" : "打烊中");
        redisTemplate.opsForValue().set(KEY,status);
        return Result.success();
    }

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

用户端

@RestController("userShopController")
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {

    public static final String KEY = "SHOP_STATUS";

    @Autowired
    private RedisTemplate redisTemplate;

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

微信登录

@ApiOperation("微信登录")
@PostMapping ("/login")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
log.info("微信登录,参数:{}",userLoginDTO.getCode());

//微信登陆
User user = userService.wxLogin(userLoginDTO);

//为微信用户生成jwt令牌
HashMap<String, Object> claims = new HashMap<>(); // jwt令牌中存储的额外信息
claims.put(JwtClaimsConstant.USER_ID,user.getId());
String token =JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);

UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
    //微信服务接口地址
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
    private final WeChatProperties weChatProperties;
    private final UserMapper userMapper;

    public UserServiceImpl(WeChatProperties weChatProperties, UserMapper userMapper) {
        this.weChatProperties = weChatProperties;
        this.userMapper = userMapper;
    }

    @Override
    public User wxLogin(UserLoginDTO userLoginDTO) {

        String openid = getopenid(userLoginDTO.getCode()); //  获取openid

        //判断openid是否为空,如果为空表示登陆失败,抛出业务异常
        if (openid == null){
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
        }

        //判断当前用户是否为新用户
        User user = userMapper.getByOpenid(openid);

        //如果是新用户,自动完成注册
        if(user == null){
            user = User.builder()
                    .openid(openid)
                    .createTime(LocalDateTime.now())
                    .build();
            userMapper.insert(user);
        }

        //但会这个用户对象,返回给前端
        return user;
    }

    /**
     *调用微信接口服务,获得当前微信用户的openid
     * @param code
     * @return
     */
    private String getopenid(String code){
        Map<String,String> map = new HashMap<>();
        map.put("appid",weChatProperties.getAppid());
        map.put("secret",weChatProperties.getSecret());
        map.put("js_code",code);
        map.put("grant_type","authorization_code");
        String json = HttpClientUtil.doGet(WX_LOGIN,map);

        JSONObject jsonObject = JSON.parseObject(json);
        String openid = jsonObject.getString("openid");
        return openid;
    };

商品浏览

CategoryController

@RestController("userCategoryController")
@RequestMapping("/user/category")
@Api(tags = "C端-分类接口")
public class CategoryController {
@Autowired
private CategoryService categoryService;

/**
 * 查询分类
 * @param type
 * @return
 */
@GetMapping("/list")
@ApiOperation("查询分类")
public Result<List<Category>> list(Integer type) {
    List<Category> list = categoryService.list(type);
    return Result.success(list);
}

}

DishController

@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {
@Autowired
private DishService dishService;

/**
 * 根据分类id查询菜品
 *
 * @param categoryId
 * @return
 */
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
    Dish dish = new Dish();
    dish.setCategoryId(categoryId);
    dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品

    List<DishVO> list = dishService.listWithFlavor(dish);

    return Result.success(list);
 }
}

DishService

public interface DishService {
/**
 * 条件查询菜品和口味
 * @param dish
 * @return
 */
List<DishVO> listWithFlavor(Dish dish);
}

DishServiceImpl

@Service
public class DishServiceImpl implements DishService {
@Autowired
private DishMapper dishMapper;
@Autowired
private DishFlavorMapper dishFlavorMapper;
@Autowired
private SetmealDishMapper setmealDishMapper;
@Autowired
private SetmealMapper setmealMapper;


/**
 * 条件查询菜品和口味
 * @param dish
 * @return
 */
public List<DishVO> listWithFlavor(Dish dish) {
    List<Dish> dishList = dishMapper.list(dish);

    List<DishVO> dishVOList = new ArrayList<>();

    for (Dish d : dishList) {
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(d,dishVO);

        //根据菜品id查询对应的口味
        List<DishFlavor> flavors = dishFlavorMapper.getByDishId(d.getId());

        dishVO.setFlavors(flavors);
        dishVOList.add(dishVO);
    }

    return dishVOList;
 }
}

SetmealController

@RestController("userSetmealController")
@RequestMapping("/user/setmeal")
@Api(tags = "C端-套餐浏览接口")
public class SetmealController {
@Autowired
private SetmealService setmealService;
/**
 * 条件查询
 *
 * @param categoryId
 * @return
 */
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
public Result<List<Setmeal>> list(Long categoryId) {
    Setmeal setmeal = new Setmeal();
    setmeal.setCategoryId(categoryId);
    setmeal.setStatus(StatusConstant.ENABLE);

    List<Setmeal> list = setmealService.list(setmeal);
    return Result.success(list);
}

/**
 * 根据套餐id查询包含的菜品列表
 *
 * @param id
 * @return
 */
@GetMapping("/dish/{id}")
@ApiOperation("根据套餐id查询包含的菜品列表")
public Result<List<DishItemVO>> dishList(@PathVariable("id") Long id) {
    List<DishItemVO> list = setmealService.getDishItemById(id);
    return Result.success(list);
}

SetmealService

public interface SetmealService {
/**
 * 条件查询
 * @param setmeal
 * @return
 */
List<Setmeal> list(Setmeal setmeal);

/**
 * 根据id查询菜品选项
 * @param id
 * @return
 */
List<DishItemVO> getDishItemById(Long id);
}

SetmealServiceImpl

@Service
@Slf4j
public class SetmealServiceImpl implements SetmealService {
@Autowired
private SetmealMapper setmealMapper;
@Autowired
private SetmealDishMapper setmealDishMapper;
@Autowired
private DishMapper dishMapper;

/**
 * 条件查询
 * @param setmeal
 * @return
 */
public List<Setmeal> list(Setmeal setmeal) {
    List<Setmeal> list = setmealMapper.list(setmeal);
    return list;
}

/**
 * 根据id查询菜品选项
 * @param id
 * @return
 */
public List<DishItemVO> getDishItemById(Long id) {
    return setmealMapper.getDishItemBySetmealId(id);
 }
}

SetmealMapper

@Mapper
public interface SetmealMapper {
/**
 * 动态条件查询套餐
 * @param setmeal
 * @return
 */
List<Setmeal> list(Setmeal setmeal);

/**
 * 根据套餐id查询菜品选项
 * @param setmealId
 * @return
 */
@Select("select sd.name, sd.copies, d.image, d.description " +
        "from setmeal_dish sd left join dish d on sd.dish_id = d.id " +
        "where sd.setmeal_id = #{setmealId}")
List<DishItemVO> getDishItemBySetmealId(Long setmealId);
}

SetmealMapper.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.SetmealMapper">

    <select id="list" parameterType="Setmeal" resultType="Setmeal">
        select * from setmeal
        <where>
            <if test="name != null">
                and name like concat('%',#{name},'%')
            </if>
            <if test="categoryId != null">
                and category_id = #{categoryId}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
    </select>
</mapper>

清理缓存

    private void cleanCache(String key){
        Set keys = redisTemplate.keys(key);
        redisTemplate.delete(keys);
    }
cleanCache("dish_*");
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇