(eblog)4、博客发布收藏、用户中心的设置

小助手 1年前 ⋅ 2855 阅读

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&lt;&gt;().eq(&quot;u.user_id&quot;, getProfileId()).orderByAsc(&quot;u.created&quot;);
IPage&lt;Post&gt; pageData = collectionService.paging(page, queryWrapper);

req.setAttribute(&quot;pageData&quot;, pageData);

return &quot;center/collection&quot;;

} 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.user_id。用u.来区分是哪个表的user_id。 然后其他的页码的渲染,就直接是个宏搞定:

<div style="text-align: center">
    <@page pageData></@page>
</div>

基本设置

我的资料

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

图片

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

  • com.example.controller.CenterController
    @GetMapping("/setting")
    public String setting() {
        User user = userService.getById(getProfileId());
        user.setPassword(null);
    req.setAttribute(&quot;user&quot;, user);
    return &quot;center/setting&quot;;
}

@ResponseBody
@PostMapping(&quot;/setting&quot;)
public Result postSetting(User user) {

    if(StringUtils.isEmpty(user.getUsername())){
        return Result.fail(&quot;用户名不能为空&quot;);
    }

    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(&quot;更新成功&quot;, null, &quot;/center/setting&quot;): Result.fail(&quot;更新失败&quot;);
}

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

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

头像

头像设计到图片上传,

  • 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(&quot;上传失败&quot;);
}

// 获取文件名
String fileName = file.getOriginalFilename();
log.info(&quot;上传的文件名为:&quot; + fileName);
// 获取文件的后缀名
String suffixName = fileName.substring(fileName.lastIndexOf(&quot;.&quot;));
log.info(&quot;上传的后缀名为:&quot; + suffixName);
// 文件上传后的路径
String filePath = constant.getUploadDir();

if (&quot;avatar&quot;.equalsIgnoreCase(type)) {
    fileName = &quot;/avatar/avatar_&quot; + getProfileId() + suffixName;

} else if (&quot;post&quot;.equalsIgnoreCase(type)) {
    fileName = &quot;/post/post_&quot; + 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(&quot;上传成功后的文件路径未:&quot; + filePath + fileName);

    String path = filePath + fileName;
    String url = constant.getUploadUrl() + fileName;

    log.info(&quot;url ---&gt; {}&quot;, 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(&quot;/upload/avatar/**&quot;)
            .addResourceLocations(&quot;file:///&quot; + constant.getUploadDir() + &quot;/avatar/&quot;);
}

}

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

  • com.example.common.lang.Constant
@Data
@Component
public class Constant {
@Value(&quot;${file.upload.dir}&quot;)
private String uploadDir;

@Value(&quot;${file.upload.url}&quot;)
private String uploadUrl;

}

然后配置文件是这样的:

  • application.yml
file:
  upload:
    dir: ${user.dir}/upload
    url: http://localhost:8080/upload    

user.dir表示用户根路径,当然了,现在图片我是直接存在了本地硬盘上,同学们可以自行拓展上传到七牛云等云盘上。 所以上面的图片上的avatar_5.png,我们的访问路径是:http://localhost:8080/upload/avatar/avatar_5.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(&quot;密码不正确&quot;);
}

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

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

}

貌似没啥说的?

我的消息

展示消息

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

这里我就搞了两种,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:

<select 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的内容中又select了一遍,比如根据to_user_id 查出toUserName等。所以我们写起来就简单了。 编码的时候我们就直接通过这个sql查询出结果:

@GetMapping("/message")
public String message() {
Page&lt;UserMessage&gt; page = getPage();

QueryWrapper wrapper = new QueryWrapper&lt;UserMessage&gt;()
        .eq(&quot;to_user_id&quot;, getProfileId())
        .orderByDesc(&quot;created&quot;);

IPage&lt;MessageVo&gt; pageData = userMessageService.paging(page, wrapper);

req.setAttribute(&quot;pageData&quot;, pageData);
return &quot;center/message&quot;;

}

页面端根据type来展示数据

<#if mess.type == 1>
    <a href="/user/${mess.fromUserId}" target="_blank"><cite>${mess.fromUserName}</cite></a>评论了您的文章<a target="_blank" href="${base}/post/${mess.postId}"><cite>${mess.postTitle}</cite></a>
</#if>
<#if mess.type == 2>
    系统消息:${mess.content}
</#if>
</blockquote>

删除消息

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

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

删除逻辑如下:

@ResponseBody
@PostMapping("/message/remove")
public Result removeMsg(Long id, boolean all) {
QueryWrapper&lt;UserMessage&gt; warapper = new QueryWrapper&lt;UserMessage&gt;()
        .eq(&quot;to_user_id&quot;, getProfileId())
        .eq(!all,&quot;id&quot;, id);

//只能删除自己的消息
boolean res = userMessageService.remove(warapper);

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

}

  • 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&lt;Category&gt; categories = categoryService.list(new QueryWrapper&lt;Category&gt;()
        .orderByDesc(&quot;order_num&quot;));

req.setAttribute(&quot;post&quot;, post);
req.setAttribute(&quot;categories&quot;, categories);
return &quot;post/edit&quot;;

}

这个就是我们跳转到编辑页面的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(!&quot;2&quot;.equals(vercode)) {
    return Result.fail(&quot;人类认证错误!&quot;);
}

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(), &quot;无权限编辑此文章!&quot;);
}

postService.saveOrUpdate(post);

return Result.succ(&quot;发布成功&quot;, null, &quot;/post/&quot; + 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(&quot;------------------&gt;捕捉到全局异常&quot;, e);

    if (req.getHeader(&quot;accept&quot;).contains(&quot;application/json&quot;)  || (req.getHeader(&quot;X-Requested-With&quot;)!= null
            &amp;&amp; req.getHeader(&quot;X-Requested-With&quot;).contains(&quot;XMLHttpRequest&quot;) )) {
        try {
            System.out.println(e.getMessage());
            Result result = Result.fail(e.getMessage(), &quot;some error data&quot;);

            resp.setCharacterEncoding(&quot;utf-8&quot;);
            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(&quot;exception&quot;, e);
    mav.addObject(&quot;message&quot;, e.getMessage());
    mav.addObject(&quot;url&quot;, req.getRequestURL());
    mav.setViewName(&quot;error&quot;);
    return mav;
}

}

删除博客

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

  • 帖子是否存在
  • 是否是自己的帖子
  • 删除与帖子相关的消息或收藏等

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

@ResponseBody
@Transactional
@PostMapping("/post/delete")
public Result delete(Long id) {
    Post post = postService.getById(id);
Assert.notNull(post, &quot;该帖子已被删除&quot;);
Assert.isTrue(post.getUserId()==getProfileId(), &quot;无权限删除此文章!&quot;);

postService.removeById(id);

// 删除相关消息、收藏等
userMessageService.removeByMap(MapUtil.of(&quot;post_id&quot;, id));
userCollectionService.removeByMap(MapUtil.of(&quot;post_id&quot;, id));

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

}

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

发表删除评论

渲染

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

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


    $(function () {
        layui.use(['fly', 'face'], function(){
            var $ = layui.$,fly = layui.fly;
            //如果你是采用模版自带的编辑器,你需要开启以下语句来解析。
            $('.detail-body').each(function(){
                var othis = $(this), html = othis.html();
                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, &quot;该文章已被删除&quot;);
...

}

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, &quot;评论id不能为空!&quot;);

Comment comment = commentService.getById(id);

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

if(comment.getUserId() != getProfileId()) {
    return Result.fail(&quot;不是你发表的评论!&quot;);
}
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()就会报错。毕竟需要登录之后才能操作的内容!


全部评论: 0

    我有话说: