<2021SC@SDUSC>博客(13)山东大学软件工程应用与实践JPress代码分析(十二)

2021SC@SDUSC

前言

在对 Controller 做最后的分析之前,我首先厘清了 module-article-web 模块下其它部分对该模块的影响,发现 directiveinterceptorkitwechat 均为工具类,而 webapp 则为前端界面,整个模块最核心的部分仍为 MVC 架构的 Controller 部分。

<2021SC@SDUSC>博客(13)山东大学软件工程应用与实践JPress代码分析(十二)

在前文中我谈到了 module-article 模块下的 Controller 模块,并就其中 adminapi 做了详细分析,本文将继续对与 _ArticleController 同级的 Controller frontucenter 进行分析。本文将是最后一篇对 JPress 框架 module-article 模块的分析。

front

front 目录下放置的是与前端 URL 映射、前端所能获得的数据相关的 Controller,是 MVC 架构中 View 部分的必备组件。

<2021SC@SDUSC>博客(13)山东大学软件工程应用与实践JPress代码分析(十二)

在该目录下一共有五个 Controller,分别是:

  • ArticleCategoryController,获取文章类别
  • ArticleController,获取文章详细信息
  • ArticlesController,获取所有文章
  • ArticleSearchController,搜索文章
  • ArticleTagController,获取文章标签

这五个 Controller 在代码层面上大体保持高度相似,因此这里我们选择其中一个具有代表性、也是代码最多的 ArticleController 进行代码层面上的拆解与分析。

ArticleController

对应前文所分析的 _ArticleController,这里的 ArticleController 属于用户端获取文章的 Controller,而前者则是管理员的,意即后者在获取文章详细内容之前应当是有权限限制的。

该 Controller 与前文大多数 Controller 基本无异,其私有成员变量 OptionServiceUserFavoriteService 均不是重点,而最核心的一共三个方法 index()postComment()doAddFavorite(),但令我吃惊的是这个 Controller 居然也没有被用到。

index()

public void index() {
    Article article = getArticle();
    render404If(article == null || !article.isNormal());
    if (StrUtil.isNotBlank(article.getLinkTo())) {
        redirect(article.getLinkTo());
        return;
    }
    setSeoInfos(article);
    doFlagMenuActive(article);
    articleService.doIncArticleViewCount(article.getId());
    User articleAuthor = article.getUserId() != null
            ? userService.findById(article.getUserId())
            : null;
    article.put("user", articleAuthor);
    setAttr("article", article);
    render(article.getHtmlView());
}

该方法主要用于获得文章的内容,如前面分析所言,首先进行了一大堆的权限判断、访问量增加等操作,最后才访问文章内容。

具体到代码层面则是:

  • 获取文章,判断文章是否正常,不正常则返回 404
  • 通过私有方法 setSeoInfo() 设置文章的 SEO 信息,以文章标题、关键字、描述作为主要内容
  • 通过私有方法 doFlagMenuActive() 设置菜单高亮信息,主要是前端界面需要用到
  • 通过 ArticleService 记录当前浏览量
  • 最后获取文章的作者和内容,返回页面给请求者

postComment()

public void postComment() {
    Long articleId = getParaToLong("articleId");
    Long pid = getParaToLong("pid");
    String nickname = getPara("nickname");
    String content = getPara("content");
    String email = getPara("email");
    String wechat = getPara("wechat");
    String qq = getPara("qq");
    if (articleId == null || articleId <= 0) {
        renderFailJson();
        return;
    }
    if (StrUtil.isBlank(content)) {
        renderJson(Ret.fail().set("message", "评论内容不能为空"));
        return;
    } else {
        content = StrUtil.escapeHtml(content);
    }
    Boolean vCodeEnable = JPressOptions.isTrueOrEmpty("article_comment_vcode_enable");
    if (vCodeEnable != null && vCodeEnable == true) {
        if (validateCaptcha("captcha") == false) {
            renderJson(Ret.fail().set("message", "验证码错误").set("errorCode", 2));
            return;
        }
    }
    if (WordFilterUtil.isMatchedFilterWords(content)) {
        renderJson(Ret.fail().set("message", "非法内容,无法发布评论信息"));
        return;
    }
    Article article = articleService.findById(articleId);
    if (article == null) {
        renderFailJson();
        return;
    }
    if (!article.isCommentEnable()) {
        renderJson(Ret.fail().set("message", "该文章的评论功能已关闭"));
        return;
    }
    Boolean commentEnable = JPressOptions.isTrueOrEmpty("article_comment_enable");
    if (commentEnable == null || !commentEnable) {
        renderJson(Ret.fail().set("message", "评论功能已关闭"));
        return;
    }
    Boolean unLoginEnable = optionService.findAsBoolByKey("article_comment_unlogin_enable");
    if (unLoginEnable == null || !unLoginEnable) {
        if (getLoginedUser() == null) {
            renderJson(Ret.fail().set("message", "未登录用户不能评论").set("errorCode", 9));
            return;
        }
    }
    ArticleComment comment = new ArticleComment();
    comment.setArticleId(articleId);
    comment.setContent(content);
    comment.setAuthor(nickname);
    comment.setPid(pid);
    comment.setEmail(email);
    comment.setWechat(wechat);
    comment.setQq(qq);
    User conmmentUser = getLoginedUser();
    if (conmmentUser != null) {
        comment.setUserId(conmmentUser.getId());
        comment.setAuthor(conmmentUser.getNickname());
    }
    Boolean reviewEnable = optionService.findAsBoolByKey("article_comment_review_enable");
    if (reviewEnable != null && reviewEnable == true) {
        comment.setStatus(ArticleComment.STATUS_UNAUDITED);
    }
    else {
        comment.setStatus(ArticleComment.STATUS_NORMAL);
    }
    articleService.doIncArticleCommentCount(articleId);
    commentService.saveOrUpdate(comment);
    if (pid != null) {
        commentService.doIncCommentReplyCount(pid);
        ArticleComment parent = commentService.findById(pid);
        if (parent != null && parent.isNormal()) {
            comment.put("parent", parent);
        }
    }
    Ret ret = Ret.ok().set("code", 0);
    Map<String, Object> paras = new HashMap<>();
    paras.put("comment", comment);
    paras.put("article", article);
    if (conmmentUser != null) {
        paras.put("user", conmmentUser.keepSafe());
        comment.put("user", conmmentUser.keepSafe());
    }
    renderHtmltoRet("/WEB-INF/views/commons/article/defaultArticleCommentItem.html", paras, ret);
    ArticleNotifyKit.notify(article, comment, conmmentUser);
    if (isAjaxRequest()) {
        renderJson(ret);
    } else {
        redirect(getReferer());
    }
}

index() 方法不同,postComment() 不是查询方法而是创建方法,所以多出了大量信息验证的步骤。具体步骤如下:

  • 读取 HTTP 请求中的文章 ID、用户 ID 等内容
  • 判断文章 ID、评论内容、用户验证码进行准确性验证
  • 拒绝非法内容的发布
  • 拒绝评论已经关闭的文章、未登录用户的请求
  • 创建 Comment 对象并设置参数
  • 判断是否需要管理员审核
  • 通过 CommentServicedoIncCommentReplyCount() 方法更新文章的评论数量
  • 更新网页内容,刷新网页

其它 Controller 的内容更少,所以这里不做过多分析。

ucenter

ucenter 包下只有一个 ArticleUCenterController 类,这个类用于处理普通用户在用户中心中对自己的文章进行增删改查。该类下除了类似于前文的 index() 方法,还有 doDel()write()doWriteSave()comment()favorite()doDelFavorite()doCommentDel(),很显然除了删除操作,其它操作的代码应当与前文分析的发布评论无异。查看后果然如此,所以我们这里只分析删除方法。

doDel()

doDel() 是上文描述的最简单的一个删除方法,当然肯定也是最具代表性的一个方法,因此我们直接来看一下它的代码。

public void doDel() {
    Long id = getIdPara();
    if (id == null) {
        renderFailJson();
        return;
    }
    Article article = articleService.findById(id);
    if (article == null) {
        renderFailJson();
        return;
    }
    if (notLoginedUserModel(article)) {
        renderJson(Ret.fail().set("message", "非法操作"));
        return;
    }
    renderJson(articleService.deleteById(id) ? OK : FAIL);
}

其实也没什么难度,具体步骤大概为:

  • 通过请求读出 ID 数据
  • 判断文章和 ID 是否为空
  • 判断用户是否登录,未登录则拒绝操作
  • 删除文章

结语

以上是 module-article-web 部分所有的 Controller 分析,当然也是 JPress 框架的 module-article 的全部分析,完结撒花。

上一篇:python logging模块打印log到指定文件


下一篇:【盘点】深度学习最常用到的20个Python库