欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

VBlog项目代码理解之后端

程序员文章站 2022-04-19 11:25:24
...


VBlog项目代码理解之后端

资源

项目地址
前后端交互代码理解
前端代码理解
推荐:整个项目几乎是只用到了SpringBoot、Vue、Mybatis、ElementUI,没有用到Redis、RabbitMQ等内容,很适合刚学完SpringBoot和Vue的同学练手,感谢作者!帮作者打个广告吧~
VBlog项目代码理解之后端
PS:这是本人第一个学习的项目,难免会有错误的地方,哪里有问题烦请指正,感谢!


因为主要学的后端,之前也练过图书管理系统,所以没什么好详细记录的,就是有些Mybatis操作还不熟悉。


Bean

很多bean类,但只有User类实现了UserDetailsUserDetailsUserDetailsService在下面的Spring Security有介绍),其他的类都通过id等方式和User绑定。
要特别注意的方法就是getAuthorities()
还用@JsonIgnore注释了一些重载方法,注意中间isEnabled()没有用这个注释

    @Override
    @JsonIgnore
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    @JsonIgnore
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    @JsonIgnore
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    @JsonIgnore
    public List<GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
        }
        return authorities;
    }

Mapper

就是一些基础方法的组合
迷惑

  • 分页:

        <!--状态与权限的搜索,为啥还限制了分页?-->
        <select id="getArticleByStateByAdmin" resultType="org.sang.bean.Article">
            SELECT a.id,a.`title`,a.`editTime`,a.`pageView`,a.`state`,u.`nickname`,c.`cateName`,a.uid FROM article a,user
            u,category c WHERE a.`cid`=c.`id` AND a.`uid`=u.`id` and a.state=1
            <if test="keywords!=null">
                AND title LIKE concat('%',#{keywords},'%')
            </if>
            ORDER BY a.editTime DESC limit #{start},#{count};
        </select>
        <select id="getArticleCountByState" resultType="int">
            SELECT count(*) FROM article
            <where>
                <if test="state!=-1">
                    AND state=#{state}
                </if>
                <if test="uid!=null">
                    AND uid=#{uid}
                </if>
                <if test="keywords!=null">
                    AND title LIKE concat('%',#{keywords},'%')
                </if>
            </where>
        </select>
    
  • 奇怪的LEFT、JOIN、ON

        <!--啊!LEFT JOIN ON是啥-->
        <select id="getArticleById" parameterType="Long" resultMap="BaseResultMap">
            SELECT a.*,t.`tagName`,t.`id` AS tid,u.`nickname`,c.`cateName` FROM article a LEFT JOIN article_tags ats ON a.`id`=ats.`aid` LEFT JOIN tags t ON ats.`tid`=t.`id` LEFT JOIN user u ON a.`uid`=u.`id` LEFT JOIN category c ON a.`cid`=c.`id` WHERE a.id=#{aid}
        </select>
        <resultMap id="BaseResultMap" type="org.sang.bean.Article">
            <id column="id" property="id"/>
            <result column="title" property="title"/>
            <result column="cid" property="cid"/>
            <result column="uid" property="uid"/>
            <result column="publishDate" property="publishDate"/>
            <result column="editTime" property="editTime"/>
            <result column="state" property="state"/>
            <result column="pageView" property="pageView"/>
            <result column="mdContent" property="mdContent"/>
            <result column="htmlContent" property="htmlContent"/>
            <result column="summary" property="summary"/>
            <result column="nickname" property="nickname"/>
            <result column="cateName" property="cateName"/>
            <collection property="tags" ofType="org.sang.bean.Tags" column="tagName">
                <id property="id" column="tid"/>
                <result property="tagName" column="tagName"/>
            </collection>
        </resultMap>
    

学习操作

  • 动态sql:

        <!--根据状态看某种全部文章或者模糊搜索-->
        <!--用动态SQL实现两种功能,厉害-->
        <select id="getArticleByState" resultType="org.sang.bean.Article">
            SELECT a.id,a.`title`,a.`editTime`,a.`pageView`,a.`state`,u.`nickname`,c.`cateName`,a.uid FROM article a,user
            u,category c WHERE a.`cid`=c.`id` AND a.`uid`=u.`id`
            <if test="state!=-2">
                and a.uid=#{uid}
            </if>
            <if test="state!=-1 and state!=-2">
                and a.state=#{state}
            </if>
            <if test="state==-2">
                and a.state=1
            </if>
            <if test="keywords!=null">
                AND title LIKE concat('%',#{keywords},'%')
            </if>
            ORDER BY a.editTime DESC limit #{start},#{count};
        </select>
    
  • foreach:

        <!--批量更新,应是对应着批量删除操作,把文章放入回收站-->
        <!--学学foreach-->
        <update id="updateArticleState">
            UPDATE article SET state=#{state} WHERE id IN
            <foreach collection="aids" item="aid" separator="," open="(" close=")">
                #{aid}
            </foreach>
        </update>
    
  • 有自增主键,用null填上:

        <insert id="addRoles">
            INSERT INTO roles_user VALUES
            <foreach collection="roles" item="role" separator=",">
                (null,#{role},#{uid})
            </foreach>
        </insert>
    

Service

UserService
还是只有UserService实现了UserDetailsService方法。
重写的loadUserByUsername方法。

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(s);
        if (user == null) {
            //避免返回null,这里返回一个不含有任何值的User对象,在后期的密码比对过程中一样会验证失败
            return new User();
        }
        // 查询用户的角色信息,并返回存入user中
        // 如果user为null这里就会报错
        List<Role> roles = rolesMapper.getRolesByUid(user.getId());
        user.setRoles(roles);
        return user;
    }
    public int reg(User user) {
        User loadUserByUsername = userMapper.loadUserByUsername(user.getUsername());
        // 看看用这个名字能不能读出数据,能就代表名字重复了
        if (loadUserB  yUsername != null) {
            // 重复
            return 1;
        }
        //插入用户,插入之前先对密码进行加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setEnabled(true);//用户可用
        long result = userMapper.reg(user);
        //配置用户的角色,默认都是普通用户
        String[] roles = new String[]{"2"};
        int i = rolesMapper.addRoles(roles, user.getId());
        boolean b = i == roles.length && result == 1;
        if (b) {
            // 成功
            return 0;
        } else {
            // 失败
            return 2;
        }
    }

其他的部分平平无奇

CategoryService
批量删除的操作比较有意思,传进去的就是一个String数组

    public boolean deleteCategoryByIds(String ids) {
        String[] split = ids.split(",");
        int result = categoryMapper.deleteCategoryByIds(split);
        return result == split.length;
    }
    int deleteCategoryByIds(@Param("ids") String[] ids);

    <delete id="deleteCategoryByIds">
        DELETE FROM category WHERE id IN
        <foreach collection="ids" separator="," open="(" close=")" item="id">
            #{id}
        </foreach>
    </delete>

DataStatisticsComponent
使用了一个定时执行注解:
好像是倒着来,每天0 : 01会执行一次
这个注解要在XXXApplication文件中开启注解支持@EnableScheduling//开启定时任务支持

    //每天执行一次,统计PV
    @Scheduled(cron = "1 0 0 * * ?")
    public void pvStatisticsPerDay() {
        articleService.pvStatisticsPerDay();
    }

ArticleService
比较复杂的就是文章和对应标签的

    public int addNewArticle(Article article) {
        //处理文章摘要, 如果没写的话就截取前50个字符
        if (article.getSummary() == null || "".equals(article.getSummary())) {
            //直接截取
            String stripHtml = stripHtml(article.getHtmlContent());
            article.setSummary(stripHtml.substring(0, stripHtml.length() > 50 ? 50 : stripHtml.length()));
        }
        // id=-1就是刚添加的,有id就是更新
        if (article.getId() == -1) {
            //添加操作
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            if (article.getState() == 1) {
                //设置发表日期
                article.setPublishDate(timestamp);
            }
            article.setEditTime(timestamp);
            // 设置当前用户,同时添加到数据库
            article.setUid(Util.getCurrentUser().getId());
            int i = articleMapper.addNewArticle(article);
            // 打标签,也是加到数据库中
            // 因为文章和tag是两个类,通过id绑定,所以不能一步走
            String[] dynamicTags = article.getDynamicTags();
            if (dynamicTags != null && dynamicTags.length > 0) {
                int tags = addTagsToArticle(dynamicTags, article.getId());
                if (tags == -1) {
                    return tags;
                }
            }
            return i;
        } else {
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            if (article.getState() == 1) {
                //设置发表日期
                article.setPublishDate(timestamp);
            }
            //更新
            article.setEditTime(new Timestamp(System.currentTimeMillis()));
            int i = articleMapper.updateArticle(article);
            //修改标签
            String[] dynamicTags = article.getDynamicTags();
            if (dynamicTags != null && dynamicTags.length > 0) {
                int tags = addTagsToArticle(dynamicTags, article.getId());
                if (tags == -1) {
                    return tags;
                }
            }
            return i;
        }
    }

    private int addTagsToArticle(String[] dynamicTags, Long aid) {
        //1.删除该文章目前所有的标签
        tagsMapper.deleteTagsByAid(aid);
        //2.将上传上来的标签全部存入数据库
        tagsMapper.saveTags(dynamicTags);
        //3.查询这些标签的id
        List<Long> tIds = tagsMapper.getTagsIdByTagName(dynamicTags);
        //4.重新给文章设置标签
        int i = tagsMapper.saveTags2ArticleTags(tIds, aid);
        return i == dynamicTags.length ? i : -1;
    }

Config:登录Spring Security配置

看Spring Security的笔记就行。
笔记地址


Controller

注意权限,如果有多个权限,那不同的权限处理放到不同的文件夹较好。而且在对应的控制类上加上权限的前缀,如/admin/user,这样也方便后面的config中的权限管理。
当使用SpringBoot和Vue做前后端分离的项目的时候,controller是不做视图跳转的,并不能控制到视图,跳转都被Vuerouter做了,所以一般都在controller上加了@RestController注解。


Resources:配置文件与前端资源

Vue的内容做npm run build,就会生成一个dist文件啊加,将其中的文件夹static和文件index.html拷贝到后端项目的resources/static/,再在application.properties中设置端口号server.port=xxxx,运行SpringBoot就行了,输入http://localhost:xxxx/index.html即可。

相关标签: 项目经验