博客给浏览者提供留言功能,可以是指出博主文章的错误,也可以是关于博客设计上的不足。未登录的用户可以进行匿名留言,匿名留言展示默认头像和默认昵称。
与数据库中留言表对应实体类设计如下:
package com.qianyucc.blog.model.entity;
import lombok.*;
import javax.persistence.*;
/**
* @author lijing
* @date 2019-10-17 16:41
* @description 留言实体类
*/
@Data
@Entity
@Table(name = "message")
public class MessageDO {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long creator;
private String content;
@Column(name = "gmt_update")
private Long gmtCreate;
}
数据库访问层设计
package com.qianyucc.blog.repository;
import com.qianyucc.blog.model.entity.*;
import org.springframework.data.jpa.repository.*;
/**
* @author lijing
* @date 2019-10-17 16:45
* @description 留言数据库访问层
*/
public interface MessageRepository extends JpaRepository<MessageDO, Long>, JpaSpecificationExecutor<MessageDO>{
}
业务层主要实现留言插入和查询功能:
package com.qianyucc.blog.service;
import com.qianyucc.blog.model.dto.*;
import com.qianyucc.blog.model.entity.*;
import com.qianyucc.blog.model.vo.*;
import com.qianyucc.blog.repository.*;
import com.qianyucc.blog.utils.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import java.time.*;
import java.util.*;
import java.util.stream.*;
/**
* @author lijing
* @date 2019-10-17 16:47
* @description 与留言相关的业务
*/
@Service
public class MessageService {
@Autowired
private MessageRepository messageRepository;
@Autowired
private UserRepository userRepository;
/**
* 插入一条留言
*
* @param messageDTO
*/
public void insMessage(MessageDTO messageDTO) {
MessageDO messageDO = new MessageDO();
messageDO.setCreator(messageDTO.getCreatorId());
messageDO.setContent(messageDTO.getContent());
messageDO.setGmtCreate(Instant.now().toEpochMilli());
messageRepository.save(messageDO);
}
/**
* 查询所有留言信息
*
* @return
*/
public List<MessageVO> findAllMessages() {
List<MessageDO> messageDOS = messageRepository.findAll();
List<MessageVO> messageVOS = messageDOS.stream()
.sorted(Comparator.comparing(MessageDO::getGmtCreate).reversed())
.map(messageDO -> {
MessageVO messageVO = new MessageVO();
messageVO.setCreatorId(messageDO.getCreator());
Optional<UserDO> byId = userRepository.findById(messageDO.getCreator());
byId.ifPresent(m -> {
messageVO.setCreatorName(m.getName());
messageVO.setAvatarUrl(m.getAvatarUrl());
});
messageVO.setContent(messageDO.getContent());
messageVO.setGmtCreate(BlogUtil.formatDate(messageDO.getGmtCreate(), "yyyy年MM月dd日 HH:mm"));
return messageVO;
})
.collect(Collectors.toList());
return messageVOS;
}
}
此处用到MessageVO
来封装发送给前端的信息
package com.qianyucc.blog.model.vo;
import lombok.*;
/**
* @author lijing
* @date 2019-10-17 16:42
* @description 封装向页面返回的留言信息
*/
@Data
public class MessageVO {
private Long creatorId;
private String creatorName;
private String avatarUrl;
private String content;
private String gmtCreate;
}
设计Controller
package com.qianyucc.blog.controller.comm;
import com.qianyucc.blog.model.dto.*;
import com.qianyucc.blog.model.vo.*;
import com.qianyucc.blog.service.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* @author lijing
* @date 2019-10-17 16:46
* @description 留言信息api
*/
@RestController
@RequestMapping("/api/comm/message")
public class MessageController {
@Autowired
private MessageService messageService;
@GetMapping("/getMessages")
public List<MessageVO> getMessages() {
List<MessageVO> messages = messageService.findAllMessages();
return messages;
}
@PostMapping("/leaveAMessage")
public RespDataVO leaveAMessage(@RequestBody MessageDTO messageDTO) {
messageService.insMessage(messageDTO);
return RespDataVO.ok("留言成功!");
}
}
其中MessageDTO
的设计如下:
package com.qianyucc.blog.model.dto;
import lombok.*;
/**
* @author lijing
* @date 2019-10-17 16:52
* @description 前端传递的留言信息
*/
@Data
public class MessageDTO {
private Long creatorId;
private String content;
}
在/src/request/api/url.js
中添加对应URL
// message
const getMessagesUrl = baseUrl + 'api/comm/message/getMessages'
const leaveAMessageUrl = baseUrl + 'api/comm/message/leaveAMessage'
新建/src/request/api/message.js
文件,用来封装与留言相关的api
// 导入axios实例
import axios from "@/request/http"
// 导入所有url
import url from '@/request/api/url'
export default {
getMessages(callback) {
axios
.get(url.getMessagesUrl)
.then(callback)
.catch(err => {
console.log("getMessages Error");
})
},
leaveAMessage(message, callback) {
axios
.post(url.leaveAMessageUrl, message)
.then(callback)
.catch(err => {
console.log("leaveAMessage Error");
})
}
}
不要忘了在/src/request/api/index.js
中导入message
新建/src/components/page/leaveMessage.vue
组件,展示留言,并提供留言功能:
<template>
<b-container class="main">
<h5>说点什么吧......h5>
<hr />
<b-media>
<template v-slot:aside>
<b-img
rounded="circle"
width="60"
height="60"
:src="isLogin == true ? userInfo.avatarUrl : '/static/images/no-name.png'"
>b-img>
template>
<h6 thumbnail v-text="isLogin == true ? userInfo.name : '匿名用户'">h6>
<b-textarea rows="5" placeholder="请输入留言内容......" v-model="message">b-textarea>
<b-button class="pull-right" variant="success" @click="leaveAMessage()">提交b-button>
b-media>
<hr />
<marquee direction="down" behaviour="scroll" height="300px">
<b-media v-for="(m,index) in messages" :key="index" class="message">
<template v-slot:aside>
<b-img
rounded="circle"
width="60"
height="60"
:src="m.creatorId == 0 ? '/static/images/no-name.png' : m.avatarUrl"
>b-img>
template>
<h6 thumbnail v-text="m.creatorId == 0 ? '匿名用户' : m.creatorName">h6>
<p>{{m.content}}p>
<small>{{m.gmtCreate}}small>
b-media>
marquee>
b-container>
template>
<script>
import { mapState } from "vuex";
export default {
name: "leaveMessage",
data() {
return {
messages: [],
message: ""
};
},
computed: {
...mapState({
isLogin: state => state.app.isLogin,
userInfo: state => state.user.userInfo
})
},
methods: {
leaveAMessage() {
if (this.message.trim() != "") {
let messageInfo = {
creatorId: this.isLogin ? this.userInfo.id : 0,
content: this.message
};
this.$api.message.leaveAMessage(messageInfo);
this.message = "";
this.getMessages();
}
},
getMessages() {
this.$api.message.getMessages(resp => {
this.messages = resp.data;
});
}
},
created() {
this.getMessages();
}
};
script>
<style scoped>
.main {
margin-top: 10px;
padding: 20px;
background-color: #fff;
min-height: 100%;
}
textarea {
margin-bottom: 10px;
}
h6 {
font-weight: bold;
}
.pull-right {
float: right;
}
.message {
border: solid rgb(239, 239, 239) 1px;
border-radius: 10px;
padding: 10px;
margin-bottom: 10px;
}
style>
上面代码用到了
标签,可实现滚动效果
在路由中添加leaveMessage
{
path: "/leaveMessage",
name: "leaveMessage",
component: leaveMessage
},
修改导航栏,点击“留言”跳转到leaveMessage
组件
<b-nav-item to="/leaveMessage">留言b-nav-item>