个人博客项目笔记_07

news2024/11/25 4:49:12

写文章

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

写文章需要 三个接口:

  1. 获取所有文章类别

  2. 获取所有标签

  3. 发布文章

1. 所有文章分类

1.1 接口说明

接口url:/categorys

请求方式:GET

请求参数:

参数名称参数类型说明

返回数据:

{
    "success":true,
 	"code":200,
    "msg":"success",
    "data":
    [
        {"id":1,"avatar":"/category/front.png","categoryName":"前端"},	
        {"id":2,"avatar":"/category/back.png","categoryName":"后端"},
        {"id":3,"avatar":"/category/lift.jpg","categoryName":"生活"},
        {"id":4,"avatar":"/category/database.png","categoryName":"数据库"},
        {"id":5,"avatar":"/category/language.png","categoryName":"编程语言"}
    ]
}

1.2 Controller

package com.cherriesovo.blog.controller;

import com.cherriesovo.blog.service.CategoryService;
import com.cherriesovo.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("categorys")
public class CategoryController {

    @Autowired
    private CategoryService categoryService;

    @GetMapping
    public Result listCategory() {
        return categoryService.findAll();
    }
}

1.3 Service

public interface CategoryService {
    Result findAll();   //类别查询
}

@Service
public class CategoryServiceImpl implements CategoryService {
    public CategoryVo copy(Category category){
        CategoryVo categoryVo = new CategoryVo();
        BeanUtils.copyProperties(category,categoryVo);
        return categoryVo;
    }
    public List<CategoryVo> copyList(List<Category> categoryList){
        List<CategoryVo> categoryVoList = new ArrayList<>();
        for (Category category : categoryList) {
            categoryVoList.add(copy(category));
        }
        return categoryVoList;
    }
    @Override
    public Result findAll() {
        //SELECT * FROM category;
        List<Category> categories = this.categoryMapper.selectList(new LambdaQueryWrapper<>());
        return Result.success(copyList(categories));
    }
}

2. 所有文章标签

2.1 接口说明

接口url:/tags

请求方式:GET

请求参数:

参数名称参数类型说明

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 5,
            "tagName": "springboot"
        },
        {
            "id": 6,
            "tagName": "spring"
        },
        {
            "id": 7,
            "tagName": "springmvc"
        },
        {
            "id": 8,
            "tagName": "11"
        }
    ]
}

2.2 Controller

@RestController
@RequestMapping("tags")
public class TagsController {

    @Autowired
    private TagService tagsService;
    
    @GetMapping
    public Result findAll(){
        return tagsService.findAll();
    }

}

2.3 Service

public interface TagService {
    Result findAll();   //查询所有的文章标签
}

TagServiceImpl:

	@Override
    public Result findAll() {
        //SELECT * FROM tag;
        List<Tag> tags = this.tagMapper.selectList(new LambdaQueryWrapper<>());
        return Result.success(copyList(tags));
    }

3. 发布文章

3.1 接口说明

接口url:/articles/publish

请求方式:POST

请求参数:

参数名称参数类型说明
titlestring文章标题
idlong文章id(编辑有值)
bodyobject({content: “ww”, contentHtml: “

ww

↵”})
文章内容
category{id: 2, avatar: “/category/back.png”, categoryName: “后端”}文章类别
summarystring文章概述
tags[{id: 5}, {id: 6}]文章标签

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": {"id":12232323}
}

3.2 Controller

package com.cherriesovo.blog.vo.params;

import com.cherriesovo.blog.vo.CategoryVo;
import com.cherriesovo.blog.vo.TagVo;
import lombok.Data;

import java.util.List;

@Data
public class ArticleParam {

    private Long id;

    private ArticleBodyParam body;

    private CategoryVo category;

    private String summary;

    private List<TagVo> tags;

    private String title;
}
package com.cherriesovo.blog.vo.params;

import lombok.Data;

@Data
public class ArticleBodyParam {

    private String content;

    private String contentHtml;

}
//json数据进行交互
@RestController
@RequestMapping("articles")
public class ArticleController {
    /*
    * 发布文章
    * */
    @PostMapping("publish")
    public Result publish(@RequestBody ArticleParam articleParam){
        return articleService.publish(articleParam);
    }
}

3.3 Service

在这里插入图片描述

public interface ArticleService {
    //文章发布
    Result publish(ArticleParam articleParam);
}

ArticleServiceImpl:

ArticleServiceImpl共需要经历如下步骤:

  1. 创建一个 Article 对象,并设置其属性,最后将文章对象插入到数据库中。

    		Article article = new Article();
            article.setAuthorId(sysUser.getId());
            article.setCategoryId(articleParam.getCategory().getId());
            article.setCreateDate(System.currentTimeMillis());
            article.setCommentCounts(0);
            article.setSummary(articleParam.getSummary());	//摘要
            article.setTitle(articleParam.getTitle());
            article.setViewCounts(0);
            article.setWeight(Article.Article_Common);
            //设置了文章的 bodyId 属性为 -1L。通常情况下,-1L 通常被用作一个特殊的标记,表示某个值无效或未设置
            article.setBodyId(-1L);	//内容id
            //插入之后会自动生成一个文章id
            this.articleMapper.insert(article);
    
  2. 将文章id与标签id进行关联——获取文章的标签列表,遍历标签列表,对每个标签执行以下操作:

    1. 创建一个 ArticleTag 对象,并设置其文章ID和标签ID。
    2. 将 ArticleTag 对象插入到数据库中(article_tag表)。
    List<TagVo> tags = articleParam.getTags();
            if (tags != null) {
                for (TagVo tag : tags) {
                    ArticleTag articleTag = new ArticleTag();
                    articleTag.setArticleId(article.getId());
                    articleTag.setTagId(tag.getId());
                    this.articleTagMapper.insert(articleTag);
                }
            }
    
  3. 文章内容存储(article_body表)

    		ArticleBody articleBody = new ArticleBody();
            articleBody.setContent(articleParam.getBody().getContent());
            articleBody.setContentHtml(articleParam.getBody().getContentHtml());
            articleBody.setArticleId(article.getId());
            articleBodyMapper.insert(articleBody);
    
  4. 更新article表中的body属性

    		article.setBodyId(articleBody.getId());
            articleMapper.updateById(article);
    
  5. 设置返回值

    		ArticleVo articleVo = new ArticleVo();
            articleVo.setId(article.getId());
            return Result.success(articleVo);
    
@Override
    @Transactional
    public Result publish(ArticleParam articleParam) {
        /*
        * 1、发布文章目的是构建article对象
        * 2、作者id——当前的登录用户
        * 3、要将标签加入到关联列表
        * 4、body 内容存储  要的是bodyId
        * */
        //此接口要加入到登录拦截中,否则会造成空指针异常
        SysUser sysUser = UserThreadLocal.get();

        Article article = new Article();
        article.setAuthorId(sysUser.getId());
        article.setCategoryId(articleParam.getCategory().getId());
        article.setCreateDate(System.currentTimeMillis());
        article.setCommentCounts(0);
        article.setSummary(articleParam.getSummary());	//摘要
        article.setTitle(articleParam.getTitle());
        article.setViewCounts(0);
        article.setWeight(Article.Article_Common);
        //设置了文章的 bodyId 属性为 -1L。通常情况下,-1L 通常被用作一个特殊的标记,表示某个值无效或未设置
        article.setBodyId(-1L);	//内容id
        //插入之后会自动生成一个文章id
        this.articleMapper.insert(article);

        List<TagVo> tags = articleParam.getTags();
        if (tags != null) {
            for (TagVo tag : tags) {
                ArticleTag articleTag = new ArticleTag();
                articleTag.setArticleId(article.getId());
                articleTag.setTagId(tag.getId());
                this.articleTagMapper.insert(articleTag);
            }
        }
        //body内容存储(article_body表)
        ArticleBody articleBody = new ArticleBody();
        articleBody.setContent(articleParam.getBody().getContent());
        articleBody.setContentHtml(articleParam.getBody().getContentHtml());
        articleBody.setArticleId(article.getId());
        articleBodyMapper.insert(articleBody);

        //更新article表中的body
        article.setBodyId(articleBody.getId());
        articleMapper.updateById(article);
        //设置返回值
        ArticleVo articleVo = new ArticleVo();
        articleVo.setId(article.getId());
        return Result.success(articleVo);
    }
package com.cherriesovo.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.ArticleTag;

public interface ArticleTagMapper  extends BaseMapper<ArticleTag> {
}
package com.cherriesovo.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.ArticleBody;

public interface ArticleBodyMapper extends BaseMapper<ArticleBody> {
}
package com.cherriesovo.blog.vo;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.util.List;

@Data
public class ArticleVo {
    //一定要记得加 要不然 会出现精度损失
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

    private String title;

    private String summary;

    private Integer commentCounts;

    private Integer viewCounts;

    private Integer weight;
    /**
     * 创建时间
     */
    private String createDate;

    private String author;

    private ArticleBodyVo body;

    private List<TagVo> tags;

    private CategoryVo category;

}
package com.cherriesovo.blog.dao.pojo;

import lombok.Data;

@Data
public class ArticleTag {

    private Long id;

    private Long articleId;

    private Long tagId;
}

当然登录拦截器中,需要加入发布文章的配置:

WebMVCConfig:

 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截test接口,后续实际遇到需要拦截的接口时,在配置为真正的拦截接口
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/test")
                .addPathPatterns("/comments/create/change")
                .addPathPatterns("/articles/publish");
    }

3.4 测试

4. AOP日志

定义一个自定义注解 LogAnnotation,用于在方法上添加日志相关的注解信息:

  • @Target(ElementType.METHOD):这个注解指定了 LogAnnotation 注解可以被应用于方法上。
  • @Retention(RetentionPolicy.RUNTIME):这个注解指定了 LogAnnotation 注解在运行时可见。
  • @Documented:这个注解指定了 LogAnnotation 注解将被包含在 Javadoc 中。
  • String module() default "";:这个注解定义了一个 module 属性,用于指定日志的模块,默认值为空字符串。
  • String operator() default "";:这个注解定义了一个 operator 属性,用于指定执行操作的操作者,默认值为空字符串。

这个自定义注解可以用于方法上,用于标记需要记录日志的方法,并且可以通过 moduleoperator 属性指定日志的模块和操作者。

Javadoc 是 Java 语言中用于生成 API 文档的工具。它能够根据源代码中的特定标记,自动生成与代码相关的文档。Javadoc 工具会扫描 Java 源代码中特定格式的注释,并根据这些注释生成 HTML 格式的 API 文档。这些注释通常以 /** 开头,以 */ 结尾,位于类、方法、字段等代码元素的前面。Javadoc 工具会解析这些注释中的标签和内容,并生成易于阅读和导航的 API 文档。)

package com.cherriesovo.blog.common.aop;

import java.lang.annotation.*;

/**
 * 日志注解
 */
//TYPE代表可以放在类上面,METHOD代表可以放在方法上
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    String module() default "";

    String operator() default "";
}

LogAspect是一个使用了 Spring AOP的日志切面类:

  • @Aspect:这个注解标识了这个类是一个切面类,用于定义通知和切点的关系。

  • @Pointcut("@annotation(com.cherriesovo.blog.common.aop.LogAnnotation)"):这个注解定义了一个切点 logPointCut(),它表示当目标方法上存在 com.cherriesovo.blog.common.aop.LogAnnotation 注解时,这个切点会匹配到。

  • public void logPointCut() { }:这个方法定义了切点的具体内容,但方法体为空,因为它只是用于标识切点,实际的逻辑在通知方法中实现。

  • @Around("logPointCut()"):这个注解表示环绕通知,它表示在目标方法执行前后都会执行通知逻辑。

  • public Object around(ProceedingJoinPoint point) throws Throwable { }:这个方法是环绕通知的具体实现。在目标方法执行前记录开始时间,在执行后记录结束时间,并记录日志。

  • private void recordLog(ProceedingJoinPoint joinPoint, long time) { }:这个方法用于记录日志,它获取了目标方法的签名、注解信息、方法参数、请求信息等,并使用日志记录器将这些信息输出到日志中。

    • ProceedingJoinPoint 是 Spring AOP 中的一个接口,它提供了对连接点(Join Point)进行操作的功能。在面向切面编程中,连接点表示程序执行过程中的特定点,比如方法的调用或异常的处理等。

      ProceedingJoinPointJoinPoint 的子接口,在 Spring AOP 中,它专门用于表示可以执行的连接点,例如在环绕通知中,通过调用 proceed() 方法可以执行目标方法。

      通常,在环绕通知中,我们会将 ProceedingJoinPoint 对象作为参数传递给通知方法,在通知方法中可以通过调用 proceed() 方法来继续执行目标方法,也可以获取连接点的信息,如方法签名、参数等。

整个类的作用是,当目标方法被调用时,记录下方法的执行时间、方法的输入参数、请求的 IP 地址等信息,并将这些信息输出到日志中,以便进行日志记录和监控。

package com.cherriesovo.blog.common.aop;

import com.alibaba.fastjson.JSON;
import com.cherriesovo.blog.utils.HttpContextUtils;
import com.cherriesovo.blog.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 日志切面
 */
@Aspect //切面 定义了通知和切点的关系
@Component
@Slf4j
public class LogAspect {
    //切点
    @Pointcut("@annotation(com.cherriesovo.blog.common.aop.LogAnnotation)")
    public void logPointCut() {
    }

    //通知类,环绕通知
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();	//开始时间
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        recordLog(point, time);
        return result;
    }

    //记录日志
    private void recordLog(ProceedingJoinPoint joinPoint, long time) {
        //获取了目标方法的签名信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //通过签名获取目标方法
        Method method = signature.getMethod();
        //获取了目标方法上的 LogAnnotation 注解,以便获取注解中的信息
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        log.info("=====================log start================================");
        log.info("module:{}",logAnnotation.module());	//输出日志中的模块信息
        log.info("operation:{}",logAnnotation.operator());	//输出日志中的操作信息

        String className = joinPoint.getTarget().getClass().getName();	//获取目标方法所属类的类名
        String methodName = signature.getName();	//获取目标方法的方法名
        //输出请求的方法名,格式为类名.方法名()
        log.info("request method:{}",className + "." + methodName + "()");

        //请求的参数
        Object[] args = joinPoint.getArgs();	//获取目标方法的参数列表
        String params = JSON.toJSONString(args[0]);	//参数列表转换为 JSON 格式的字符串,这里只获取了第一个参数
        log.info("params:{}",params);	//输出请求的参数信息

        //获取request 设置IP地址
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();	获取当前的HttpServletRequest对象
        log.info("ip:{}", IpUtils.getIpAddr(request));	//输出请求的 IP 地址


        log.info("excute time : {} ms",time);	//输出方法的执行时间
        log.info("=====================log end================================");
    }

}
package com.cherriesovo.blog.utils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

public class HttpContextUtils {
    /*用于获取当前线程的 HttpServletRequest 对象,通过 RequestContextHolder.getRequestAttributes() 获取到当前请求的属性对		 象,然后将其转换为 ServletRequestAttributes,再调用 getRequest() 方法获取到 HttpServletRequest 对象。*/
    public static HttpServletRequest getHttpServletRequest(){
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }
}

package com.cherriesovo.blog.utils;

import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

//IP 地址获取工具类 IpUtils,用于从 HTTP 请求中获取客户端的真实 IP 地址
public class IpUtils {

    private static Logger logger = LoggerFactory.getLogger(IpUtils.class);

    /**
     * 获取IP地址
     * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = null;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            logger.error("IPUtils ERROR ", e);
        }
        // 使用代理,则获取第一个IP地址
        if (StringUtils.isEmpty(ip) && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }
}
      ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
    } catch (Exception e) {
        logger.error("IPUtils ERROR ", e);
    }
    // 使用代理,则获取第一个IP地址
    if (StringUtils.isEmpty(ip) && ip.length() > 15) {
        if (ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }
    }
    return ip;
}

}


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

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

相关文章

PyTorch-Lightning:trining_step的自动优化

文章目录 PyTorch-Lightning&#xff1a;trining_step的自动优化总结&#xff1a; class _ AutomaticOptimization()def rundef _make_closuredef _training_stepclass ClosureResult():def from_training_step_output class Closure PyTorch-Lightning&#xff1a;trining_ste…

算法 分割字符串为实体类

题目 String userData "10000:张三:男:1998-01-01#10001:张三:男:1998-01-01#10002:李四:女:1999-02-02#10003:王五:男:2000-03-03#10004:赵六:女:2001-04-04"; String[] usersArray userData.split("#"); // 使用Stream API将字符串数组转换为SysUser对…

ALV合并单元格

1、文章说明 在开发一些报表时&#xff0c;需要显示双层的标题&#xff0c;或者合并单元格的数据&#xff0c;归根结底就是要实现类似EXCEL合并单元格的需求。如图所示 网上的资料&#xff0c;很多根据国外某大神的方法实现&#xff1a;https://tricktresor.de/blog/zellen-ver…

JavaScript(四)-Web APIS

文章目录 日期对象实例化时间对象方法时间戳 节点操作DOM节点查找节点增加节点删除节点 M端事件JS插件Window对象BOM&#xff08;浏览器对象模型&#xff09;定时器-延时函数JS执行机制location对象navigator对象history对象 本地存储本地存储介绍本地存储分类localStoragesess…

Play Module Factory:Codigger系统上的高效Module开发工具

Play Module Factory&#xff0c;这款在Codigger系统上独树一帜的Play-Module开发工具&#xff0c;为广大的开发者们提供了一个全新的、高效的插件开发平台。它汇集了丰富的模板资源、基础库、API接口以及语言支持&#xff0c;这些功能强大的工具组合在一起&#xff0c;使得开发…

14亿美元!德国默克与AI生物科技公司合作;马斯克Neuralink首位脑机接口植入者用意念打游戏;黄仁勋在俄勒冈州立大学开讲

AI for Science 的新成果、新动态、新视角—— 日本第一 IT 公司富士通&#xff1a;生成式 AI 加速药物研发 马斯克&#xff1a;Neuralink 首位脑机接口植入者用「意念」打游戏 默克与 AI 生物科技公司 Caris 达成合作 AI 蛋白质设计服务提供商「天鹜科技」完成数千万元 Pre…

VE、希喂、PR猫咪主食冻干怎么样?测评品控最强、配方最好主食冻干!

我发现还是有不少铲屎官局限于“进口最高贵”&#xff0c;盲目的迷信进口产品。看到进口粮就盲买&#xff0c;甚至过分的贬低国产品牌&#xff0c;将国产粮贴上“不靠谱”“不合格”等标签。 最近&#xff0c;我针对主食冻干的国内、国际标准&#xff0c;相关规范文件&#xf…

C++资源重复释放问题

这不是自己释放了2次&#xff1b; 可能是类互相引用&#xff0c;有类似现象释放资源时引起&#xff1b;还不太了解&#xff1b; 类对象作为函数参数也会引起&#xff1b; 下面是一个简单示例&#xff1b; #include <iostream> #include <string.h> #include &l…

如何快速写一份简历

文章目录 如何快速写一份简历一些写简历的技巧 最近一段时间一直在忙简历相关的事情&#xff0c;起初是有一个其他行业的朋友问我&#xff0c;说这些简历我写了好久真难写&#xff0c;我说你可以借助AI&#xff0c;现在这种工具多了去了&#xff0c;为什么不借助呢&#xff1f;…

2024年 Mathorcup高校数学建模竞赛(B题)| 甲骨文识别 | 特征提取,图像分割解析,小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮200人完成了建模与思路的构建的处理了&#xff5e; 本篇文章是鹿鹿学长经过深度思考&#xff0c;独辟蹊径&#xff0c;通过神经网络解决甲骨文识别问题。结合特征提取&#xff0c;图像分割等多元算法&…

Apabi Reader软件:打开ceb文件

Apabi Reader软件&#xff1a;打开ceb文件 软件下载软件安装 打开ceb文件参考 软件下载 下载官网-Apabi Reader软件 软件安装 打开ceb文件 ceb文件目录如下&#xff1a; 打开文件如下&#xff1a; 参考

从“黑箱”到“透明”:云里物里电子标签助力汽车总装数字化转型

“汽车总装”指“汽车产品&#xff08;包括整车及总成等&#xff09;的装配”&#xff0c;是把经检验合格的数以百计、或数以千计的各种零部件按照一定的技术要求组装成整车及发动机、变速器等总成的工艺过程&#xff0c;是汽车产品制造过程中最重要的工艺环节之一。 其中&…

社交网络与Web3:数字社交的下一阶段

随着信息技术的飞速发展&#xff0c;人们的社交方式也发生了巨大的变化。从最初的互联网聊天室到如今的社交网络平台&#xff0c;我们已经见证了数字社交的不断演变和发展。而随着区块链技术的兴起&#xff0c;Web3时代的到来将为数字社交带来全新的可能性和挑战。本文将探讨社…

.NET MAUI使用Visual Studio Android Emulator(安卓模拟器)运行

Android Emulator&#xff08;安卓模拟器&#xff09;运行&#xff1a; 安卓模拟器一直卡在不动&#xff1a; 在某些情况下&#xff0c;在“打开或关闭 Windows 功能”对话框中启用 Hyper-V 和 Windows 虚拟机监控程序平台后可能无法正确启用Hyper-V。 我就是开启Hyper-V才把安…

Python网络爬虫中JSON格式数据存储详解

目录 一、引言 二、JSON格式数据简介 三、Python中处理JSON数据 四、网络爬虫中获取JSON数据 五、存储JSON数据到文件 六、从文件中读取JSON数据 七、注意事项和常见问题 八、总结 一、引言 在网络爬虫的应用中&#xff0c;JSON格式数据以其轻量级、易读易写的…

Redis性能管理和集群的三种模式(二)

一、Redis集群模式 1.1 redis的定义 redis 集群 是一个提供高性能、高可用、数据分片、故障转移特性的分布式数据解决方案 1.2 redis的功能 数据分片&#xff1a;redis cluster 实现了数据自动分片&#xff0c;每个节点都会保存一份数据故障转移&#xff1a;若个某个节点发生故…

2024年第十四届 Mathorcup (B题)| 甲骨文智能识别 | 深度学习 计算机视觉 |数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看Mathorcup (B题&#xff09;&#xff01; CS团队…

基于springboot+vue的汽车租赁管理系统

背景介绍: 网络发展的越来越迅速&#xff0c;它深刻的影响着每一个人生活的各个方面。每一种新型事务的兴起都是为了使人们的生活更加方便。汽车租赁管理系统是一种低成本、更加高效的电子商务方式&#xff0c;它已慢慢的成为一种全新的管理模式。人们不再满足于在互联网上浏览…

自动化测试提速必备 - 并发编程

在自动化测试领域&#xff0c;多线程和多进程技术被广泛应用于提高测试的执行效率和性能。通过并发运行测试用例&#xff0c;可以显著缩短测试周期&#xff0c;特别是在面对大量测试用例或者需要在多个环境中进行测试时尤为重要。 在实际的自动化测试中&#xff0c;我们经常碰…

为什么选择成为程序员?

目录 兴趣和热爱高薪和就业机会持续学习和不断成长挑战和乐趣 兴趣和热爱 许多人选择成为程序员可能是热爱&#xff0c;对计算机&#xff0c;以及编程和科技产生了浓厚的兴趣&#xff0c;并且享受着解决每一个技术问题&#xff0c;构建应用程序和探索新技术所带来的乐趣。 谈到…