基于SSM实现个人随笔分享平台:创作心灵,分享自我

news2024/11/18 5:34:02

项目简介

 本文将对项目的功能及部分细节的实现进行介绍。个人随笔分享平台基于 SpringBoot + SpringMVC + MyBatis 实现。实现了用户的注册与登录、随笔主页、文章查询、个人随笔展示、个人随笔查询、写随笔、草稿箱、随笔修改、随笔删除、访问量及阅读量统计等功能。该项目登录模块对明文密码进行了加盐处理,并且将session使用Redis进行了持久化存储,为分布式的支持奠定了基础,同时,使用了统一功能处理与拦截器。
项目总览


文章目录

  • 项目简介
  • 1 功能概览
  • 2 主要功能展示
  • 3 系统部分实现细节
    • 3.1 草稿箱实现
    • 3.2 分页查询
    • 3.3 查询功能
    • 3.4 随笔的修改与发布
    • 3.5 随笔与草稿的删除
    • 3.5 对随笔摘要的 markdown 标签处理
    • 3.6 基于 MD5 的加盐算法处理明文密码
    • 3.7 使用 Redis 持久化 Session
  • 4 开发过程遇到的问题
    • 4.1 Redis服务的远程连接问题与session持久化失败
    • 4.2 查询功能url获取参数中文乱码问题
    • 4.3 草稿箱内容出现在个人随笔页
  • 写在最后


1 功能概览

以下是对个人随笔分享平台功能的表格整理:

功能描述
登录功能用户可以使用用户名和密码登录到平台。
注册功能用户可以注册新的账户来访问平台。
个人信息展示可以查看个人信息: 头像、昵称、访问量、文章数等。
个人随笔列表展示用户可以查看自己发布的随笔列表。
查询个人随笔用户可以根据关键字查询自己发布的随笔。
随笔主页分页查询功能用户可以在随笔主页浏览所有用户的随笔,系统将用户的文章进行分页展示
查询随笔用户可以根据关键字查询其他用户发布的随笔。
发布随笔功能用户可以发布新的随笔。
随笔草稿箱用户可以保存随笔的草稿,并在需要时进行编辑和发布。
随笔或草稿编辑功能用户可以编辑已发布的随笔或草稿。
随笔详情展示用户可以查看随笔的详细内容和相关信息。
随笔或草稿删除功能用户可以删除已发布的随笔或草稿。
访问量与阅读量统计平台可以统计每篇随笔的访问量和阅读量,并显示给用户。

2 主要功能展示

随笔主页
无论用户是否登录,都可以使用随笔主页。在该界面中,您可以查阅所有作者发布的内容。
随笔主页
分页展示
在随笔主页,内容采用分页展示,默认每页展示的文章条数最大为5条,点击下一页等按钮可以进行页面内容切换。
分页展示

“搜你想搜”
在随笔主页,你可以使用搜索框搜索你想要查询的内容,系统会根据你给定的字段进行模糊匹配,检索系统中符合条件的文章,并同样以分页展示的形式呈现给你。
搜你想搜
登录与注册模块
与大多数为用户提供服务的系统一样,该平台同样包含了注册与登录功能。未注册的用户通过登记相关信息可以成为平台的用户,平台的用户可以使用用户名和密码进入自己的随笔主页,同时也拥有了体验该平台完整功能的权利。需要注意的是,该平台暂未提供手机登录等“生产”级别的业务,如有需要,可自行扩展–比如可以使用某某云之类提供的接口扩展短信验证码等模块。
登录模块
注册模块
个人随笔列表展示
在该页面中,将展示您发布的所有随笔(草稿除外),您可以在该页面查看自己的信息(包括自己的头像、文章数量及总访问量),搜索自己的内容,或者选择查看、编辑、删除自己的随笔。
个人随笔列表展示

搜索TA的内容
在用户随笔主页中,可以在搜索框中输入想要搜索的内容,系统将模糊匹配您发布的文章是否有包含该字段的文章,如果有,将以列表的方式呈现给你。
搜索TA的内容
发布随笔与编辑随笔
你可以选择对应随笔进行编辑,或者点击写随笔按钮来新建一篇随笔。随笔的编辑与发布支持MarkDown语法。
编辑与发布随笔

草稿箱模块
由于并不是每个人都有连续的时间,能够将一篇随笔写完发布后再去处理其他事情。因此,本系统提供了草稿箱功能,您可以将你编辑的内容先保存到草稿箱,等到您需要的时候进行发布、编辑或者删除。
编辑草稿
草稿箱展示
随笔详情展示
在随笔主页或者“我的随笔”页面,您可以通过点击查看全文按钮的方式查看随笔的全文内容。不仅如此,您还可以查阅本文的部分作者信息、文章的阅读量与发布时间修改时间等信息。
随笔详情展示


3 系统部分实现细节

3.1 草稿箱实现

本系统对于草稿箱的实现,实际是对文章的状态进行区分。在articleinfo文章表中,我们预留出了一个state字段,用于表示文章的状态:

  • state = 1 : 正常的发布文章;
  • state = 0 : 处于草稿箱的文章;

在进行个人随笔主页以及草稿箱页面展示文章信息,只需要对应展示该登录用户对应文章状态的文章即可。对于前端页面,对“发布文章”与“保存草稿”按钮的监听进行了区分:

  • 点击发布文章按钮: 请求包含文章的标题、文章的内容和 state = 1;
  • 点击保存草稿按钮: 请求同上,区别是 state = 0;

基于此构造 ajax 请求后端进行 insert 操作。

后端实现的核心代码:

     /**
     * 发布文章 state = 1 与保存草稿箱 state = 0
     * 如果是保存草稿箱, 则请求的文章对象中 state = 0
     * 更新了传入 createtime 和 updatetime 解决数据库不兼容问题
     */
    @RequestMapping("/add")
    public AjaxResult add(HttpServletRequest request, Articleinfo articleinfo) {
        // 非空校验
        if (articleinfo == null || !StringUtils.hasLength(articleinfo.getTitle()) ||
                !StringUtils.hasLength(articleinfo.getContent())) {
            return AjaxResult.fail(-1, "非法参数");
        }
        // 发布文章操作
        // 得到当前用户的 uid
        Userinfo userinfo = UserSessionUtils.getSessUser(request);
        if (userinfo == null || userinfo.getId() <= 0) {
            // 无效的登录用户
            return AjaxResult.fail(-2, "无效的登录用户");
        }
        articleinfo.setUid(userinfo.getId()); // 设置作者 id
        // 设置创建时间和更新时间, 解决数据库没有默认约束的情况
        articleinfo.setCreatetime(LocalDateTime.now());
        articleinfo.setUpdatetime(LocalDateTime.now());
        // 添加数据库并返回
        return AjaxResult.success(articleService.add(articleinfo));
    }

3.2 分页查询

分页查询的实现涉及几个重要的因素:

  • pIndex: 当前页码;
  • pSize: 每页最多显示的文章条数。

而分页功能在 SQL 中的实现基于如下的 SQL:

select xxx from articleinfo 
where xxx
limit pIndex offset offsetSize;

经过推导,我们可以得出如下映射关系:

offsetSize = (pIndex - 1) / pSize

对于 where 子句的内容,我们使用 Mybatis 的动态 SQL 进行处理。

而总页数需要通过对 文章总数 / 每页最大文章数 的结果进行向上取整,比如: ceil()进行处理。

同时,在前端页面,该平台使用的是拼接跳转的方式,点击下一页就将pIndex ++(需要对范围进行判断,防止越界),而每页结果在构造 ajax 请求发送给后端请求响应哦时候,就从 location.search 中获取请求参数中的 pindex。

后端核心代码如下:

    /**
     * 分页查询
     * @param pIndex 当前页码
     * @param pSize  每页最多显示的文章条数
     * @param key  检索的 title
     * @return  返回的 data 中包含一个 map, 含有当前页的文章列表与文章总数量
     */
    @RequestMapping("/listbypage")
    public AjaxResult getListByPage(@RequestParam("pindex") Integer pIndex,
                                    @RequestParam("psize") Integer pSize,
                                    @RequestParam("key") String key) {
        // 参数校正
        if (pIndex == null || pIndex < 1) {
            pIndex = 1;
        }
        if (pSize == null || pSize <= 1) {
            pSize = 3;
        }
        // 分页查询
        int offSize = (pIndex - 1) * pSize;
        List<Articleinfo> articleInfoList = articleService.getListByPage(pSize, offSize, key);
        // 对摘要进行处理
        for (Articleinfo articleinfo : articleInfoList) {
            articleinfo.setContent(
                    MarkdownUtils.removeMarkdownTags(articleinfo.getContent(), 256));
        }
        // 获取文章总数
        int totalArticleCount = articleService.getTotalArticleCount(key);
        HashMap<String, Object> map = new HashMap<>();
        map.put("articleInfoList", articleInfoList);
        map.put("totalArticleCount", totalArticleCount);
        return AjaxResult.success(map);
    }
}

3.3 查询功能

查询功能主要通过 Mybaties 的动态 SQL 实现,在 *mapper.xml 的相应 select 语句中使用了 if 标签。如果前端构造请求的时候没有 key (即搜索框中没有内容),则默认返回的是当前页应该展示的文章列表或者是当前登录用户的所有文章列表。注意,这里所说的列表指的是正常发布的文章,对于草稿箱的内容,需要在草稿箱中查看。

后端核心代码如下

    /**
     * 加载用户的文章列表信息
     * key: 搜索 title
     * state: 0 草稿, 1 文章
     */
    @RequestMapping("/mylist")
    public AjaxResult getMyList(HttpServletRequest request, Integer state, String key) {
        Userinfo userinfo = UserSessionUtils.getSessUser(request);
        if (userinfo == null) {
            return AjaxResult.fail(-1, "非法请求");
        }
        List<Articleinfo> list = articleService.getMyList(userinfo.getId(), state, key);
        // 将 list 中每个文章信息对象的正文进行处理, 去除 markdown 标签并最多显示 256 个字符
        for (Articleinfo articleinfo : list) {
            String content = articleinfo.getContent();
            content = MarkdownUtils.removeMarkdownTags(content,
                    ApplicationVariable.THE_MAXIMUM_NUMBER_OF_CHARACTERS_IN_THE_DIGEST);
            articleinfo.setContent(content);
        }
        return AjaxResult.success(list);
    }

3.4 随笔的修改与发布

随笔的修改与发布与草稿箱的实现模块类似,修改会先将文章id作为请求,请求后端返回当前文章内容,往后点击发布文章进行 update 操作。而发布文章则是 insert 操作。与草稿箱不同的是,这里的修改与发布所更新的 state 字段都是 1。

后端实现核心代码如下

    /**
     * 修改文章
     * 只有当前登录用户是作者的情况下才能进行修改(验权)
     */
    @RequestMapping("/update")
    public AjaxResult update(HttpServletRequest request, Articleinfo articleinfo) {
        // 非空检验
        if (articleinfo == null || !StringUtils.hasLength(articleinfo.getTitle()) ||
                !StringUtils.hasLength(articleinfo.getContent()) ||
                articleinfo.getId() == null ||
                articleinfo.getId() <= 0) {
            return AjaxResult.fail(-1, "非法参数");
        }
        // 得到当前登录用户的 id
        Userinfo userinfo = UserSessionUtils.getSessUser(request);
        if (userinfo == null || userinfo.getId() == null || userinfo.getId() <= 0) {
            return AjaxResult.fail(-2, "无效用户");
        }
        // 设置文章对象的 uid 为当前用户的 id, 便于 mapper 验证是否为当前登录用户
        articleinfo.setUid(userinfo.getId());
        articleinfo.setUpdatetime(LocalDateTime.now());
        return AjaxResult.success(articleService.update(articleinfo));
    }

3.5 随笔与草稿的删除

对于随笔与草稿的删除都是通过传给后端当前文章或草稿的 id 请求删除操作。同时,后端需要对当前登录用户进行验权,即判断当前登录用户是否为文章的作者。以解决当前登录用户注销后忘记关闭页面而另一个人登录后,由于session存在导致的误删误改的情况。

后端实现的核心代码如下

    /**
     * 删除文章
     * 只有当前登录的用户是文章的作者才能删除(验权)
     */
    @RequestMapping("/del")
    public AjaxResult del(HttpServletRequest request, @RequestParam("aid") Integer id) {
        // 参数检验
        if (id == null || id <= 0) {
            return AjaxResult.fail(-1, "非法参数");
        }
        // 进行删除
        // 只有当前登录的用户同时是文章的作者的情况下才能删除
        Userinfo user = UserSessionUtils.getSessUser(request);
        if (user == null) {
            return AjaxResult.fail(-2, "用户未登录");
        }
        int delCount = articleService.del(id, user.getId());
        return AjaxResult.success(delCount);
    }

3.5 对随笔摘要的 markdown 标签处理

由于数据库存储的文章内容均为 markdown 字符,因此,需要对 markdown 标签进行处理。使得在随笔的列表展示中,摘要不显示形如 # 这样的符号。
笔者实现的方式是通过正则表达式匹配,从而进行处理,具体实现代码如下:

import java.util.regex.Pattern;

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * @date 2023/7/23 23:18
 * 用于处理文章列表的文章摘要信息
 * 1. 对摘要去除 markdown 标签处理
 * 2. 摘要只截取文章正文前 n 个字符
 */
public class MarkdownUtils {

    // 匹配Markdown标签的正则表达式
    private static String regex = "\\*\\*|__|\\*|_|~~|`|\\[\\]|\\(|\\)|\\{|\\}|\\[|\\]|#|\\+|-|\\.|!";

    /**
     * 对摘要进行去 markdown 标签处理
     */
    public static String removeMarkdownTags(String markdown, int n) {
        // 先对 markdown 字符串进行截取
        markdown = preprocessingString(markdown, n);
        // 使用空字符串替换Markdown标签并返回
        return Pattern.compile(regex).matcher(markdown).replaceAll("");
    }

    /**
     * 截取正文的前 n 个字符作为摘要
     */
    private static String preprocessingString(String s, int n) {
        if (s == null || s == "" || n <= 0) {
            return "";
        }
        String result = "";
        if (n >= s.length()) {
            result = s.substring(0, s.length());
        } else {
            result = s.substring(0, n);
        }
        return result;
    }
}

3.6 基于 MD5 的加盐算法处理明文密码

若数据库存储的密码是形如: 11111 这样的明文形式,则是非常不安全的,若数据库泄露,用户的密码很容易被获取。因此,需要对密码进行处理。

单一的采用 md5 的方式进行加密也是不安全的,因为 md5 对固定字符的加密结果是相同的,比如对于111加密,加密结果是EF2类似这样,通过彩虹表就可以逆向查出来加密前的密码。

笔者采用的方式是通过“加盐”的方式进行处理,即将未加密的密码拼接一个随机值(盐值),然后再进行md5加密,这样就解决了固定密码加密结果相同的的问题。而在存储到数据库前,也需要将盐值一并存储,笔者采用的方式是使用$符号进行分割:
盐值\$加密的密码。进行比对的时候,只需要从数据库中取出盐值,进行加密后与取出加密的密码进行比较。 具体实现如下:

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import org.springframework.util.StringUtils;

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * @date 2023/7/24 17:01
 * 密码明文基于 MD5 随机盐值加密处理
 */
public class PasswordUtils {

    /**
     * 加密(加盐)
     * @param password 需要加密的密码
     * @return 盐值$加密的密码
     */
    public static String encrypt(String password) {
        // 随机盐值
        String salt = IdUtil.simpleUUID();
        // 密码 md5(随机盐值 + 密码)
        String encryptPassword = SecureUtil.md5(salt + password);
        return salt + "$" + encryptPassword;
    }

    /**
     * 解密
     * @param password          要验证的密码(未加密)
     * @param securePassword    数据库中加了盐值的密码
     * @return 返回密码是否正确
     */
    public static boolean decrypt(String password, String securePassword) {
        boolean result = false;
        if (StringUtils.hasLength(password) && StringUtils.hasLength(securePassword)) {
            if (securePassword.length() == 65 && securePassword.contains("$")) {
                String[] securePasswordArr = securePassword.split("\\$");
                // 盐值
                String salt = securePasswordArr[0];
                // 加密后的密码
                String encryptPassword = securePasswordArr[1];
                // 使用同样的 md5 与同样的盐值对 password 进行加密
                password = SecureUtil.md5(salt + password);
                // 比较是否相同
                return encryptPassword.equals(password);
            }
        }
        return result;
    }
}

3.7 使用 Redis 持久化 Session

首先,需要在 SpringBoot 项目中引入依赖:

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

在配置文件中添加上 redis 相关的配置:

spring:
  redis:
    host: xxx.xxx.xxx.xxx
    port: 6379
    session:
    store-type: redis
    timeout: 1800
    redis:
    flush-mode: on_save
    namespace: spring:session

4 开发过程遇到的问题

4.1 Redis服务的远程连接问题与session持久化失败

笔者在开发过程中,使用 Xshell 连接云服务器的 redis 总是出现问题,在处理过程中发现服务器被劫持,最终只得重装系统。当然,也安装了一些工具来防止暴力破解。回到正题,启动了 redis 服务为什么关闭远程连接会话窗口后不久,服务器的 redis 进程就关闭了呢? 后面笔者采用 nohup 的方式启动服务,问题得到解决(需要在配置文件中先把 redis 配置后台运行):
redis服务
正当我兴致慢慢去启动项目的时候,项目又出现了问题,大概就是在持久化 session 的过程中抛了异常,经查: 当对象需要进行网络传输的时候,需要实现序列化接口,而在项目中,当用户登录成功时,其 session 就是存储了 userinfo 对象。当实现了序列化接口后,问题得到解决。

4.2 查询功能url获取参数中文乱码问题

在实现随笔主页的查询过程中,笔者的思路是将查询的 key 通过点击按钮的方式构造到 url 上,这样每一页都可以通过自己写的方法获取到 search 上的指定参数。在功能完成时,测试英文搜索没问题,而当输入中文检索的时候,则显示不了预期结果。通过调试,发现后端的 sql 竟然传入了一段乱码的 key!!! 将前端对应的代码进行修改,使用decodeURIComponent()进行解码,问题得到解决:

         // 获取当前 url 的参数
         function getUrlValue(key) {
            var params = location.search; // '?xxx&xxx'
            if (params.length > 1) {
                // 截取
                params = params.substring(1);
                var paramArr = params.split("&");
                // 获取对应的 keyValue
                for(var i = 0; i < paramArr.length; i++) {
                    var kv = paramArr[i].split("=");
                    if(kv[0]==key) {
                        return decodeURIComponent(kv[1]); // 处理中文乱码
                    }
                }
            }
            return "";
        }

4.3 草稿箱内容出现在个人随笔页

这个问题的出现实属不该,是笔者在迭代功能的过程中忘记给 sql 添加 where state = xxx 的子句。导致在随笔主页和个人随笔主页以及草稿箱均显示了草稿和已发布的文章。在生产环境中,很多企业都有自己的工具,对于忘记添加 where 语句的 sql 是会报错的!还好,笔者的该模块还仅仅是查询,如果是 insert 或者 update 就会出现数据污染问题了。


写在最后

本文被 JavaEE编程之路 收录点击订阅专栏 , 持续更新中。
 以上便是本文的全部内容啦!创作不易,如果你有任何问题,欢迎私信,感谢您的支持!

在这里插入图片描述

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

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

相关文章

十六章:可靠性确实重要:一种端到端的弱监督语义分割方法

0.摘要 弱监督语义分割是一项具有挑战性的任务&#xff0c;因为它只利用图像级别的信息作为训练的监督&#xff0c;但在测试时需要产生像素级别的预测。为了应对这样一个具有挑战性的任务&#xff0c;最近最先进的方法提出了采用两步解决方案&#xff0c;即&#xff1a;1&#…

自动上传git

自动上传git 执行脚本 保存为.bat文件 echo off title bat 交互执行git命令 D: cd D:/git/test git add . git commit -m %date:~0,4%年%date:~5,2%月%date:~8,2%日 git push教程如下 1、搜索任务计划程序&#xff08;最好管理员身份运行&#xff0c;普通用户可能无权限&am…

下载JMeter的历史版本——个人推荐5.2.1版本

官网地址&#xff1a;https://archive.apache.org/dist/jmeter/binaries/

【Git|项目管理】Git的常用命令以及使用场景

文章目录 1.前言2.工作区,暂存区,版本库简介3.Git的常用命令4.版本回退5.撤销修改6.删除文件7.总结 1.前言 在学习Git命令之前,需要先了解工作区,暂存区和版本库这三个概念 2.工作区,暂存区,版本库简介 在使用Git进行版本控制时&#xff0c;有三个重要的概念&#xff1a;工作…

机器学习——异常检测

异常点检测(Outlier detection)&#xff0c;⼜称为离群点检测&#xff0c;是找出与预期对象的⾏为差异较⼤的对象的⼀个检测过程。这些被检测出的对象被称为异常点或者离群点。异常点&#xff08;outlier&#xff09;是⼀个数据对象&#xff0c;它明显不同于其他的数据对象。异…

invalid use of incomplete type class ui(new Ui::MainWindow)报错,解决方案

invalid use of incomplete type class ui(new Ui::MainWindow报错&#xff0c;解决方案 原因解决方案 原因 就是在我改控件button的名字的时候&#xff0c;没有选中控件&#xff0c;导致吧mainwindow的名字改了。。。 解决方案 吧mainwindow的名字改回来 MainWindow 完美解…

【LeetCode】101.对称二叉树

题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false提示&#xff1a; 树中节点数…

java项目之社区疫情防控管理信息系统(ssm+mysql+jsp)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的社区疫情防控管理信息系统。技术交流和部署相关看文章末尾&#xff01; 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Ja…

小米新专利曝光:解决升降摄像头痛点,隐藏式摄像头指日可待

根据国家知识产权局官方网站的最新消息&#xff0c;小米移动软件公司的“摄像头隐藏式电子设备及隐藏式摄像头”的专利申请在今天获得了授权。 这款电子设备的主要组成部分包括壳体、摄像模组和可伸缩的反射组件。壳体上设有一个开口&#xff0c;可以让反射组件向外伸出。反射组…

数据可视化大屏拼接屏开发实录:屏幕分辨率测试工具

一、可视化大屏开发 在数据可视化大屏开发时&#xff0c;确定数据可视化大屏拼接屏的板块尺寸需要考虑以下几个因素&#xff1a; 屏幕分辨率&#xff1a;首先需要知道每个板块屏幕的分辨率&#xff0c;包括宽度和高度&#xff0c;这决定了每个板块上可以显示的像素数量。 数据…

Qt : day4

1.思维导图 2.服务器 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//给服务器指针实例化空间server new QTcpServer(this);}Widget::~Widget() {delete ui;…

Pytorch(一)

目录 一、基本操作 二、自动求导机制 三、线性回归DEMO 3.1模型的读取与保存 3.2利用GPU训练时 四、常见的Tensor形式 五、Hub模块 一、基本操作 操作代码如下: import torch import numpy as np#创建一个矩阵 x1 torch.empty(5,3)# 随机值 x2 torch.rand(5,3)# 初始化…

grid网格布局看这一篇就够了(接近3w字的总结)

在当今现代Web设计中&#xff0c;如何实现有效的布局一直是一个关键问题。这就是为什么CSS3推出了“grid网格布局”作为一种新的布局方式。使用grid&#xff0c;您可以轻松地设置复杂的网格布局&#xff0c;而无需使用冗长的CSS代码或框架。本文将探讨grid的概念、语法和实际应…

青大数据结构【2021】

一、单选&#xff08;17&#xff01;&#xff09; 根据中序遍历得到降序序列可以知道&#xff0c;每个结点的左子树的结点的值比该结点的值小&#xff0c;因为没有重复的关键字&#xff0c;所以拥有最大值的结点没有左子树。 二、简答 三、分析计算 四、算法分析 3.迪杰斯特拉…

DAY53:动态规划(十八)最长公共子序列+不相交的线+最大子序列和

文章目录 1143.最长公共子序列(注意递推的逻辑)思路DP数组含义递推公式初始化完整版重要&#xff1a;该解法是否保持了元素顺序总结 1035.不相交的线&#xff08;注意思路&#xff09;思路完整版 53.最大子序列和思路1&#xff1a;贪心思路1完整版思路2&#xff1a;动态规划DP数…

java商城系统和php商城系统对比

java商城系统和php商城系统是两种常见的电子商务平台&#xff0c;它们都具有一定的优势和劣势。那么&#xff0c;java商城系统和php商城系统又有哪些差异呢&#xff1f; 一、开发难度 Java商城系统和PHP商城系统在开发难度方面存在一定的差异。Java商城系统需要使用Java语言进…

【前端工程化】未使用docker时,前端项目实现线上秒级回滚

目录 一. 前言 二. 思路 三. 实践 3.1 准备单页应用项目 3.2 保存历史构建index.html内容 3.3 模拟服务端托管前端应用 3.4 快速回滚node服务端代码开发 3.5 快速回滚前端可视化页面开发 3.6 快速回滚测试 四. 总结 一. 前言 项目快速回滚是前端工程化中很重要的一环&…

【项目】轻量级HTTP服务器

文章目录 一、项目介绍二、前置知识2.1 URI、URL、URN2.2 CGI2.2.1 CGI的概念2.2.2 CGI模式的实现2.2.3 CGI的意义 三、项目设计3.1 日志的编写3.2 套接字编写3.3 HTTP服务器实现3.4 HTTP请求与响应结构3.5 EndPoint类的实现3.5.1 EndPoint的基本逻辑3.5.2 读取请求3.5.3 构建响…

yolov5 onnx模型 转为 rknn模型

1、转换为rknn模型环境搭建 onnx模型需要转换为rknn模型才能在rv1126开发板上运行&#xff0c;所以需要先搭建转换环境 模型转换工具 模型转换相关文件下载&#xff1a; 网盘下载链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;teuc 将其移动到虚拟机中&#xf…

用于提取数据的三个开源NLP工具

开发人员和数据科学家使用生成式AI和大语言模型&#xff08;LLM&#xff09;来查询大量文档和非结构化数据。开源LLM包括Dolly 2.0、EleutherAI Pythia、Meta AI LLaMa和StabilityLM等&#xff0c;它们都是尝试人工智能的起点&#xff0c;可以接受自然语言提示&#xff0c;生成…