黑马苍穹外卖

文章目录

项目介绍

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

一.环境搭建配置:

在这里插入图片描述

后端环境搭建 - 熟悉项目结构:

后端环境搭建 - 熟悉项目结构

Common类:

在这里插入图片描述
constant :常量类
context:上下文
enumeration:枚举类
exception:异常处理类
json:jison转换类
properties:配置属性类
result:返回结果类、
utils:工具类

Pojo类:

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

二.前后端交互:

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

nginx反向代理

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

在这里插入图片描述

前端发请求然后通过负载均衡分配后端服务器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

blog.csdnimg.cn/direct/50c70ac41f0649ddb837b1f88bd71671.png)

登录加密处理

在这里插入图片描述

在这里插入图片描述
使用springframework工具类: DigestUtils.md5DigestAsHex()
在这里插入图片描述

接口定义:前后端开发流程

在这里插入图片描述

Swagger: Knife4j依赖

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Swagger接口生成文档步骤:

package com.sky.config;

import com.sky.interceptor.JwtTokenAdminInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

/**
 * 配置类,注册web层相关组件
 */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)
                .addPathPatterns("/admin/**")
                .excludePathPatterns("/admin/employee/login");
    }

    /**
     * 通过knife4j生成接口文档
     * @return
     */
    @Bean
    public Docket docket() {
        log.info("准备生成接口文档");
        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
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始设置静态资源映射");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}



在这里插入图片描述

Swagger常用注解:

在这里插入图片描述

三.员工管理模块:

新增员工

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

在这里插入图片描述

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

后端新增功能:
//控制层
@RestController
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {
	@PostMapping
    @ApiOperation("新增员工")
    public Result save( @RequestBody EmployeeDTO employeeDTO){
        log.info("新增员工:{}",employeeDTO);
        employeeService.save(employeeDTO);
        return Result.success() ;
    }
   }
// 服务层
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

@Override
    public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
//        对象属性拷贝
        BeanUtils.copyProperties(employeeDTO,employee);
//        设置账号的状态,默认正常状态 1 锁定0
        employee.setStatus(StatusConstant.ENABLE);
//        默认密码
        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(19L);

        employeeMapper.insert(employee);

    }
  }
 
//服务层接口
public interface EmployeeService {

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

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

}

//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;

}
	
//数据库实体类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;

    private Integer status;

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

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

    private Long createUser;

    private Long updateUser;

}
//mapper
@Mapper
public interface EmployeeMapper  {

    /**
     * 根据用户名查询员工
     * @param username
     * @return
     */
    @Select("select * from employee where username = #{username}")
    Employee getByUsername(String username);

    @Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status)" +
            "values" +
            "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    void insert(Employee employee);
}

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

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 处理sql异常
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
//        Duplicate entry 'zhangsan' for key 'employee.idx_username'
        log.error("异常信息:{}",ex.getMessage());
        String message =ex.getMessage();
//        如果包含"Duplicate entry" :键值对已存在的意思
        if (message.contains("Duplicate entry")){
//            数组的第三个 “ ” 空格
           String[] split = message.split(" ");
            String username = split[2];
            String msg = username + MessageConstant.ALREADY_EXISTS;
            return  Result.error(msg);

        }else {
            return Result.error(MessageConstant.UNKNOWN_ERROR);

        }

    }
}

新增员工: 获取创建人和修改人的id

在拦截器校验成功时 解析token中的登录用户的id值(token中会带登录用户的id):
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分页查询:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在pom中导入pageheper依赖


		<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>

DTO类

@Data
public class EmployeePageQueryDTO implements Serializable {

    //员工姓名
    private String name;

    //页码
    private int page;

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

}

application.yml配置:

mybatis:
  #mapper配置文件
  //配置扫描mapper地址
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.sky.entity
  configuration:
    #开启驼峰命名
    map-underscore-to-camel-case: true

EmployeeMapper类

public interface EmployeeMapper  {

    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 employee
        <where>
            <if test="name !=null and name != ''">
            //CONCAT函数  1.功能 将多个字符串连接成一个字符串
            //返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。

   // mysql> SELECT CONCAT('张三','李四','王五');
	//result> 张三李四王五
	//——————————————————————————————————————————————
	//mysql> SELECT CONCAT('张三','李四',NULL);
	//result> NUll
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>

server层

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

  @Override
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO){
        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);
    }
}

server接口层

 /**
     * 页码分页
     * @param employeePageQueryDTO
     * @return
     */
public interface EmployeeService {
	PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);

}

Controller控制层

@RestController
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {
 @GetMapping("/page")
    @ApiOperation("员工分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
        log.info("员工分页查询,参数为 {}:",employeePageQueryDTO);
        PageResult pageResult= employeeService.pageQuery(employeePageQueryDTO);
        return Result.success(pageResult);
    }
}
日期格式化:

在这里插入图片描述

  /**
     * 扩展Spring MVC框架的消息转化器
     * @param converters
     */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
	@Autowired
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
//        创建一个消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//        需要为消息转换器设置一个对象转换器,对象转换器可以将java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
//        将自己的消息转化器加入到容器中
//        0:代表顺序排在第一位最先调用
        converters.add(0,converter);
    }
}
/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
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);
    }
}

员工账号启用和禁用状态设置:

在这里插入图片描述
Controller层

@RestController
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;
    @Autowired
    private JwtProperties jwtProperties;
       /**
     * 启用禁用员工账号
     * @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();
    }
}

Service层

  @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();
        employeeMapper.update(employee);
    }

EmployeeMapper 类

@Mapper
public interface EmployeeMapper  {
 void update(Employee employee);
}

EmployeeMapper,xml

<!--    配置类做了扫描 可以扫描的到类型别名 不用写完整地址-->
    <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>

编辑员工(根据id查询):

查询id显示人员的目的是:
点击修改按钮页面保留原来的数据 (数据有内容不是空白的)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Controller层
/**
     * 根据id查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询员工信息")
    public Result<Employee> getById(@PathVariable Long id){
        log.info("id查询员工信息:{}",id);
        Employee employee = employeeService.getById(id);
        return  Result.success(employee);
    }
    /**
     * 编辑员工信息
     * @param employeeDTO
     */
    @PutMapping
    @ApiOperation("修改员工信息")
    public Result<Employee> update(@RequestBody  EmployeeDTO employeeDTO){
        log.info("编辑员工信息:{}",employeeDTO);
        employeeService.update(employeeDTO);
        return  Result.success();
    }
EmployeeService接口和和Impl 实现类
接口----------------------------------------------
   /**
     * 编辑员工信息
     * @param employeeDTO
     */
    void update(EmployeeDTO employeeDTO);
    /**
     * 根据id查询
     * @param id
     * @return
     */
    Employee getById(Long id);
    



实现类--------------------------------------
    /**
     * 根据id查询
     * @param id
     * @return
     */
    @Override
    public Employee getById(Long id){
        Employee employee = employeeMapper.getById(id);
        employee.setPassword("****");
        return employee;
    }

    /**
     * 编辑员工信息
     * @param employeeDTO
     */
    @Override
    public void update(EmployeeDTO employeeDTO){
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO,employee);
        employee.setUpdateUser(BaseContext.getCurrentId());
        employee.setUpdateTime(LocalDateTime.now());
        employeeMapper.update(employee);
    }
Mapper
  /**
     * 根据主键动态修改属性         修改写在xml里
     * @param employee
     */

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

四.分类模块

导入分类模块功能代码

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

在这里插入图片描述
然后把类导进来就好。。。。。。。
在这里插入图片描述

公共字段填充

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

AutoFill注解类

/**
 * 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
 *
 * @author ayyy
 * @date 2024/03/13  18:26
 */
//@Target:当前自定义注解加在什么位置  ElementType.METHOD:加在方法上
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//    数据库操作类型: UPDATE    INSERT
    OperationType value();
}
AutoFillAspect类
/**
 * 自定义切面,实现公共字段自动填充处理逻辑
 *
 * @author ayyy
 * @date 2024/03/13  18:32
 */
//切面注解
@Aspect
//bean注解
@Component
//日志注解
@Slf4j
public class AutoFillAspect {
    /**
     * 切入点
     */
    /* 可以使用通配符描述切入点,快速描述
    * :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public *(代表返回值任意) com.itheima.*.UserService.find*(*(一个任意参数)))
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法


..  :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..(多个任意)))
匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法

+ :专用于匹配子类类型:
execution(*(任意返回值) *(任意包)..*Service+(类u或接口的子类).*(..))
*/
//    execution:指定拦截位置 ,第一个*代表  @annotation:自定义注解位置
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){

    }

    /**
     * 前置通知,在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
//    连接点:JoinPoint (获取 参数类型 ,值)
    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) {
                throw new RuntimeException(e);
            }

        }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) {
                throw new RuntimeException(e);
            }

        }
    }
}


在mapper类中加入AutoFill注解
/**
     * 根据主键动态修改属性
     * @param employee
     */
//    自定义注解加入才能通知到
    @AutoFill(value = OperationType.UPDATE)
    void update(Employee employee);
     /**
     * 新增员工
     * @param
     * @return
     */
    @Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status)" +
            "values" +
            "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    @AutoFill(value = OperationType.INSERT)
    void insert(Employee employee);

常用类
/**
 * 数据库操作类型
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT

}

五.菜品模块

新增菜品

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
新增菜品类中有新增口味类所以要分开写:
Controller层

@RestController
@RequestMapping("/admin/dish")
@Slf4j
@Api(tags = "菜品相关接口")
public class DishController {
    @Autowired
    private DishService dishService;
    @PostMapping()
    @ApiOperation(value = "新增菜品")
    public Result  save(@RequestBody DishDTO dishDTO){
        log.info("新增菜品:{}",dishDTO);
        dishService.saveWithFlavor(dishDTO);
        return Result.success();
    }
}

DishService层

public interface DishService {
    /**
     * 新增菜品对应口味数据
     * @param dishDTO
     */
    public void saveWithFlavor(DishDTO dishDTO);
}

    private DishFlavorMapper dishFlavorMapper;
    @Override
    @Transactional
    public void saveWithFlavor(DishDTO dishDTO) {
//       向菜品表插入1条数据
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO,dish);
        dishMapper.insert(dish);
        //在mapper中设置返回主键值
        //获取inset语句生成的主键值:id
        Long dishId = dish.getId();
        //向口味表插入n条数据
        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (!flavors.isEmpty() && flavors.size()>0){
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishId);
            });
            dishFlavorMapper.insertBatch(flavors);
        }

    }
}


Mapper层:

DishMapper

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

<?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">
<!--  useGeneratedKeys="true":需要主键值返回 keyProperty="id":返回主键值id   -->
    <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>
</mapper>

DishFlavorMapper

@Mapper
public interface DishFlavorMapper {
       void insertBatch(List<DishFlavor> flavors);
}

<?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">
        insert into dish_flavor (dish_id, name, value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>
</mapper>

阿里云上传文件

设置配置类

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

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

}

配置yml文件:
 alioss:
    endpoint: ${sky.alioss.endpoint}
    access-key-id: ${sky.alioss.access-key-id}
    access-key-secret: ${sky.alioss.access-key-secret}
    bucket-name: ${sky.alioss.bucket-name}
配置环境变量:
 alioss:
    endpoint: oss-cn-beijing.aliyuncs.com
    access-key-id: LTAI5tQ2s7fkHymv9xHjLcFL
    access-key-secret: 70MRFuxAXcw9bglrCs3A1QujopPdeW
    bucket-name: ayyy-object-photo

阿里云工具类:

需要自己配置文件访问路径


@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {

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

    /**
     * 文件上传
     *
     * @param bytes
     * @param objectName
     * @return
     */
    public String upload(byte[] bytes, String objectName) {

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 创建PutObject请求。
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }

        //文件访问路径规则 https://BucketName.Endpoint/ObjectName
        StringBuilder stringBuilder = new StringBuilder("https://");
        stringBuilder
                .append(bucketName)
                .append(".")
                .append(endpoint)
                .append("/")
                .append(objectName);

        log.info("文件上传到:{}", stringBuilder.toString());

        return stringBuilder.toString();
    }
}
创建配置工具类对象:
/**
 * 配置类,用于创建AliOssUtil对象
 *
 * @author ayyy
 * @date 2024/03/15  10:55
 */
@Configuration
@Slf4j
public class OssConfiguration {
//    @Bean 项目启动调用方法 把对象创建 交给spring容器管理
    @Bean
//    @ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个。
    //当没有这个bean在去创建(保证创建一次)
    @ConditionalOnMissingBean
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
        log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
       return new AliOssUtil(aliOssProperties.getEndpoint(),
                aliOssProperties.getAccessKeyId(),
                aliOssProperties.getAccessKeySecret(),
                aliOssProperties.getBucketName());

    }
}
CommonController(上传文件接口):
@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();
        //截取原始文件名的后缀   例如:dfdfdf.png
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        //构造新文件名称
        String objectName = UUID.randomUUID().toString() + extension;

        //文件的请求路径
        //objectName 防止文件名称覆盖 所以重命名
            String filePath = aliOssUtil.upload(file.getBytes(), objectName);
            return Result.success(filePath);
        } catch (IOException e) {
            log.error("文件上传失败:{}",e);
        }
        return null;
        }
} 

菜品分页查询

在这里插入图片描述

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

Controller层
@RestController
@RequestMapping("/admin/dish")
@Slf4j
@Api(tags = "菜品相关接口")
public class DishController {
    @Autowired
    private DishService dishService;
   
    @GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
        log.info("菜品分页查询:{}",dishPageQueryDTO);
        PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }
}
Service层
public interface DishService {
  
/**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);


@Service
@Slf4j
public class DishServiceImpl implements DishService {
        @Autowired
        private  DishMapper dishMapper;
        @Autowired
        private DishFlavorMapper dishFlavorMapper;
     /**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    @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());
    }

}

Mapper层:
@Mapper
public interface DishMapper {
/**
     * 菜品分页查询
     * @param dishPageQueryDTO
     * @return
     */
    Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);
    }
<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>
          //  注意: <!--                         这里id用错表了导致查询不到-->
      //    <!--            <if test="categoryId != null">-->
//<!--                and d.id =#{categoryId}--  要用categoryId>
            <if test="categoryId != null">
                and d.category_id = #{categoryId}
            </if>
            <if test="status != null">
                and d.status = #{status}
            </if>
        </where>
        order by d.create_time desc
    </select>

删除菜品

在这里插入图片描述
菜品删除功能在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Controller层
@DeleteMapping()
    @ApiOperation("菜品删除功能")
    public Result delete(@RequestParam List<Long> ids){
        log.info("菜品删除:{}",ids);
        dishService.deleteBatch(ids);
        return Result.success();
    }
Service接口
public interface DishService {
    /**
     * 菜品批量删除
     * @param ids
     */
    void deleteBatch(List<Long> ids);
}

Service层
@Override
    @Transactional
    public void deleteBatch(List<Long> ids) {
//可以一次删除一个菜品,也可以批量删除菜品
        //起售中的菜品不能删除
        for (Long id : ids) {
            Dish dish = dishMapper.getById(id);
            if (dish.getStatus().equals(StatusConstant.ENABLE)){
               throw  new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
            }
        }
        //被套餐关联的菜品不能删除
        List<Long> setmealIdsDishIds = setmealDishMapper.getSetmealIdsDishIds(ids);
        if (!setmealIdsDishIds.isEmpty() && setmealIdsDishIds.size() > 0){
            throw  new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);
        }
        //删除菜品后,关联的口味数据也需要删除掉
        for (Long id : ids) {
            dishMapper.deleteById(id);
            dishFlavorMapper.deleteBatch(id);
        }
    }

Mapper层:
DishMapper
 /**
     * 根据id查询
     * @param id
     * @return
     */
    @Select("select * from dish where id=#{id} ")
    Dish getById(Long id);

    /**
     * 根据id删除
     * @param ids
     */
    @Delete("delete from dish where id=#{id} ")
    void deleteById(Long ids);
DishFlavorMapper
@Mapper
public interface DishFlavorMapper {
     

       /**
        * 根据菜品id删除
        * @param dishId
        */
       @Delete("delete from dish_flavor where dish_id=#{dishId}")
       void deleteBatch(Long dishId);
}
SetmealDishMapper
Mapper
public interface SetmealDishMapper {

    /**
     *  根据多个菜品id查询套餐id
     * @param dishIds
     * @return
     */
    List<Long> getSetmealIdsDishIds(List<Long> dishIds);
}

Mapper.xml:
DishFlavorMapper
<?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">
        insert into dish_flavor (dish_id, name, value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>
</mapper>
SetmealDishMapper
<?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.SetmealDishMapper">


    <select id="getSetmealIdsDishIds" resultType="java.lang.Long">
        select setmeal_id from setmeal_dish where dish_id in
//#  collection="dishIds":Mapper类的形参名字  item="dishId":遍历后出数据的别名 要和 #{dishId}一致  就是给where  dish_id(这个取的别名)  separator=",":遍历一次的分隔符  open="(" :开始前 close=")":结束后
        <foreach collection="dishIds" item="dishId" open="(" separator="," close=")">
            #{dishId}
        </foreach>

    </select>
</mapper>

优化:

    //删除菜品后,关联的口味数据也需要删除掉
//          批量删除菜品
         dishMapper.deleteBatch(ids);
//           批量删除口味
        dishFlavorMapper.deleteBatchDishFlavor(ids);


------------------------------------------------------------------
        <delete id="deleteBatch"  >
        delete from dish where id in
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
--------------------------------------------------------------------
   <delete id="deleteBatchDishFlavor">
        delete from dish_flavor where dish_id in
        <foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
            #{dishId}
        </foreach>
    </delete>
    }

修改菜品

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

Controller层

先回显数据

 @GetMapping("/{id}")
    @ApiOperation("根据id查询菜品")
    public Result<DishVO> getById(@PathVariable Long id){
        log.info("根据id查询菜品:{}",id);
            DishVO dishVO = dishService.getByIdWithFlavor(id);
            return Result.success(dishVO);
    }

    @PutMapping()
    @ApiOperation("菜品修改功能")
    public Result update(@RequestBody DishDTO dishDTO){
        dishService.update(dishDTO);
        return Result.success();
    }
Service接口
/**
     *  根据id查询菜品和对应的口味数据
     * @param id
     * @return
     */
    DishVO getByIdWithFlavor(Long id);

    /**
     * 修改菜品
     * @param dishDTO
     */
    void update(DishDTO dishDTO);
Service层
  /**
     * 根据id查询菜品和对应的口味数据
     *
     * @param id
     * @return
     */
 @Override
 
    public DishVO getByIdWithFlavor(Long id) {
//        根据id查询菜品数据
        Dish dish = dishMapper.getById(id);
//        根据菜品id查询口味数据
        List<DishFlavor> dishFlavors = dishFlavorMapper.getflavordishId(id);
//        将查询到的数据封装到VO
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(dish, dishVO);
        dishVO.setFlavors(dishFlavors);
        return dishVO;
    }
     /**
     * 修改菜品
     * @param dishDTO
     */
  @Override
    public void update(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());
                System.out.println(flavors);
            });
            dishFlavorMapper.insertBatch(flavors);
        }
Mapper层:
DishFlavorMapper
/**
        * 根据菜品id查询对应的口味数据
        * @param dishId
        * @return
        */
  @Select("select * from dish_flavor where dish_id =#{dishId}")
       List<DishFlavor> getflavordishId(Long dishId);

       /**
        * 根据菜品id数据删除口味数据
        * @param id
        */
       @Delete("delete from dish_flavor where dish_id=#{dishId}")
       void deleteByDishid(Long id);
 /**
     * 根据id查询
     * @param id
     * @return
     */
    @Select("select * from dish where id=#{id} ")
    Dish getById(Long id);
/**
     * 根据id批量删除
     * @param ids
     */
  void deleteBatch(List<Long> ids);
    /**
     * 修改菜品数据
     * @param dish
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Dish dish);
Mapper.xml:
DishFlavorMapper
<?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">
        insert into dish_flavor (dish_id, name, value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>

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

</mapper>
DishMapper
<?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">
<!--  useGeneratedKeys="true":需要主键值返回 keyProperty="id":返回主键值id   -->
    <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>

    <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 = #{categoryId}
            </if>
            <if test="status != null">
                and d.status = #{status}
            </if>
        </where>
        order by d.create_time desc
    </select>
    <delete id="deleteBatch"  >
        delete from dish where id in
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

    <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>

Redis数据库

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

连接redis

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

在这里插入图片描述

数据类型介绍

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

字符串操作命令

在这里插入图片描述

哈希操作命令

在这里插入图片描述

列表操作命令

补充: ltrim mylist 0 6 (表示列表保留0-6范围的数据)用于多删除
在这里插入图片描述

集合操作命令

在这里插入图片描述

有序集合操作命令

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

java中操作Redis

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

数据源yml
spring:
 datasource:
  redis:
     host: ${sky.redis.host}
     port: ${sky.redis.port}
     password: ${sky.datasource.password}
     database: ${sky.datasource.database}

dev.yml----------------------------------------------
sky:
  redis:
    host: localhost
    port: 6379
    password: 123456
    database: 10
配置类
@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;
    }
}

####java实现操作

/**
 * 功能描述
 *
 * @author ayyy
 * @date 2024/03/21  17:12
 */
@SpringBootTest
public class SpringDataRedisTest {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void testRedisTemplate(){
        System.out.println(redisTemplate);
//        字符串类型
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //        哈希类型
        HashOperations hashOperations = redisTemplate.opsForHash();
        //        列表类型
        ListOperations listOperations = redisTemplate.opsForList();
        //         集合类型
        SetOperations setOperations = redisTemplate.opsForSet();
        //        有序集合类型
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();

    }

    /**
     * 操作字符串类型数据
     */
    @Test
    public void testString(){
        // set get setex setnx
//        添加
        redisTemplate.opsForValue().set("city","北京");
//        获取key的 value
        String city =  (String) redisTemplate.opsForValue().get("city");
        System.out.println(city);
        redisTemplate.opsForValue().set("code","1234",3, TimeUnit.MINUTES);
        redisTemplate.opsForValue().setIfAbsent("lock","1");
        redisTemplate.opsForValue().setIfAbsent("lock","2");
    }
    /**
     * 操作哈希类型数据
     */
    @Test
    public void testHash(){
        //hset hget hdel hkey hvals
        HashOperations hashOperations = redisTemplate.opsForHash();
//        添加
        hashOperations.put("100","name","tom");
        hashOperations.put("100","age","20");
        Object name = hashOperations.get("100", "name");
        System.out.println(name);
//        获取所有的键key(字段 )
        Set keys = hashOperations.keys("100");
        System.out.println(keys);
//        获取所有值
        List values = hashOperations.values("100");
        System.out.println(values);
//        删除
        hashOperations.delete("100","age");
    }

店铺营业状态设置

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

在这里插入图片描述
在这里插入图片描述
设置营业状态就只用Controller传一个值就好

Controller层

/**
 * adimin 店铺相关接口
 *
 * @author ayyy
 * @date 2024/03/21  22:57
 */
@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店铺相关接口
 *
 * @author ayyy
 * @date 2024/03/21  22:57
 */
@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);
    }
}

微信登录

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

HttpClient

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

微信小程序

在这里插入图片描述
在这里插入图片描述
不勾请求发不了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

入门案例
<!--index.wxml-->
<navigation-bar title="Weixin" back="{{false}}" color="black" background="#FFF"></navigation-bar>
<scroll-view class="scrollarea" scroll-y type="list">
  <view class="container">
   <view>
   {{msg}}
   </view>
   <view>
   <!--  bind:tap=绑定事件  "getUserInfo":方法名称-->
   <button bind:tap="getUserInfo" type="primary">获取用户信息</button>
   <!-- 动态获取nickName 传入js中 -->
   昵称:{{nickName}}
   <image style="width: 100px; height: 100px;" src="{{url}}">
   </image>
   </view>
   <view>
     <button bind:tap="wxLogin" type="warn">微信登录</button>
   </view>
  授权码:{{code}}
  </view>
  <view>
    <button bind:tap="sendRequest" type="default">发送请求</button>
  </view>
</scroll-view>

// index.js
Page({
  data:{
    msg :'hello world',
    nickName:"",
    url:"",
    code:""
  },
// 获取微信用户头像和昵称
  getUserInfo(){
    wx.getUserProfile({
      desc: '获取微信用户信息',
      success: (res) =>{
         console.log(res.userInfo);       
        //  为数据赋值
        this.setData({
          nickName: res.userInfo.nickName,
          url: res.userInfo.avatarUrl
        })
      }
    })
  },
// 微信登录,获取微信用户的授权码
 wxLogin(){
  wx.login({
    success: (res) => {
      console.log(res.code);
      this.setData({
        code:res.code
      })
            
    },
  })
 },
 sendRequest(){
  wx.request({
    url: 'http://localhost:8080/user/shop/status',
    method:"GET",
    success:(res)=>{
      // res.data后端返回的全部数据(result的数据)
      console.log(res.data)
      
    }
  })
 }
})

微信登录

首先微信小程序 通过wx.login()方法获取code 然后通过wx.request()发送code给后端 后端通过 设置yml 发送凭证校验接口(就是传地址)然后微信平台会返回openid等 然后我们自己转成json来掉openid的键 获取他的值。

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

application.yml
sky:
  jwt:
    user-secret-key: ayyy
    user-ttl: 720000000
    user-token-name: authentication
JwtProperties
 /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

Controller层
package com.sky.controller.user;

import com.sky.constant.JwtClaimsConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.properties.JwtProperties;
import com.sky.result.Result;
import com.sky.service.UserService;
import com.sky.utils.JwtUtil;
import com.sky.vo.UserLoginVO;
import io.jsonwebtoken.Claims;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * 功能描述
 *
 * @author ayyy
 * @date 2024/03/24  16:32
 */
@RestController
@RequestMapping("/user/user")
@Api("C端用户相关接口")
@Slf4j

public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private JwtProperties jwtProperties;
    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    @PostMapping("/login")
    @ApiOperation("微信登录")
    public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
        log.info("微信登录:{}",userLoginDTO.getCode());
//        微信登录
        User user = userService.wxLogin(userLoginDTO);

//      为微信用户生成jwt令牌
        HashMap<String, Object> claims = new HashMap<>();
        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);

    }
}

Service接口
/**
 * 功能描述
 *
 * @author ayyy
 * @date 2024/03/24  16:37
 */
public interface UserService {

    User wxLogin(UserLoginDTO userLoginDTO);

}
Service层
package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.exception.LoginFailedException;
import com.sky.mapper.UserMapper;
import com.sky.properties.WeChatProperties;
import com.sky.service.UserService;
import com.sky.utils.HttpClientUtil;
import io.netty.handler.codec.MessageAggregationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;

/**
 *
 *
 * @author ayyy
 * @date 2024/03/24  17:03
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private WeChatProperties weChatProperties;
    @Autowired
    private UserMapper userMapper;

    public static final String WX_LOGIN="https://api.weixin.qq.com/sns/jscode2session";
    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    @Override
    public User wxLogin(UserLoginDTO userLoginDTO) {
        String openid = getOpenid(userLoginDTO.getCode());
        //判断当前用户是否是新用户
        if (openid.isEmpty()){
            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){
        HashMap<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");
        //调用微信接口服务,获得当前微信用户的openid
        String json = HttpClientUtil.doGet(WX_LOGIN, map);
        //判断openid是否为空,如果为空登录失败,抛出异常
        JSONObject jsonObject = JSON.parseObject(json);
//        获取key:"openid"键的  值value
        String openid = jsonObject.getString("openid");
        return openid;
    }
}

Mapper层:
package com.sky.mapper;

import com.sky.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * 功能描述
 *
 * @author ayyy
 * @date 2024/03/24  17:32
 */
@Mapper
public interface UserMapper {
    /**
     * 根据openid查询用户
     * @param openid
     * @return
     */
    @Select("select * from user where openid=#{openid}")
    User getByOpenid(String openid);

    /**
     * 插入数据
     * @param user
     */
    void insert(User user);
}

Mapper.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.UserMapper">

    <insert id="insert" useGeneratedKeys="true" keyProperty="id" >
        insert into user(openid, name, phone, sex, id_number, avatar, create_time)
            values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})
    </insert>

</mapper>

Usertoken校验

package com.sky.interceptor;

import com.sky.constant.JwtClaimsConstant;
import com.sky.context.BaseContext;
import com.sky.properties.JwtProperties;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

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

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
//        解析jwt里面自带的id
            Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
            log.info("当前用户id:", userId);
            BaseContext.setCurrentId(userId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

拦截路径
package com.sky.config;

import com.sky.interceptor.JwtTokenAdminInterceptor;
import com.sky.interceptor.JwtTokenUserInterceptor;
import com.sky.json.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.List;

/**
 * 配置类,注册web层相关组件
 */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
    @Autowired
    private JwtTokenUserInterceptor jwtTokenUserInterceptor;

    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)
                .addPathPatterns("/admin/**")
                .excludePathPatterns("/admin/employee/login");
//        添加拦截器
        registry.addInterceptor(jwtTokenUserInterceptor)
//                拦截路径
                .addPathPatterns("/user/**")
//               排除拦截(无需拦截的路径)
                .excludePathPatterns("/user/user/login")
                .excludePathPatterns("/user/shop/status");
    }
    /**
     * 设置静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始设置静态资源映射");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

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

导入商品浏览功能

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

缓存商品数据

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

菜品控制层
public class DishController {
    @Autowired
    private DishService dishService;
    @Autowired
    private RedisTemplate redisTemplate;
    @PostMapping()
    @ApiOperation(value = "新增菜口")
    public Result save(@RequestBody DishDTO dishDTO){
        log.info("新增菜品:{}",dishDTO);
        dishService.saveWithFlavor(dishDTO);
        //清理缓存数据
        String key ="dish"+ dishDTO.getCategoryId();
        cleanCache(key);
        return Result.success();
    }
/**
     * 清理缓存
     * @param pattern
     */
private void cleanCache(String pattern){
        Set keys = redisTemplate.keys(pattern);
        redisTemplate.delete(keys);
    }

缓存套餐

spring Cache

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

@RestController
@RequestMapping("/user")
@Slf4j
@EnableCaching
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @PostMapping
    @CachePut(cacheNames = "userCache",key = "#user.id")
    public User save(@RequestBody User user){
        userMapper.insert(user);
        return user;
    }
    @CacheEvict(cacheNames = "userCache",key = "#id")
    @DeleteMapping
    public void deleteById(Long id){
        userMapper.deleteById(id);
    }
//    userCache下所有的键值对都删除
    @CacheEvict(cacheNames = "userCache", allEntries = true)
	@DeleteMapping("/delAll")
    public void deleteAll(){
        userMapper.deleteAll();
    }

    @Cacheable(cacheNames = "userCache",key = "#id")
    @GetMapping
    public User getById(Long id){
        User user = userMapper.getById(id);
        return user;
    }

}
缓存套餐

在这里插入图片描述

购物车

添加购物车

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

Controller层

@RestController
@RequestMapping("/user/shoppingCart")
@Slf4j
@Api("C端购物车相关接口")
public class ShoppingCartController {
    @Autowired
    private ShoppingCartService shoppingCartService;
 /**
     * 添加购物车
     * @param shoppingCartDTO
     * @return
     */
    @PostMapping("/add")
    @ApiOperation("添加购物车")
    public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){
        log.info("添加购物车,商品信息为:{}",shoppingCartDTO);
        shoppingCartService.addShoppingCart(shoppingCartDTO);
        return Result.success();
    }
}

Service接口

/**
     * 添加购物车
     * @param shoppingCartDTO
     */
    void addShoppingCart(ShoppingCartDTO shoppingCartDTO);

Service层

@Service
@Slf4j
public class ShoppingCartServiceImpl implements ShoppingCartService {
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private SetmealMapper setmealMapper;
    /**
     * 添加购物车
     * @param shoppingCartDTO
     */
    @Override
    public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
         //判断当前加入购物车中的商品是否已经存在
        ShoppingCart shoppingCart = new ShoppingCart();
        BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
        Long userid = BaseContext.getCurrentId();
        shoppingCart.setUserId(userid);

        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);

        //如果已经存在了,只需要数量加一
        if (list != null && list.size()>0){
            ShoppingCart cart = list.get(0);
            cart.setNumber(cart.getNumber()+1);
            shoppingCartMapper.updateNumberById(cart);
        }else{
            //如果不存在插入一条购物车数据
            //判断本次添加到购物车的是菜品还是套餐
            Long dishId = shoppingCartDTO.getDishId();
            if (dishId != null){
                //本次添加到购物车的是菜品
                Dish dish = dishMapper.getById(dishId);
                shoppingCart.setName(dish.getName());
                shoppingCart.setImage(dish.getImage());
                shoppingCart.setAmount(dish.getPrice());
            }else {
                //本次添加到购物车的是套餐
                Long setmealId = shoppingCartDTO.getSetmealId();
                Setmeal setmeal = setmealMapper.getById(setmealId);
                shoppingCart.setName(setmeal.getName());
                shoppingCart.setImage(setmeal.getImage());
                shoppingCart.setAmount(setmeal.getPrice());
            }
            shoppingCart.setNumber(1);
            shoppingCart.setCreateTime(LocalDateTime.now());
                shoppingCartMapper.insert(shoppingCart);

        }


    }
}

Mapper层:

/**
     * 动态条件查询
     * @param shoppingCart
     * @return
     */
    List<ShoppingCart> list(ShoppingCart shoppingCart);
      /**
     * 根据id修改商品数量
     * @param shoppingCart
     */
    @Update("update shopping_cart set number = #{number} where id = #{id}")
    void updateNumberById(ShoppingCart shoppingCart);

Mapper.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.ShoppingCartMapper">

    <select id="list" resultType="com.sky.entity.ShoppingCart">
        select * from shopping_cart
        <where>
            <if test="userId != null">
                and user_id = #{userId}
            </if>
            <if test="setmealId != null">
                and setmeal_id = #{setmealId}
            </if>
            <if test="dishId != null">
                and dish_id = #{dishId}
            </if>
            <if test="dishFlavor != null">
                and dish_flavor = #{dishFlavor}
            </if>
        </where>
    </select>
 </mapper>

查看购物车

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

  /**
     * 查看购物车
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("看查购物车")
    public Result<List<ShoppingCart>> list(){
        List<ShoppingCart> list = shoppingCartService.showShoppingCart();
        return Result.success(list);
    }

Service接口

 /**
     * 查看购物车
     * @return
     */
    List<ShoppingCart> showShoppingCart();

Service实现层

 /**
     * 查看购物车
     * @return
     */
    @Override
    public List<ShoppingCart> showShoppingCart() {
        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCart = ShoppingCart.builder()
                .userId(userId)
                .build();
        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
        return list;
    }

清空购物车

Controller层

@DeleteMapping("/clean")
    @ApiOperation("清空购物车")
    public Result clean(){
        shoppingCartService.cleanShoppingCart();
        return Result.success();
    }
    }

Service接口

 /**
     * 清空购物车
     */
    void cleanShoppingCart();

Service实现层`

  @Override
    public void cleanShoppingCart() {
        //获取当前微信用户id
        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCart = ShoppingCart.builder()
                .userId(userId)
                .build();
        shoppingCartMapper.deleteByUserId(userId);
    }

Mapper层:

/**
     * 根据用户id删除购物车数据
     * @param userId
     */
    @Delete("delete from shopping_cart where user_id = #{userId}")
    void deleteByUserId(Long userId);

导入代码簿

在这里插入图片描述

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

用户订单

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

微信支付

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
6 会返回一个字符串
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

cpolar 安装(创建公网地址 IP)

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

Spring Task

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

/**
 * 定时任务类,定时处理订单状态
 */
@Component
@Slf4j
public class OrderTask {

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 处理超时订单的方法
     */
    //每分钟触发一次
    @Scheduled(cron = "0 * * * * ? ")
    public void processTimeoutOrder(){
        log.info("定时处理超时订单:{}", LocalDateTime.now());

        LocalDateTime time = LocalDateTime.now().plusMinutes(-15);

        // select * from orders where status = ? and order_time < (当前时间 - 15分钟)
        List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);

        if(ordersList != null && ordersList.size() > 0){
            for (Orders orders : ordersList) {
                orders.setStatus(Orders.CANCELLED);
                orders.setCancelReason("订单超时,自动取消");
                orders.setCancelTime(LocalDateTime.now());
                orderMapper.update(orders);
            }
        }
    }

    /**
     * 处理一直处于派送中状态的订单
     */
    @Scheduled(cron = "0 0 1 * * ?") //每天凌晨1点触发一次
    public void processDeliveryOrder(){
        log.info("定时处理处于派送中的订单:{}",LocalDateTime.now());

        LocalDateTime time = LocalDateTime.now().plusMinutes(-60);

        List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, time);

        if(ordersList != null && ordersList.size() > 0){
            for (Orders orders : ordersList) {
                orders.setStatus(Orders.COMPLETED);
                orderMapper.update(orders);
            }
        }
    }
}

WebSocket

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

客户催单

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

Apache ECharts

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

营业额统计

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

用户统计

在这里插入图片描述

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

订单统计

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

销量统计排名

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

工作台

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

Apache POI

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

excel

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

前端

VUE基础

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

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

Router路由

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

vuex 介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第一个变量为函数名setname

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

TypeScript

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

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

在这里插入图片描述

前端环境搭建

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

分页查询

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

套餐分类

在这里插入图片描述

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值