2021SC@SDUSC
前言
在对 Controller 做最后的分析之前,我首先厘清了 module-article-web
模块下其它部分对该模块的影响,发现 directive
、interceptor
、kit
和 wechat
均为工具类,而 webapp
则为前端界面,整个模块最核心的部分仍为 MVC 架构的 Controller 部分。
在前文中我谈到了 module-article
模块下的 Controller 模块,并就其中 admin
和 api
做了详细分析,本文将继续对与 _ArticleController
同级的 Controller front
和 ucenter
进行分析。本文将是最后一篇对 JPress 框架 module-article
模块的分析。
front
front
目录下放置的是与前端 URL 映射、前端所能获得的数据相关的 Controller,是 MVC 架构中 View 部分的必备组件。
在该目录下一共有五个 Controller,分别是:
-
ArticleCategoryController
,获取文章类别 -
ArticleController
,获取文章详细信息 -
ArticlesController
,获取所有文章 -
ArticleSearchController
,搜索文章 -
ArticleTagController
,获取文章标签
这五个 Controller 在代码层面上大体保持高度相似,因此这里我们选择其中一个具有代表性、也是代码最多的 ArticleController
进行代码层面上的拆解与分析。
ArticleController
对应前文所分析的 _ArticleController
,这里的 ArticleController
属于用户端获取文章的 Controller,而前者则是管理员的,意即后者在获取文章详细内容之前应当是有权限限制的。
该 Controller 与前文大多数 Controller 基本无异,其私有成员变量 OptionService
、UserFavoriteService
均不是重点,而最核心的一共三个方法 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 对象并设置参数
- 判断是否需要管理员审核
- 通过
CommentService
的doIncCommentReplyCount()
方法更新文章的评论数量 - 更新网页内容,刷新网页
其它 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
的全部分析,完结撒花。