小Hub领读:

继续我们的eblog,今天来完成博客文章收藏,用户中心的设置!


项目名称:eblog

项目 Git 仓库:https://github.com/MarkerHub/eblog(给个 star 支持哈)

项目演示地址:https://markerhub.com:8082

前几篇项目讲解文章:

1、(eblog)Github 上最值得学习的 Springboot 开源博客项目!

2、(eblog)小 Hub 手把手教你如何从 0 搭建一个开源项目架构

3、(eblog)整合Redis,以及项目优雅的异常处理与返回结果封装

4、(eblog)用Redis的zset有序集合实现一个本周热议功能

5、(eblog)自定义Freemaker标签实现博客首页数据填充


1、用户中心-1

上一篇文章中,我们已完成了用户的登录与注册,现在我们来完成以下登录之后可以操作的事情,比如进入用户中心等

我的主页

首先我们来看看我的主页:

这是点击用户主页之后的显示效果,上面是用户的基本信息,左边是最近发表的文章,右边是最近的操作等(评论,发表等),最近操作部分我们暂时就不弄了,课下大家自行完成。

所以这个页面要完成很简单,只需要把用户的基本信息,和最近的文章传到页面就行了:

  • com.example.controller.UserController

@Controller

public class UserController extends BaseController{

@RequestMapping("/user/{id:\\d*}")

public String home(@PathVariable Long id) {

User user = userService.getById(id);

user.setPassword(null);

//30天内容的文章

Date date30Before = DateUtil.offsetDay(new Date(), -30).toJdkDate();

List<Post> posts = postService.list(new QueryWrapper<Post>()

.eq("user_id", id)

.ge("created", date30Before)

.orderByDesc("created"));

req.setAttribute("user", user);

req.setAttribute("posts", posts);

return "user/home";

}

}

至于页面,就是直接展示数据,就不再多说了!~

用户中心

点击用户中心后,是个tab标签,分为我发的贴和我收藏的贴

我发的贴

先来看看我发的贴,这个直接安装userId查询post就完成了

  • com.example.controller.CenterController

@GetMapping("")

public String index() {

Page page = getPage();

log.info("-------------->进入个人中心");

QueryWrapper<Post> wrapper = new QueryWrapper<Post>().eq("user_id", getProfileId())

.orderByDesc("created");

IPage<Post> pageData = postService.page(page, wrapper);

req.setAttribute("pageData", pageData);

return "center/index";

}

好像没啥知识点说的~

我收藏的贴

我收藏的贴,因为涉及到关联表

  • UserCollection

  • Post

所以,需要关联查询

@GetMapping("/collection")

public String collection() {

Page page = getPage();

QueryWrapper queryWrapper = new QueryWrapper<>().eq("u.user_id", getProfileId()).orderByAsc("u.created");

IPage<Post> pageData = collectionService.paging(page, queryWrapper);

req.setAttribute("pageData", pageData);

return "center/collection";

}

collectionService.paging的最终mapper是这样的:

<select id="selectPosts" resultType="com.example.entity.Post">

select *

from user_collection u

left join post p on u.post_id = p.id

${ew.customSqlSegment}

select>

所以我们的条件是u.userid。用u.来区分是哪个表的userid。 然后其他的页码的渲染,就直接是个宏搞定:

style="text-align: center">

@page>

基本设置

我的资料

ok,上面的都比较简单,我们来看看基本设置,这里涉及到表单提交和头像修改等。

我的资料和密码修改都只是简单的表单提交,上次我们说过,表单提交都已经帮我们封装好的了,我们只需要返回的时候告诉表单提交成功之后的跳转链接是啥,所以我的资料提交是这样的:

  • com.example.controller.CenterController

@GetMapping("/setting")

public String setting() {

User user = userService.getById(getProfileId());

user.setPassword(null);

req.setAttribute("user", user);

return "center/setting";

}

@ResponseBody

@PostMapping("/setting")

public Result postSetting(User user) {

if(StringUtils.isEmpty(user.getUsername())){

return Result.fail("用户名不能为空");

}

User tempUser = userService.getById(getProfileId());

// tempUser.setEmail(user.getEmail());

tempUser.setUsername(user.getUsername());

tempUser.setGender(user.getGender());

tempUser.setSign(user.getSign());

boolean isSucc = userService.updateById(tempUser);

if(isSucc) {

//更新shiro的信息

AccountProfile profile = getProfile();

profile.setUsername(user.getUsername());

profile.setGender(user.getGender());

}

return isSucc ? Result.succ("更新成功", null, "/center/setting"): Result.fail("更新失败");

}

资料提交之后涉及到shiro登录信息的修改,所以在updateById完成之后,我们获取到shiro的用户信息 AccountProfile profile = getProfile();,然后直接重新set搞定。 前端需要注意的是,我在确认按钮那里加了alert=*"true"*

class="layui-btn" key="set-mine" lay-filter="*" lay-submit alert="true">确认修改

头像

头像设计到图片上传,

  • com.example.controller.CenterController

@ResponseBody

@PostMapping("/upload")

public Result upload(@RequestParam(value = "file") MultipartFile file,

@RequestParam(name="type", defaultValue = "avatar") String type) {

if(file.isEmpty()) {

return Result.fail("上传失败");

}

// 获取文件名

String fileName = file.getOriginalFilename();

log.info("上传的文件名为:" + fileName);

// 获取文件的后缀名

String suffixName = fileName.substring(fileName.lastIndexOf("."));

log.info("上传的后缀名为:" + suffixName);

// 文件上传后的路径

String filePath = constant.getUploadDir();

if ("avatar".equalsIgnoreCase(type)) {

fileName = "/avatar/avatar_" + getProfileId() + suffixName;

} else if ("post".equalsIgnoreCase(type)) {

fileName = "/post/post_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + suffixName;

}

File dest = new File(filePath + fileName);

// 检测是否存在目录

if (!dest.getParentFile().exists()) {

dest.getParentFile().mkdirs();

}

try {

file.transferTo(dest);

log.info("上传成功后的文件路径未:" + filePath + fileName);

String path = filePath + fileName;

String url = constant.getUploadUrl() + fileName;

log.info("url ---> {}", url);

User current = userService.getById(getProfileId());

current.setAvatar(url);

userService.updateById(current);

//更新shiro的信息

AccountProfile profile = getProfile();

profile.setAvatar(url);

return Result.succ(url);

} catch (IllegalStateException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return Result.succ(null);

}

上面的逻辑其实还是很简单的,首先获取到图片,重命名,并保存到指定位置,然后更新user,在更新shiro的头像信息。 文件复制就这一行代码重要

file.transferTo(dest);

其他都比较固定了。 另外,因为我们的图片位置是存放在根目录下的:

这涉及到一些静态资源的加载问题,所以我们需要在mvc配置中添加这个静态资源的位置,

@Configuration

public class WebMvcConfig implements WebMvcConfigurer {

@Autowired

Constant constant;

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

registry.addResourceHandler("/upload/avatar/**")

.addResourceLocations("file:///" + constant.getUploadDir() + "/avatar/");

}

}

上面的WebMvcConfig重写了addResourceHandlers方法,把upload的文件夹读取进去了,然后Constant 是个加载的常量,我提取到了配置文件中

  • com.example.common.lang.Constant

@Data

@Component

public class Constant {

@Value("${file.upload.dir}")

private String uploadDir;

@Value("${file.upload.url}")

private String uploadUrl;

}

然后配置文件是这样的:

  • application.yml

file:

upload:

dir: ${user.dir}/upload

url: http://localhost:8080/upload

user.dir表示用户根路径,当然了,现在图片我是直接存在了本地硬盘上,同学们可以自行拓展上传到七牛云等云盘上。 所以上面的图片上的avatar5.png,我们的访问路径是:http://localhost:8080/upload/avatar/avatar5.png

密码

密码修改也只是一个简单的form表单提交

@ResponseBody

@PostMapping("/repass")

public Result repass(String nowpass, String pass, String repass) {

if(!pass.equals(repass)) {

return Result.fail("两次密码不相同");

}

User user = userService.getById(getProfileId());

String nowPassMd5 = SecureUtil.md5(nowpass);

if(!nowPassMd5.equals(user.getPassword())) {

return Result.fail("密码不正确");

}

user.setPassword(SecureUtil.md5(pass));

userService.updateById(user);

return Result.succ("更新成功", null, "/center/setting");

}

貌似没啥说的?

我的消息

展示消息

我的消息包括两种,一个是系统消息,一个是别人评论了我的文章,或者收藏了我的文章等类型。

这里我就搞了两种,com.example.entity.UserMessage的type设置了2中类型:

  • type消息类型,1评论消息,2系统消息

页面展示的时候我们需要根据type的类型来选择要展示的数据样式,我们先来看下实体的结构

CREATE TABLE `user_message` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT,

  `from_user_id` bigint(20) DEFAULT NULL COMMENT '发送消息的用户ID',

  `to_user_id` bigint(20) NOT NULL COMMENT '接收消息的用户ID',

  `post_id` bigint(20) DEFAULT NULL COMMENT '消息可能关联的帖子',

  `comment_id` bigint(20) DEFAULT NULL COMMENT '消息可能关联的评论',

  `content` text,

  `type` tinyint(2) DEFAULT NULL COMMENT '消息类型,1评论消息,2系统消息',

  `created` datetime NOT NULL,

  `modified` datetime DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

这个表里面有很多广关联的id,那么在页面展示的时候我们需要展示评论详情,用户来源,评论文章标题等的时候就需要关联很多表,所以在写sql的时候我们采用了另一种方式来完成,我们先来看看vo:

@Data

public class MessageVo extends UserMessage {

private String toUserName;

private String fromUserName;

private String postTitle;

private String commentContent;

}

上面就是我们的数据需要展示的内容,如何才能让sql都查出来呢,我们来看看sql:

id="selectMessages" resultType="com.example.vo.MessageVo">

SELECT m.*,

( SELECT username FROM `user` WHERE id = m.to_user_id ) AS toUserName,

( SELECT username FROM `user` WHERE id = m.from_user_id ) AS fromUserName,

( SELECT title FROM `post` WHERE id = m.post_id ) AS postTitle,

( SELECT content FROM `comment` WHERE id = m.comment_id ) AS commentContent

FROM `user_message` m

${ew.customSqlSegment}

可以看到上面的内容,我在select的内容中又select了一遍,比如根据touserid 查出toUserName等。所以我们写起来就简单了。 编码的时候我们就直接通过这个sql查询出结果:

@GetMapping("/message")

public String message() {

Page<UserMessage> page = getPage();

QueryWrapper wrapper = new QueryWrapper<UserMessage>()

.eq("to_user_id", getProfileId())

.orderByDesc("created");

IPage<MessageVo> pageData = userMessageService.paging(page, wrapper);

req.setAttribute("pageData", pageData);

return "center/message";

}

页面端根据type来展示数据

href="/user/${mess.fromUserId}" target="_blank">${mess.fromUserName}评论了您的文章 target="_blank" href="${base}/post/${mess.postId}">${mess.postTitle}

#if>

系统消息:${mess.content}

#if>

删除消息

删除的js原框架已经帮我们完成了,所以我们只需要填充数据,特别是data-id

data-id="${mess.id}">

删除逻辑如下:

@ResponseBody

@PostMapping("/message/remove")

public Result removeMsg(Long id, boolean all) {

QueryWrapper<UserMessage> warapper = new QueryWrapper<UserMessage>()

.eq("to_user_id", getProfileId())

.eq(!all,"id", id);

//只能删除自己的消息

boolean res = userMessageService.remove(warapper);

return res ? Result.succ("操作成功", null, "/center/message") : Result.fail("删除失败");

}

  • static/res/mods/user.js

上面的js有些链接需要修改下,大家注意一下

2、用户中心-2

上面的用户中心,我们完成了一些数据的展示,现在我们来床造一些数据,比如发表文章,评论等。这就涉及到一些登录后的权限问题,未登录操作报错提示等问题,我们在码代码的过程再作细节的调整。

发布编辑博客

我们先来看看评论的页面

发布博客分为新发布和编辑发布,一般来说我们根据是否有传博客id过来判断,如果有id,那么我们就查询出来,然后回显数据提交更新,因为页面都是一样的,所以,新发布、编辑我们用了一个方法,从这个页面可以看到,我们需要的数据有博客实体,还有分类列表,还有个验证码。

@GetMapping("/post/edit")

public String edit() {

String id = req.getParameter("id");

Post post = new Post();

if(!StringUtils.isEmpty(id)) {

post = postService.getById(Long.valueOf(id));

Assert.notNull(post, "文章已被删除!");

Long profileId = getProfileId();

Assert.isTrue(post.getUserId()==getProfileId(), "无权限编辑此文章!");

}

List<Category> categories = categoryService.list(new QueryWrapper<Category>()

.orderByDesc("order_num"));

req.setAttribute("post", post);

req.setAttribute("categories", categories);

return "post/edit";

}

这个就是我们跳转到编辑页面的controller,从里面可以看到,我们通过id是否为空来判断是否是编辑,如果是编辑,我们还用断言来判断一下文章是否已经删除,或者是否是自己发布的文章等。

延伸:断言与异常

这里有个细节,当我们是新发布文章时候,那么我们传过去的post就是个new Post(),属性都是空的,所以我们在页面中使用${post.title}时候freemaker会报错,这时候我们需要解决这个问题,让他不报错,解决方法很简单,我们只需要在配置文件中配置好freemaker在这种情况下不报错就行了:

spring:

freemarker:

cache: false

settings:

classic_compatible: true

加上了classic_compatible=true之后,那么我们在刚才属性为空的情况下我们就不会报错了。 

好了,那么文章填充好内容之后我们提交,<form *action="/post/submit" *method=**"post">,我们写一个submit方法,这里面我们需要考虑几个问题:

  • Post必填字段是否文章和符合要求

  • 如果是编辑,那么是否是自己的文章

  • 认证码问题

好,接下来我们一一解决,首先来看下验证码问题,页面中的“人类认证”,应该是是类似于注册页面的验证码问题来的,不过这里我就直接跳过了,直接写死了1+1=2的答案,大家回去自行加上验证码的验证,逻辑其实简单,生成验证码放到session中,然后submit时候比较session中的认证码和提交的验证码对比即可,和注册验证码一样的逻辑。

验证码我写死了:

if(!"2".equals(vercode)) {

return Result.fail("人类认证错误!");

}

接下来看看post字段问题,我们以前讲过一个框架hibernate validatior,刚好springboot自带集成了这个框架,那么我们直接使用,使用方法是找到post实体,然后在字段上添加属性的认证逻辑,比如:

/**

* 标题

*/

@Length(min = 4,max = 32, message = "标题长度不能超过最少4位,最长32位")

@NotBlank(message = "标题不能为空")

private String title;

/**

* 内容

*/

@NotBlank(message = "内容不能为空")

private String content;

@NotNull(message = "分类不能为空")

private Long categoryId;

然后submit方法中参数验证post,需要注入到@Valid注解和BindingResult验证结果。所以整体代码是这样的:

@ResponseBody

@Transactional

@PostMapping("/post/submit")

public Result submit(@Valid Post post, BindingResult bindingResult, String vercode) throws JsonProcessingException {

if(!"2".equals(vercode)) {

return Result.fail("人类认证错误!");

}

if(bindingResult.hasErrors()) {

return Result.fail(bindingResult.getFieldError().getDefaultMessage());

}

if(post.getId() == null) {

post.setUserId(getProfileId());

post.setModified(new Date());

post.setCreated(new Date());

post.setCommentCount(0);

post.setEditMode(null);

post.setLevel(0);

post.setRecommend(false);

post.setViewCount(0);

post.setVoteDown(0);

post.setVoteUp(0);

} else {

Post tempPost = postService.getById(post.getId());

Assert.isTrue(tempPost.getUserId()==getProfileId(), "无权限编辑此文章!");

}

postService.saveOrUpdate(post);

return Result.succ("发布成功", null, "/post/" + post.getId());

}

可以看到submit(@Valid Post post, BindingResult bindingResult, String vercode)参数的使用,bindingResult.hasErrors()就是判断提交的post是否符合验证逻辑的。 后面说到的是否是自己的文章,只需要比较一下post.userId和当前登录的用户id即可,直接使用断言判断。

那么这里我来延伸一个问题,断言的用处。我们知道很多地方我们需要做一下校验和判断,从而知道数据是否符合预期的期望,如果不符合那么断言就会抛出异常。

那么我们需要对断言抛出的错误进行一番处理,有两种情况,同步方法或异步方法,当同步的controller时候,我们跳转到异常页面,并把断言的错误提示展示出来。那么异步的话,我们需要返回json数据,弹窗提示断言的错误提示。

在之前,我们已经做过一个全局异常处理,我发现写得不是很好,对我们断言的错误处理不够好,所以这里我调整了一下,我们通过req的header中是否包含X-Requested-With来判断是否是异步的方法。然后通过获取resp.getWriter()来写入json数据。

  • com.example.common.exception.GlobalExceptionHandler

@Slf4j

@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(value = Exception.class)

public ModelAndView defaultErrorHandler(HttpServletRequest req, HttpServletResponse resp, Exception e) {

log.error("------------------>捕捉到全局异常", e);

if (req.getHeader("accept").contains("application/json") || (req.getHeader("X-Requested-With")!= null

&& req.getHeader("X-Requested-With").contains("XMLHttpRequest") )) {

try {

System.out.println(e.getMessage());

Result result = Result.fail(e.getMessage(), "some error data");

resp.setCharacterEncoding("utf-8");

PrintWriter writer = resp.getWriter();

writer.write(JSONUtil.toJsonStr(result));

writer.flush();

} catch (IOException i) {

i.printStackTrace();

}

return null;

}

if(e instanceof HwException) {

//...

}

ModelAndView mav = new ModelAndView();

mav.addObject("exception", e);

mav.addObject("message", e.getMessage());

mav.addObject("url", req.getRequestURL());

mav.setViewName("error");

return mav;

}

}

删除博客

上面我们已经可以发布和编辑文章,我们可以在用户中心的我发布的帖子中可以看到自己发布的文章,接下来我们来弄一下删除博客,同样道理,需要做一下简单校验

  • 帖子是否存在

  • 是否是自己的帖子

  • 删除与帖子相关的消息或收藏等

都比较简单,所以我就直接给出代码了,收藏功能我们虽然还没做,但是删除逻辑简单,所以我们直接写上去了。

@ResponseBody

@Transactional

@PostMapping("/post/delete")

public Result delete(Long id) {

Post post = postService.getById(id);

Assert.notNull(post, "该帖子已被删除");

Assert.isTrue(post.getUserId()==getProfileId(), "无权限删除此文章!");

postService.removeById(id);

// 删除相关消息、收藏等

userMessageService.removeByMap(MapUtil.of("post_id", id));

userCollectionService.removeByMap(MapUtil.of("post_id", id));

return Result.succ("删除成功", null, "/center");

}

大家主题调整一下js的url,原本的url不是/post/delete的,注意改过来~

发表删除评论

渲染

接下来我们看下评论的功能,上一次我们已经能把文章的评论功能展示出来了,但是有个bug,就是一些表情和图片不能渲染出来,我们只是把内容原原本本展示出来而已,基于layui的这个编辑器,我们还要做下渲染,我们先来完成这部分然后再作评论功能,其实简单,模板中已经给出了提示,如果我们用的是layui自带的编辑器的话,我们需要加上那段代码:

然后又因为layui加载模块的js,我们写在了layout.ftl模板上了,所以这里我们等页面加载完成之后我们再渲染,这样之前的js已经渲染完成了。我们就用到了jq的$(function () {});。然后重要的就是othis.html(fly.content(html));渲染代码了。

我们看到的效果是这样的: 

好了,上面的div的类是detail-body都已经得以渲染,包括文章内容等部分。

发布

评论发布其实涉及到的东西还是挺多的,我们先来梳理一下

  • 一下简单逻辑判断

  • 文章的评论数量加一

  • 侧边栏的本周热议重新排行

  • 通知文章作者有人评论了

  • 通知@的用户有人回复了你的评论

1、一下简单逻辑判断,其实就是判断内容是否为空等,没啥说的,直接断言判断

@ResponseBody

@Transactional

@PostMapping("/post/reply")

public Result reply(Long pid, Long parentId, String content) {

Assert.notNull(pid, "找不到对应文章!");

Assert.hasLength(content, "评论内容不能为空!");

Post post = postService.getById(pid);

Assert.isTrue(post != null, "该文章已被删除");

...

}

2、保存评论并文章评论数量加一

Comment comment = new Comment();

comment.setPostId(pid);

comment.setContent(content);

comment.setUserId(getProfileId());

comment.setCreated(new Date());

comment.setModified(new Date());

comment.setLevel(0);

comment.setVoteDown(0);

comment.setVoteUp(0);

commentService.save(comment);

// 评论数量加一

post.setCommentCount(post.getCommentCount() + 1);

postService.saveOrUpdate(post);

3、侧边栏的本周热议功能

关于这个功能我们一开始就做了,但是好像考虑有点不周全,只有评论加一,没有评论减一,因为后面删除评论就需要减一了,所以我们调整一下postService.incrZsetValueAndUnionForLastWeekRank这个方法,加上一个参数*boolean *isIncr来判断是增加还是减少。

大家注意一下所以调用了这个方法的地方都修改一下。

所以侧边栏的数量加一很简单了,我们直接调用这个方法就搞定了

//更新首页排版版的评论数量

postService.incrZsetValueAndUnionForLastWeekRank(comment.getPostId(), true);

4、通知作者

用户中心的我的消息就是用来查看消息的,我们还要区分消息类型来展示不同的样式。我们先把消息保存起来,但只是保存起来其实是不够的,我们学过websocket,但有评论的时候我们应该实时通知作者,这怎么做到呢,下一次作业我们会做好调整。我们先把消息保存起来。

// 通知作者

UserMessage message = new UserMessage();

message.setPostId(pid);

message.setCommentId(comment.getId());

message.setFromUserId(getProfileId());

message.setToUserId(post.getUserId());

message.setType(1);

message.setContent(comment.getContent());

message.setCreated(new Date());

userMessageService.save(message);

5、通知被@的用户

当我们点击评论的回复按钮时候,评论的输入框中就会出现@当前评论的用户名称,所以我们发布的评论需要告知这个用户,根据@的特点,我们提取@与空格之间的用户名称,然后搜索出来再报错消息。

// 通知@的人

if(content.startsWith("@")) {

String username = content.substring(1, content.indexOf(" "));

System.out.println(username);

QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", username);

User user = userService.getOne(wrapper);

if(user != null) {

UserMessage message2 = new UserMessage();

message.setPostId(pid);

message2.setCommentId(comment.getId());

message2.setFromUserId(getProfileId());

message2.setToUserId(user.getId());

message2.setType(3);

message2.setContent(comment.getContent());

message2.setCreated(new Date());

userMessageService.save(message2);

}

}

String username = content.substring(1, content.indexOf(" "));就是提取用户名称来的,那么这里又涉及到一个问题,就是用户昵称的唯一性问题,所以我们需要修改一下注册的时候,我们也要加上昵称唯一性的校验。

  • com.example.service.impl.UserServiceImpl#register

User po = this.getOne(new QueryWrapper<User>().eq("email", user.getEmail()).or().eq("username", user.getUsername()));

if(po != null) {

return Result.fail("邮箱或昵称已被注册");

}

注意同步一下代码~,其实用户中心的修改用户昵称那里也需要加上唯一性校验,大家自行完成。另外,我还在数据库中加了唯一索引,保证字段的唯一。 

上面的方法,我们后期还需要调整,现在展示先这样做。

删除

评论的删除也是差不多的逻辑

  • 简单校验

  • 评论删除

  • 评论数量减一

  • 本周热议重新排行

直接给出代码了,有了添加的代码逻辑,相信对删除逻辑也应该熟悉才行。

@ResponseBody

@Transactional

@PostMapping("/post/jieda-delete/")

public Result reply(Long id) {

Assert.notNull(id, "评论id不能为空!");

Comment comment = commentService.getById(id);

Assert.notNull(comment, "找不到对应评论!");

if(comment.getUserId() != getProfileId()) {

return Result.fail("不是你发表的评论!");

}

commentService.removeById(id);

// 评论数量减一

Post post = postService.getById(comment.getPostId());

post.setCommentCount(post.getCommentCount() - 1);

postService.saveOrUpdate(post);

//评论数量减一

postService.incrZsetValueAndUnionForLastWeekRank(comment.getPostId(), false);

return Result.succ(null);

}

未登录提示 什么是未登录提示,之前我们做过未登录跳转到登录页面,其实我们依靠的都是shiro框架。一些涉及到用才能操作的就需要登录之后才能完成,回顾一下我们学过的shiro内容,有两种方法:

  • 配置式:在com.example.config.ShiroConfig#shiroFilter配置hashMap.put("/post/edit""auth");

  • 或者注解形式在对应方法上使用@RequiresAuthentication

auth是过滤器的别名,具体其实是AuthFilter。

所以大家记得,一些需要登录才能操作的方法记得用这两种形式标记,不然可能用getProfileId()就会报错。毕竟需要登录之后才能操作的内容!

 给eblog一个star,感谢支持哈

layui设置按钮不可点击_(eblog)7、博客发布收藏、用户中心的设置相关推荐

  1. kotlin设置按钮不可点击_全彩LED显示屏软件空点功能如何设置、使用?

    全彩LED显示屏的显示功能得以实现,所使用的软件功不可没.可以说是显示屏所展现的一切的效果和特性,都得归功于软件和显示屏设备.全彩LED显示屏软件空点功能如何设置.使用,你们知道吗?下面景信科技小编为 ...

  2. kotlin设置按钮不可点击_电脑护眼设置开启教程

    随着科技的进步,电脑已成为我们不可或缺的工作学习工具.对于长时间对着电脑的白领来说,如何保护眼睛成了重中之重.其实,我们电脑上早已有了护眼模式.今天小编就给大家介绍一下开启它的方法,一起来看看吧. 一 ...

  3. kotlin设置按钮不可点击_如何设置iphone手机铃声

    如何设置iphone手机铃声? 设置iphone手机铃声必须用iTunes软件,用iTunes软件将歌曲格式转换成m4r格式,才能在iphone手机里设置成铃声. 工具/原料 iTunes软件 iph ...

  4. kotlin设置按钮不可点击_跟编程探索家学APP开发:设置APP首页的基础结构

    我是编程探索家阿源.大家如果想继续跟着学做一款APP(安卓+苹果双版本),请关注西瓜视频或者头条号:编程探索家. 先看一下我们这个系列教程需要做出来的APP效果: 1. 打开Android Studi ...

  5. 设置html按钮点击事件无效果,css怎么设置按钮不能点击?

    css怎么设置按钮不能点击?下面本篇文章就来给大家介绍一下使用CSS设置按钮不能点击的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. 想要按钮不能点击可以通过设置按钮点击事件失 ...

  6. ztree树封装 json实例_小白7天入门PHP Web开发 - Day 6[下](综合)个人博客实例讲解用户数据的存储...

    <小白7天入门PHP Web开发>系列文章,面向单纯善良的完全不懂Web开发编程的入门速成课程,小白们如果感兴趣可以研读此系列文章,也可以连线提问.各路大神有何指教还请指点一二.希望各路大 ...

  7. cadence spb 16.5 破解过程实例和使用感受_赤松子耶_新浪博客

    cadence spb 16.5 破解过程实例和使用感受_赤松子耶_新浪博客 Cadence Allegro16.5详细安装具体的步骤 1.下载SPB16.5下来后,点setup.exe,先安装第一项 ...

  8. Linux 共享库:LD_LIBRARY_PATH 与ld.so.conf_爱过了就好_新浪博客

    Linux 共享库:LD_LIBRARY_PATH 与ld.so.conf_爱过了就好_新浪博客 Linux 共享库:LD_LIBRARY_PATH 与ld.so.conf     (2009-07- ...

  9. 深入理解计算机系统 -资料整理 高清中文版_在所不辞的博客-CSDN博客_深入理解计算机系统第四版pdf

    深入理解计算机系统 -资料整理 高清中文版_在所不辞的博客-CSDN博客_深入理解计算机系统第四版pdf

最新文章

  1. 升级php7_PHP5.9 升级到PHP7 遇到的一些坑(phpfpm 图解)
  2. 使用消息队列实现分布式事务-公认较为理想的分布式事务解决方案(转)
  3. springboot-web开发(静态资源)
  4. mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法
  5. linux修改容器内的mysql端口_Linux系统下修改phpstudy集成环境中的MySQL端口号的步骤...
  6. R7-3 出租 (20 分)
  7. S3C DMA使用方法,2410-2440 dma介绍
  8. 软件随想--写牛B的代码
  9. 数值分析(7)-正交多项式
  10. oracle如何验证导入完整,关于Oracle 9i导入/导出效果的测试报告
  11. 电商数据分析Excel案例
  12. 信噪比(dB)换算公式
  13. 外贸建站五大必备要领
  14. 如何将计算机c盘分区,无损调节电脑C盘分区,让C盘空间不再尴尬(超实用)
  15. PHP 第三方调用 UC_Center用户登录认证
  16. vuex技术多组件共享数据-vuex模块化+namespace
  17. 通过Java实现恩尼格玛密码机
  18. ads1115与树莓派
  19. redis连接超时,本地连接不上服务器上的redis
  20. i58400升级可以换什么cpu_宝贝标题关键词顺序可以换吗?关键词顺序对标题有什么影响?...

热门文章

  1. 实战:SQL sever如何实现同一列上下行运算?
  2. 【转】msyql使用-用户创建/权限配置
  3. Ajax请求Session超时的解决办法:拦截器 + 封装jquery的post方法
  4. android使用tabhost实现导航
  5. 【转】 UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合
  6. 我心目中理想的开源软件
  7. Git 初始化及仓库创建及操作
  8. 微信小程序1. Forgot to add page route in app.json. 2. Invoking Page() in async task.
  9. smokeping部署安装
  10. UI层调用WCF服务实例(源码)