springboot瑞吉外卖

news2024/9/22 19:21:19
  1. 创建数据库,项目初始化
  2. 静态资源不在static目录下,如何映射
  3. 结果类
  4. 登录
  5. 过滤器拦截路径
  6. 全局异常处理器
  7. 分页查询
  8. 消息转换器
  9. 修改禁用
  10. 分页
  11. 编辑
  12. 公共字段自动填充使用ThreadLocal
  13. 新增用户
  14. 绑定的数据不可删除
  15. 上传
  16. 下载
  17. 前端传递的数据,不在同一张表时,DTO
  18. 响应的值,不在一个类中,多表分页
  19. 修改新增菜品,对两张表新增/修改数据
  20. 批量删除
  21. 用户下单
  22. 优化redis
  23. Springcache->redis简化开发
  24. 主从复制、读写分离,Sharding-JDBC
  25. Nginx概述
  26. Yapi
  27. swagger

数据库

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

  1. 员工表
  2. 菜品菜单分类表
  3. 菜品表
  4. 菜单表
  5. 套餐菜品关系表
  6. 菜品口味关系表
  7. 用户表
  8. 地址表
  9. 购物车表
  10. .订单表
  11. 订单明细表

流程:

  1. 创建实体类entity
  2. 创建controller
  3. 创建service 以及 impl
  4. 创建mapper
  5. 创建config配置类
  6. 创建common返回结果 R

静态资源不在static目录下,如何映射

编写web配置类,继承WebMvcConfigurationSupport 类

/**
 *   web配置类
 */
@Slf4j   //  输出日志
@Configuration   // 声明配置类
public class WebConfig extends WebMvcConfigurationSupport {

    /**
     * 设置静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("静态资源映射开始");
//        前端资源方式的请求  /backend/**
        registry.addResourceHandler("/backend/**")
//         前端资源所在的位置
        .addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }
}

结果类

/**
 * 通用返回结果类
 * @param <T>
 */
@Data
public class R<T> {

    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据  实体对象

    private Map map = new HashMap(); //动态数据

    //返回成功
    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    //失败
    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    //动态数据
     public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

登录

//Mapper
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {}
//service
public interface EmployeeService extends IService<Employee> {}
//ServiceImpl
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {}
//config
@Slf4j
@RestController   // @Controller + @ResponseBody(响应数据)
@RequestMapping("/employee")
public class EmployeeCpntroller {

    @Autowired
    EmployeeService employeeService;

    @PostMapping("/login")
    public R<Employee>  EmployeeLogin(@RequestBody Employee employee, HttpServletRequest request){
//        1.获取提交的密码,进行加密
        String password = employee.getPassword();
//          将密码转化为字体节数组继续加密后,赋值给password
        password = DigestUtils.md5DigestAsHex(password.getBytes());
//        2.根据用户进行查询,查看数据是否存在
        LambdaQueryWrapper<Employee> queryWrapper= new LambdaQueryWrapper<>();
//        Employee::getUsername(创建Employee对象获取username属性) = 前端传递的username
        queryWrapper.eq(Employee::getUsername,employee.getUsername());
//        根据查询结果封装为实体类对象
        Employee one = employeeService.getOne(queryWrapper);
//        3.判断当前username的返回结果
        if (one == null){
            return R.error("当前用户不存在");
        }
//        4.密码比对
        if (!one.getPassword().equals(password)){
            return R.error("密码错误,请重新输入");
        }
//        5.查看员工状态
        if (one.getStatus() == 0){
            return R.error("当前用户已被禁用");
        }
//        6.登录成功,将id保存到session中,返回登录成功结果
        HttpSession session = request.getSession();
        session.setAttribute("employee",one.getId());
        return R.success(one);
    }
    @RequestMapping("/logout")
//          退出不需要返回详细数据
    public R<String> logout(HttpServletRequest request ){
        HttpSession session = request.getSession();
//        退出操作,将session的employee信息删除
        session.removeAttribute("employee");
        return R.success("操作成功");
    }
}

使用过滤器拦截路径

/**
 * 配置过滤器
 */
//                       过滤器名称              过滤器拦截路径
@WebFilter(filterName = "LoginCheckFilter" , urlPatterns = "/*")
@Component
@Slf4j
public class LoginCheckFilter implements Filter {

//    路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//        向下转型
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        log.info("开始获取路径");
//        1.获取本次请求的URL
        String requestURI = request.getRequestURI();
//        2.判断请求是否处理    urls是放行路径   /employee/page是当前列表的查询请求
        String[] urls = new String[]{"/employee/login","/employee/page","/employee/logout","/backend/**","/front/**"};
        boolean check = check(urls, requestURI);
//        check  = true时 ,标识存在当前放行目录中,进行放行
        if (check){
            log.info("放行目录放行");  //放行路径
            filterChain.doFilter(request,response);  //放行
            return;
        }
        log.info("登录放行前置");
//        判断用户是否登录
        HttpSession session = request.getSession();
        Object employee = session.getAttribute("employee");
        log.info("登录放行中置"+requestURI);
        if (employee != null){
            log.info("登录放行路径");  //放行路径
            filterChain.doFilter(request,response);  //放行
            return;
        }
//        不放行,将R对象转为JSON,响应给前端处理
        log.info("放行跳转");
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }

    /**
     *   路径匹配,判断本次请求是否放行
     */
    public boolean check(String[] urls , String requestURL){
        for (String s:urls){
//            是否匹配
            boolean match = PATH_MATCHER.match(s, requestURL);
            if (match){
                return true;
            }
        }
        return false;
    }
}

指定异常处理器


/**
 *   指定异常处理   annotations(拦截哪个注解下的异常) = (RestController.class)
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 异常处理方法,处理sql异常
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        if (ex.getMessage().contains("Duplicate entry")){
            String[] s = ex.getMessage().split(" ");
            String name = s[2] + "已存在";
            return R.error(name);
        }
        return R.error("未知异常");
    }

}

分页查询

分页插件

/**
 *   分页插件
 */
@Configuration()
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //添加分页插件
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

}

使用mybatis-plus进行分页

    /*
     *   分页查询
     */
    @GetMapping("/page")        //   /employee/page?page=1&pageSize=10&name=123
    public R<Page> page(int page,int pageSize,String name){
        log.info("page = {} , pageSize = {} , name = {}",page,pageSize,name);
//        分页条件   查询的页码   每页数据
        Page employeePage = new Page(page, pageSize);
        LambdaQueryWrapper<Employee> queryWrapper= new LambdaQueryWrapper<>();
//        添加过滤条件   StringUtils.isNotEmpty()->判断当前属性是否为空,为空不执行      getUsername的值 ?=  name
        queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getUsername,name);
//        添加排序条件
        queryWrapper.orderByDesc(Employee::getUpdateTime);
//        将分页  和  查询条件传入进去
        employeeService.page(employeePage,queryWrapper);
        return R.success(employeePage);
    }

消息转换器

将json转为java 将java转为json

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于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_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(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .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);
    }
}

@Slf4j   //  输出日志
@Configuration   // 声明配置类
public class WebConfig extends WebMvcConfigurationSupport {
  /**
     *   扩展springmvc的消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //创建消息转换器
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用jackson将对象转化为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的转换器,追加到mvc的框架集合中,优先使用
        converters.add(0,messageConverter);
    }
}

修改->禁用

    /**
     *  修改
     */
    @PutMapping
    public R<String> BanEmployee(@RequestBody  Employee employee ,HttpServletRequest request){
        //前端已修改禁用状态
        Integer status = employee.getStatus();
        System.out.println("禁用状态为:"+status);
//        拿出登录时存储的当前用户id
        Long employee1 = (Long)request.getSession().getAttribute("employee");
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(employee1);
        employeeService.updateById(employee);
        return R.success("操作成功");
    }

分页

分页插件

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 *   分页插件
 */
@Configuration()
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //添加分页插件
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

}

    /*
     *   分页查询
     */
    @GetMapping("/page")        //   /employee/page?page=1&pageSize=10&name=123
    public R<Page> page(int page,int pageSize,String name){
        log.info("page = {} , pageSize = {} , name = {}",page,pageSize,name);
//        分页条件   查询的页码   每页数据
        Page employeePage = new Page(page, pageSize);
        LambdaQueryWrapper<Employee> queryWrapper= new LambdaQueryWrapper<>();
//        添加过滤条件   StringUtils.isNotEmpty()->判断当前属性是否为空,为空不执行      getUsername的值 ?=  name
        queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getUsername,name);
//        添加排序条件
        queryWrapper.orderByDesc(Employee::getUpdateTime);
//        将分页  和  查询条件传入进去
        employeeService.page(employeePage,queryWrapper);
        return R.success(employeePage);
    }

页面回显

    /**
     *  页面回显
     */
    @GetMapping("/{id}")
    public  R<Employee> ShowDate(@PathVariable Long id){
        log.info("当前id是:"+id);
        LambdaQueryWrapper<Employee> queryWrapper= new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getId,id);
        Employee one = employeeService.getOne(queryWrapper);
        return R.success(one);
    }

自动填充ThreadLocal

客户端每次发送的http请求,对应的服务器都会分配一个新的线程来处理,在处理请求时,多个类的多个方法均为同一个显示。->拦截器->执行update方法->自定义字段处理器 用的都是同一个线程

ThreadLocal:不是线程,而是线程的局部变量,使用ThreadLocal维护变量时,每一个ThreadLocal为当前变量的副本,每个线程都可以独立的改变副本,而不会影响其他线程对应的版本,ThreadLocal为每个线程提供单独的存储时间,具有线程隔离的效果,只有在线程内才可以取的到对应的值,线程外,不能访问该值.

1.加入注解

@TableField(fill = FieldFill.INSERT)    //  插入时填充字段
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE) //插入更新时,填充字段
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)  //  插入时填充字段
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)  //插入更新时,填充字段.
    private Long updateUser;

2.自定义对象处理器通过 ThreadLocal 获取当前操作对象的id值

/**
 *    自定义元数据对象处理器,将公共字段的值key
 */
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override   // 执行insert操作时,执行
    public void insertFill(MetaObject metaObject) {
        long id = Thread.currentThread().getId();
        log.info("自定义线程id为:"+id);
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("createUser",BaseContext.getCurrentId());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }

    @Override   // 执行更新操作时,执行
    public void updateFill(MetaObject metaObject) {
        long id = Thread.currentThread().getId();
        log.info("自定义线程id为:"+id);
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }
}

3.过滤器中,判断当前员工是否登录时,加入了 ThreadLocal 的set方法存储当前用户id的值

在这里插入图片描述4.自定义ThreadLocal

/**
 *  基于ThreadLocal的封装工具类,用户保存和获取当前登录Id
 */
public class BaseContext {

//    Long表示当前存储线程的字段属性
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     *  获取id,存储id
     * @param id
     */
    public static void serCurrentId(Long id){
        threadLocal.set(id);
    }

    /**
     *  取出存储id
     * @return
     */
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

新增用户

   @PostMapping
    public R<String>  Employee(@RequestBody Employee employee, HttpServletRequest request){
        String username = employee.getUsername();
//        2.根据用户进行查询,查看数据是否存在
        LambdaQueryWrapper<Employee> queryWrapper= new LambdaQueryWrapper<>();
//        Employee::getUsername(创建Employee对象获取username属性) = 前端传递的username
        queryWrapper.eq(Employee::getUsername,employee.getUsername());
        Employee user = employeeService.getOne(queryWrapper);
//        System.out.println(user);
        if (user != null){
            return R.error("当前用户名称已经存在");
        }
//        设置默认密码加密密码
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
//        获取当前session
        HttpSession session = request.getSession();
        Long empId =(Long) session.getAttribute("employee");
//        employee.setCreateUser(empId);
//        employee.setUpdateUser(empId);
        employeeService.save(employee);
        return R.success("操作成功");
    }

绑定的数据不能删除

查看当前分类id是否绑定了菜品和套餐,如果绑定了菜品和套餐抛出自定义异常,没有则删除当前分类

    /**
     *   根据id进行删除分类
     * 	@param id
     */
    @Override
    public void remove(Long id) {
        //        查询当前分类是否关联了菜品
        LambdaQueryWrapper<Dish> queryWrapper= new LambdaQueryWrapper<>();
        queryWrapper.eq(Dish::getCategoryId,id);
        int count = dishService.count(queryWrapper);
        if (count > 0){
            //  关联了菜品
            throw  new CustomException("当前分类已存在菜品,不可被删除");
        }
        //        查询当前分类是否关联了套餐
        LambdaQueryWrapper<Setmeal> qw = new LambdaQueryWrapper<>();
        qw.eq(Setmeal::getCategoryId,id);
        int count1 = setmealService.count(qw);
        if (count1 > 0){
            //  关联了菜品
            throw  new CustomException("当前分类已存在套餐,不可被删除");
        }
//        删除分类
        super.removeById(id);
    }

自定义异常

/**
 *   业务自定义异常
 */
public class CustomException extends RuntimeException {

    public CustomException(String message){
        super(message);
    }

}

指定自定义异常处理

/**
 *   指定异常处理   annotations(拦截哪个注解下的异常) = (RestController.class)
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 异常处理方法,处理sql异常
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        if (ex.getMessage().contains("Duplicate entry")){
            String[] s = ex.getMessage().split(" ");
            String name = s[2] + "已存在";
            return R.error(name);
        }
        return R.error("未知异常");
    }

    /**
     *  自定义异常
     * @param ex
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public R<String> exceptionHandlerone(CustomException ex){
//        抛出异常提示
        return R.error(ex.getMessage());
    }

}

上传

1.获取当前文件名称,根据文件名称获取文件后缀,使用uuid生成新的文件名称
2.判断当前目录是否已存在
3.输出文件到指定位置
在这里插入图片描述``

 @PostMapping("/upload")  // 文件上传
    public R<String> upload(MultipartFile file){
        // 获取原始文件名称
        String fileName = file.getOriginalFilename();
        // 随机生成文件名称,uuid       获取文件后缀名
        String hzName = fileName.substring(fileName.lastIndexOf("."));
        //  使用uuid  +  后缀名称
        fileName = UUID.randomUUID().toString() + hzName;
        //  判断当前目录是否存在,不存在则创建
        File directory = new File(basePath);
        if (!directory.exists()){
            directory.mkdirs();
        }
        try {
//            指定下载图片的位置
            file.transferTo(new File(basePath+fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }

下载

1.输入流 获取当前文件所在的路径
2.创建输出流,用于响应文件
3.输入流读取文件,输出流写文件
4.刷新并关闭流

    @GetMapping("/download")
    public void download(String name, HttpServletResponse response){
        try {
//            获取输入流,读取当前上传图片的内容
            FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
//            输出流,用于读取当前下载文件
            ServletOutputStream outputStream = response.getOutputStream();
//            上传的文件格式
            response.setContentType("image/jpeg");
//            读取输出流内容至输出流
            int len= 0;
            byte[] bytes = new byte[1024];
            while ((len = fileInputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
//                刷新
                outputStream.flush();
            }
//              关闭流
            outputStream.close();
            fileInputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

不在同一个表时

一般用于前端传递的值与封装的对象不统一,业务需要多表处理,简化操作,把两个表的属性写在一起
在这里插入图片描述
在这里插入图片描述

响应的字段不在同一个类中 响应数据 分页

1.创建关联表对象DTO,关联表中为响应的全部字段
2.创建响应类的原对象,进行数据的条件查询
在这里插入图片描述
3.dto拷贝源对象,排除原对象缺少的字段 (list)
4.源对象获取缺少的字段(list),遍历该对象,拿出对象的值赋值给dto对象,根据拿出的对象id进行分类表格的查询,查询出获取分类表的name值,赋值给dto对象,再将新的list集合,排除拷贝的对象赋值给Dto对象
在这里插入图片描述

    @GetMapping("/page")
    public R<Page> pageDish(int page,int pageSize,String name){
        Page<Dish> pg = new Page<>(page,pageSize);
        Page<DishDto> pgDto = new Page<>();  //获取DishDto对象,里面有dish类中缺少的页面显示字段
        LambdaQueryWrapper<Dish> queryWrapper= new LambdaQueryWrapper<>();
//        添加排序条件
        queryWrapper.like(name!=null,Dish::getName,name);
        queryWrapper.orderByDesc(Dish::getUpdateTime);
        dishService.page(pg,queryWrapper);

        //拷贝对象       records是页面查出的缺少数据集合   要排除掉
        BeanUtils.copyProperties(pg,pgDto,"records");
        //获取列表排除的字段   records  给其添加字段
        List<Dish> records = pg.getRecords();
        // 得到records集合的原对象,map方法取出对象,
        List<DishDto> list = records.stream().map((item)->{
            DishDto dishDto = new DishDto();//创建dto对象
            BeanUtils.copyProperties(item,dishDto);//将取出的对象拷贝到dto对象
            Long categoryId = item.getCategoryId(); //获取分类id
            Category byId = categoryService.getById(categoryId);//根据id查询出该条分类数据
            String name1 = byId.getName();//获取当前对象的name字段值
            dishDto.setCategoryName(name1);//查询出的name字段值通过dto的set方法进行赋值
            return dishDto;
        }).collect(Collectors.toList());  //创建新的list集合
        pgDto.setRecords(list);      //将新的list集合,赋值给当前排除的集合的对象
        return R.success(pg);
    }

新增 修改 存在不同表的数据

新增:
1.先新增菜品,获取菜品id
2.获取dto对象的getFlavors(为list)属性,属性封装了口味表
3.item遍历list,获取菜品id,储存到口味表中
4.将dto封装的口味表list新增通过saveBatch方法新增

    /**
     * 新增
     * @param dishDto
     */
    @Override
    public void saveWithFlaow(DishDto dishDto) {
        //1.新增菜品表, 获取id
        this.save(dishDto);
        Long Id = dishDto.getId();
        //2.获取dto对象的getFlavors()属性,属性封装了口味表
        List<DishFlavor> flavors = dishDto.getFlavors();
        //3.item遍历list,获取菜品id,储存到口味表中
        flavors = flavors.stream().map((item) -> {
            item.setDishId(Id);
            return item;
        }).collect(Collectors.toList());
        //4.再将dto封装的口味表list新增
        dishFlavorService.saveBatch(flavors);
    }

修改:
1.新增菜品
2.将口味表删除
3.获取当前Dta的请求参数list值
4.遍历list,得到的对象获取菜品表id
5.新增口味表

    /**
     *
     * @param dishDto    修改菜品
     */
    @Override
    public void UpdateDish(DishDto dishDto) {
        //1.新增菜品
        this.updateById(dishDto);

        //2.删除口味表
        LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
        dishFlavorService.remove(lambdaQueryWrapper);
        //3.获取flavors属性 -> 为口味表
        List<DishFlavor> flavors = dishDto.getFlavors();
        //4.item遍历当前口味集合,获取菜品的id,口味表进行赋值存储
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());
        //5.完成口味表新增
        dishFlavorService.saveBatch(flavors);
    }

删除

1.批量删除时,传递list值
2.queryWrapper使用in,查询出删除的全部数据

    /**
     *  删除
     * @param ids
     */
    @Override
    public void DeleteDisable(List<Long> ids) {
        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Setmeal::getId,ids); // 查询所有ids的数据
        queryWrapper.eq(Setmeal::getStatus,1); //查询ids数据中是否有在售的
        int count = this.count(queryWrapper);
        if (count > 0){   // 大于0代表有在售套餐
            throw new CustomException("套餐正在售卖,不可删除");
        }
        this.removeByIds(ids);  //否则批量删除所有ids的数据
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);
        setmealDishesService.remove(lambdaQueryWrapper);
    }

用户下单

1.从ThreadLocal获取当前用户的id
2.根据用户id,查询当前用户添加到购物车中的数据
3.根据当前用户id查询,地址数据
4.遍历当前购物车的数据,订单详情表通过set的方式进行复制
5.通过set的方式赋值订单表
6.像订单表插入输入,向订单明细表插入数据,删除购物车数据

    /*
     *   用户下单
     * @param orders
     */
    @Override
    @Transactional
    public void Submit(Orders orders) {
        // 1.获取用户id
        Long currentId = BaseContext.getCurrentId();
        // 2.根据用户,获取当前购物车数据
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId,currentId);
        List<ShoppingCart> list = shoppingCartService.list(lambdaQueryWrapper);
        if (list == null || list.size() == 0){
            throw new CustomException("购物车不能为空");
        }
        // 查询用户数据 、 地址数据
        User byId = userService.getById(currentId);
        Long addressBookId = orders.getAddressBookId();
        AddressBook addressBook = addressBookService.getById(addressBookId);
        if (addressBook == null){
            throw new CustomException("地址不能为空");
        }
        // 3.订单表1条
        //  生成订单号
        long orderId = IdWorker.getId();
        // 计算总金额,遍历购物车数据
        AtomicInteger amount = new AtomicInteger(); //保证多线程,保证数据正确
        List<OrderDetail> orderDetails = list.stream().map((item)->{
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setNumber(item.getNumber());
            orderDetail.setDishFlavor(item.getDishFlavor());
            orderDetail.setDishId(item.getDishId());
            orderDetail.setSetmealId(item.getSetmealId());
            orderDetail.setName(item.getName());
            orderDetail.setImage(item.getImage());
            orderDetail.setAmount(item.getAmount());
            amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
            return orderDetail;
        }).collect(Collectors.toList());

        orders.setNumber(String.valueOf(orderId));
        orders.setId(orderId);
        orders.setOrderTime(LocalDateTime.now());
        orders.setCheckoutTime(LocalDateTime.now());
        orders.setStatus(2);
        orders.setAmount(new BigDecimal(amount.get()));//总金额
        orders.setUserId(currentId);
        orders.setNumber(String.valueOf(orderId));
        orders.setUserName(byId.getName());
        orders.setConsignee(addressBook.getConsignee());
        orders.setPhone(addressBook.getPhone());
        orders.setAddress((addressBook.getProvinceName() == null ? "" : addressBook.getProvinceName())
                + (addressBook.getCityName() == null ? "" : addressBook.getCityName())
                + (addressBook.getDistrictName() == null ? "" : addressBook.getDistrictName())
                + (addressBook.getDetail() == null ? "" : addressBook.getDetail()));
        //向订单表插入数据,一条数据
        this.save(orders);
        // 4.订单明细表插入数据
        orderDetailServicel.saveBatch(orderDetails);
        // 5.下单完成后,清空购物车数据
        shoppingCartService.remove(lambdaQueryWrapper);
    }

redis 优化

spring:
  redis:
    host: localhost
    port: 6379
    database: 0
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

序列化器

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        //默认的Key序列化器为:JdkSerializationRedisSerializer
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

1.查询数据,保存redis里面

  // 1.先从redis里面获取数据
/**
     *   查询套餐
     * @return
     */
    @GetMapping("/list")
    public R<List<DishDto>> QueryDishCategoryId(Dish dish){
        List<DishDto> listDto = null;
        String key = "dish_" + dish.getCategoryId() +"_" +dish.getStatus();
        // 1.先从redis里面获取数据
        listDto = (List<DishDto>)redisTemplate.opsForValue().get(key);
        if (listDto != null){
           return R.success(listDto);
        }
 			 // 2.没有的话查询数据库
			//3. 如果不存在,查询数据库,把查询到的菜品缓存到redis里
       	    // key = dish_当前套餐的id_1    listDao是查询出的套餐结果
        redisTemplate.opsForValue().set(key,listDto,60, TimeUnit.MINUTES);
        return R.success(listDto);

2.修改/删除/更新后,删除当前redis

    @PutMapping
    public R<String> UpdateDish(@RequestBody DishDto dishDto){
        dishService.UpdateDish(dishDto);
        // 清理指定缓存
        String key = "dish_" + dishDto.getCategoryId() +"_1";
        redisTemplate.delete(key);
        return R.success("操作成功");
    }

SpringCache简化开发

注解:
EnableCaching 开启缓存注解功能,一般放在启动类上
使用map的方式


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

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserService userService;

    /**
     *       CachePut将方法的返回值放入缓存
     *       value:缓存的名称
     *       key :缓存的key,存储的值为 user对象
     */
    @CachePut(value = "userCache" , key ="#user.id" )
    @PostMapping
    public User save(User user){
        userService.save(user);
        return user;
    }

    /**
     *  清理指定缓存根据value和key,删除缓存
     */
    @CacheEvict(value = "userCache" , key = "#id")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }


    /**
     *  更新数据时,删除缓存数据
     */
    @CacheEvict(value = "userCache" , key = "#user.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

    /*
     *  在执行方法时,先查看cacheManager里有没有缓存的数据,如果有的话,注解返回数据,如果没有的话,查询数据
     *  condition->条件,满足条件时,才缓存数据
     */
    @Cacheable(value = "userCache" , key = "#id" , condition = "#result != null" )
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

    /**
     *  在执行方法时,先查看cacheManager里有没有缓存的数据,如果有的话,注解返回数据,如果没有的话,查询数据
     */
    @Cacheable(value = "userCache" , key = "#user.id+'_'+#user.name" , condition = "#result != null" )
    @GetMapping("/list")
    public List<User> list(User user){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getId() != null,User::getId,user.getId());
        queryWrapper.eq(user.getName() != null,User::getName,user.getName());
        List<User> list = userService.list(queryWrapper);
        return list;
    }
}

开发的方式:

1.启动类+ @EnableCaching 开启缓存注解功能
2.配置文件

spring:
  cache:
    redis:
      time-to-live: 1800000  #30分钟到期
  redis:
    host: localhost
    port: 6379
    username: root
    database: 0

3.jar包

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
		

使用:
1.当查询时,有缓存的时候,直接走缓存,如果没有匹配到缓存的时候,那么查询数据库后,再将结果加入缓存中

    /**
     *  查询套餐
     */
    @GetMapping("/list")
    @Cacheable(value = "setSetmea" , key = "#dish.categoryId")
    // 设置名称为setSetmea,key为#dish.categoryId, value为返回值的缓存数据
    public R<List<Setmeal>> QuerySetmea(Setmeal dish){
        LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(dish.getCategoryId()!=null,Setmeal::getCategoryId,dish.getCategoryId());
        lambdaQueryWrapper.eq(dish.getStatus()!=null,Setmeal::getStatus,dish.getStatus());
        List<Setmeal> list = setmealService.list(lambdaQueryWrapper);
        return R.success(list);
    }

2.当使用删除/新增/修改的方法时,会直接删除改缓存

    /**
     *  删除菜单
     * @return
     */
    @CacheEvict(value = "setSetmea" , allEntries = true)
    // 删除名称为setSetmea的全部数据
    @DeleteMapping
    public R<String> DeleteDisable(@RequestParam List<Long> ids){
        setmealService.DeleteDisable(ids);
        return R.success("删除成功");
    }

其他方法

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

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserService userService;

    /**
     *       CachePut将方法的返回值放入缓存
     *       value:缓存的名称
     *       key :缓存的key,存储的值为 user对象
     */
    @CachePut(value = "userCache" , key ="#user.id" )
    @PostMapping
    public User save(User user){
        userService.save(user);
        return user;
    }

    /**
     *  清理指定缓存根据value和key,删除缓存
     */
    @CacheEvict(value = "userCache" , key = "#id")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }


    /**
     *  更新数据时,删除缓存数据
     */
    @CacheEvict(value = "userCache" , key = "#user.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

    /*
     *  在执行方法时,先查看cacheManager里有没有缓存的数据,如果有的话,注解返回数据,如果没有的话,
     *  查询数据后,将方法的返回值放到缓存中
     *  condition->条件,满足条件时,才缓存数据
     *  unless -> 条件满足时,不缓存
     */
    @Cacheable(value = "userCache" , key = "#id" , unless = "#result == null" )
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

    /**
     *  在执行方法时,先查看cacheManager里有没有缓存的数据,如果有的话,注解返回数据,如果没有的话,
     *  查询数据后,将方法的返回值放到缓存中
     */
    @Cacheable(value = "userCache" , key = "#user.id+'_'+#user.name" , condition = "#result != null" )
    @GetMapping("/list")
    public List<User> list(User user){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getId() != null,User::getId,user.getId());
        queryWrapper.eq(user.getName() != null,User::getName,user.getName());
        List<User> list = userService.list(queryWrapper);
        return list;
    }


}

读写分离、主从复制

对同一时间段有大量并发,将数据库拆分为主库和从库,主库负责增删改操作,从库负责查询操作,能够有效的避免由数据更新导致的行锁,使整个系统的查询性能得到极大的改善

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>

server:
  port: 8080
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: ASSIGN_ID
spring:
  shardingsphere:
    datasource:
      names:
        master,slave
      # 主数据源
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.138.100:3306/rw?characterEncoding=utf-8
        username: root
        password: root
      # 从数据源
      slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.138.101:3306/rw?characterEncoding=utf-8
        username: root
        password: root
    masterslave:
      # 读写分离配置
      load-balance-algorithm-type: round_robin #轮询
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: master    
      # 从库数据源名称列表,多个逗号分隔
      slave-data-source-names: slave
    props:
      sql:
        show: true #开启SQL显示,默认false
  main:
    allow-bean-definition-overriding: true

Nginx

nginx是一款轻量级别的web服务器/反向代理服务器。内存少,并发能力强。
Linux安装
1.yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
2.wget 看看是否下载 yum install wget 下载
3. wget http://nginx.org/download/nginx-1.16.1.tar.gz 下载
4. tar -zxvf nginx-1.16.1.tar.gz 解压
5. cd nginx-1.16.1 mkdir /usr/local/nginx
./configure --prefix=/usr/local/nginx 安装到哪里去
6.make && make install; 安装

nginx 常用命令

  1. ./nginx -v 查看版本
  2. ./nginx -t 查看配置文件是否错误
  3. ./nginx 启动nginx
  4. /usr/local/nginx/sbin/nginx(地址) -s stop 关闭nginx
  5. log里面 cat nginx.pid 当前进程id
  6. nginx -s reload 重新加载
    vim /etc/profile 里面将Nginx的地址输入到里面去可以不用输入地址就可以使用了(PATH=/usr/local/nginx/sbin: J A V A H O M E / b i n : JAVA_HOME/bin: JAVAHOME/bin:PATH)

全局块
和nginx运行相关的全局配置
events块
和网络连接相关的网络配置
http块
代理、缓存、日志记录
http全局快:
servere模:server全局块 / location 块

1.部署静态资源/vim nginx.conf 将静态资源放到html目录下,即可访问
在这里插入图片描述

2.反向代理
服务端进行设置,为服务器提供服务。
用户访问反向代理服务器,反向代理将用户发送的请求转到到指定服务上
在这里插入图片描述

3.负载均衡
客户端发送请求至负载均衡服务器,由负载均衡服务器进行web服务器的分发操作。
在这里插入图片描述
在这里插入图片描述

yapi

介绍:
Api管理平台

Swagger

生成接口文档

  1. 导入maven坐标
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-jfinal</artifactId>
            <version>3.0.2</version>
        </dependency>
  1. 导入相关配置
    在这里插入图片描述3.设置静态资源映射
 registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
 registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

在这里插入图片描述
4.配置拦截器的放行请求
在这里插入图片描述

可以直接查看接口
在这里插入图片描述
常用注解
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java设计模式之策略模式

一&#xff1a;策略模式 1.什么是策略模式? 模板方法模式是一种行为设计模式&#xff0c; 它在超类中定义了一个算法的框架&#xff0c; 允许子类在不修改结构的情况下重写算法的特定步骤。 策略模式的基本介绍 1.策略模式&#xff08;Strategy Pattern&#xff09;中&#x…

【MQ基本概念 MQ的工作原理】

一、MQ基本概念 1、MQ概述 MQ全称Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存 消息的容器。多用于分布式系统之间进 行通信。 小结 MQ&#xff0c;消息 队列&#xff0c;存储消息的中间件 分布式系统通信两种方式&#xff1a;直接远程…

2022年数维杯D题 极端天气问题思路指导

D题损失评估与应对策略的研究三重拉尼娜事件下的极端气候灾害 很明显D题是一个数据收集➕数据处理类型题目&#xff0c;这与美赛中的E题题型相似。该题所涉及的极端天气与2021年小美赛极端天气题目高度相似。因此&#xff0c;我们首先对大家整理了去年小美赛极端天气的相关论文…

Kubeadm搭建kubernetes集群

Kubeadm搭建kubernetes集群 环境说明 | 角色 | ip | 操作系统 |组件 | | – | – | – | | master | 192.168.226.10 |centos8 | docker&#xff0c;kubectl&#xff0c;kubeadm&#xff0c;kubelet | | node1 | 192.168.226.20 |centos8 |docker&#xff0c;kubectl&#xff…

登陆拦截案例

登陆拦截案例 登陆拦截器小案例&#xff0c;判断登陆的用户名及密码是否正确&#xff1b;&#xff1a; 1.创建一个maven项目&#xff0c;导入相关的坐标&#xff1a; <dependencies><dependency><groupId>org.springframework</groupId><artifact…

【Hack The Box】windows练习-- Blackfield

HTB 学习笔记 【Hack The Box】windows练习-- Blackfield &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月17日&#x1f334; &…

[Games 101] Lecture 10 Geometry 1 (Introduction)

Geometry 1 (Introduction) Ways to Represent Geometry 隐式 (Implicit) 几何 只告诉点满足某种约束或关系&#xff0c;并不给出实际的点&#xff0c;也就是说&#xff0c;定义 f(x,y,z)0f(x,y,z) 0 f(x,y,z)0 例如&#xff0c;定义三维空间中的点&#xff0c;满足&#…

上海亚商投顾:信创概念掀涨停潮

上海亚商投顾前言&#xff1a;无惧大盘大跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪指数早盘低开低走&#xff0c;沪指一度跌超1%&#xff0c;失守3100点关口&#xff0c;创业板指盘中跌逾2%&#xff…

软件工程毕业设计 SSM计算机毕设项目合集【源码+论文】

文章目录前言 题目1 : 基于SSM的网上租车系统 <br /> 题目2 : 基于SSM的药品管理系统 <br /> 题目3 : 基于SSM的药源药品商城保健品购物网站 <br /> 题目4 : 基于SSM的疫情防控物业管理系统 <br /> 题目5 : 基于SSM的音乐网站 <br />前言 &…

如何通过更好的文档管理减轻小型企业的压力

如何通过更好的文档管理减轻小型企业的压力 企业如何处理企业文档在很大程度上体现了企业以目前的形式茁壮成长的能力以及在当今的市场中成长为成熟的、有竞争力的实体的能力。 具体来说&#xff0c;在小型企业中&#xff0c;许多员工都需要承担多种职责&#xff0c;每一分钟…

代码随想录58——单调栈:739每日温度、 496下一个更大元素I

文章目录1.739每日温度1.1.题目1.2.解答1.2.1.单调栈使用情况1.2.2.本题解答2. 496下一个更大元素I2.1.题目2.2.解答1.739每日温度 参考&#xff1a;代码随想录&#xff0c;739每日温度&#xff1b;力扣题目链接 1.1.题目 1.2.解答 1.2.1.单调栈使用情况 首先想到的当然是暴…

通过STM32Cube配置完成基于I2C协议的AHT20温湿度传感器的数据采集

文章目录前言一、I2C协议1、应用2、组成3、软件I2C和硬件I2C3.1软件I2C3.2硬件I2C二、通过硬件I2C协议采集AHT20的数据1、配置项目2、配置代码三、效果四、总结五、参考资料前言 硬件&#xff1a;stm32f103c8t6 核心板软件&#xff1a;STM32CubeMX 6.4.0软件&#xff1a;keil5…

java常见集合框架的区别

1.说说有哪些常见集合&#xff1f; 集合相关类和接口都在java.util中&#xff0c;主要分为3种&#xff1a;List&#xff08;列表&#xff09;、Map&#xff08;映射&#xff09;、Set(集)。 Java集合主要关系 其中Collection是集合List、Set的父接口&#xff0c;它主要有两个子…

澳大利亚博士后招聘|国立大学—太阳能电池方向

【国外博士后招聘-知识人网】澳大利亚国立大学博士后—太阳能电池方向 澳大利亚国立大学&#xff08;The Australian National University&#xff09;&#xff0c;简称ANU&#xff0c;始建于1946年&#xff0c;坐落于澳大利亚首都堪培拉&#xff0c;是公立研究型综合类大学&am…

Ernie-SimCSE对比学习在内容反作弊上应用

作者 | ANTI 导读 AI技术在不同行业和业务被广泛的应用&#xff0c;本文介绍了反作弊团队在与spammer对抗愈演愈烈的趋势下&#xff0c;不断探索前沿技术&#xff0c;将百度NLP预训练模型结合对比学习用于解决spam内容中知道提问群发推广作弊的技术方案。 本次分享&#xff0c;…

从单车智能到车路协同,均胜电子正在加快智能驾驶商业化进程

进入2022年&#xff0c;自动驾驶迈入了商业化的关键期&#xff0c;但市场分层也开始越来越明显。 一方面&#xff0c;L2级及以上智能辅助驾驶的搭载量在不断攀升&#xff0c;未来将成为量产车的标准配置。根据《高工智能汽车研究院》数据显示&#xff0c;今年1-9月前装标配搭载…

基于python的停车场管理系统的设计与实现/智能停车管理系统

摘要 车位信息是停车场供应用户必不可少的一个部分。在停车场发展的整个过程中&#xff0c;车位信息担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类系统管理程序也在不断改进。本课题所设计的停车场管理系统&#xff0c;使用Django框架&#xff0c;Python语…

Linux学习-37-查看文件系统硬盘信息(df、du命令)

10.2 df用法详解&#xff1a;查看文件系统硬盘使用情况 Linux 磁盘管理好坏直接关系到整个系统的性能问题。df &#xff08;disk free&#xff09;命令&#xff0c;检查文件系统的磁盘空间占用情况&#xff0c;包括文件系统所在硬盘分区的总容量、已使用的容量、剩余容量等。 …

[附源码]Python计算机毕业设计bugbase管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Div3 cf1741

Cf1741 文章目录A. Compare T-Shirt SizesB. Funny Permutation&#xff08;思维&#xff09;C. Minimize the Thickness&#xff08;&#xff09;A. Compare T-Shirt Sizes 题意: 第一行&#xff0c;输入测试样例个数n&#xff0c;接下来n行输入 &#xff0c;输入a和b代表衣服…