一、留言页面
留言和评论的实现几乎一模一样,还不需要传blogid,设计思路也一样,这里就直接给出代码。(或者直接参照评论代码,直接复制来改)
1. 持久层接口
在dao
包下创建MessageDao
接口,添加如下接口:(不需要传博客id)
@Mapper
@Repository
public interface MessageDao {
//新增一个留言
int savaMessage(Message message);
//删除一个留言
void deleteMessage(Long id);
/*查询父级留言 查询一级回复 查询二级回复*/
//查询父级留言 : 它的评论类的父留言的-1
List<Message> findMessageParentIdNull(Long messageParentId);
//查询一级回复 : 根据父留言id
List<Message> findMessageParentNotNull(Long parentId);
//查询二级回复 : 根据子回复 id
List<Message> findMessageReplyId(Long childId);
//单独查询一条回复,查出父评论,用于发送回复邮件
Message findByParentId(Long id);
}
2.mapper
在mapper
目录下创建MessageDao.xml
文件,添加如下
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.hxj.dao.MessageDao">
<!--新增一个留言-->
<insert id="savaMessage" parameterType="Message">
insert into t_message (nickname, email, content, avatar, create_time, parent_message_id, admin_message)
values (#{nickname},#{email},#{content},#{avatar},#{createTime},#{parentMessageId},#{adminMessage});
</insert>
<!--删除一个留言-->
<delete id="deleteMessage">
delete
from t_message
where id=#{id};
</delete>
<!--查询父级留言 : 它的评论类的父留言的-1-->
<select id="findMessageParentIdNull" resultType="Message">
select *
from t_message
where parent_message_id = #{messageParentId}
order by create_time desc;
</select>
<!--查询一级回复 : 根据父留言id-->
<select id="findMessageParentNotNull" resultType="Message">
select *
from t_message
where parent_message_id = #{parentId}
order by create_time desc;
</select>
<!--查询二级回复 : 根据子回复 id-->
<select id="findMessageReplyId" resultType="Message">
select *
from t_message
where parent_message_id = #{childId}
order by create_time desc;
</select>
<select id="findByParentId" resultType="Message">
select *
from t_message where id = #{id};
</select>
</mapper>
3. 业务层
在service
包下创建MessageService
接口
public interface MessageService {
//新增一个留言
int savaMessage(Message message);
//删除一个留言 Message message
void deleteMessage(Long id);
/*查询留言信息*/
List<Message> listMessage();
Message findByParentId(Long id);//为了发邮件
}
在Impl
包下创建接口实现类:MessageServiceImpl
接口实现类
@Service
public class MessageServiceImpl implements MessageService {
@Autowired
private MessageDao messageDao;
//存放迭代子评论的集合
private List<Message> tempReplys = new ArrayList<>();
@Override
public int savaMessage(Message message) {
message.setCreateTime(new Date());
return messageDao.savaMessage(message);
}
@Override
public void deleteMessage(Long id) {
messageDao.deleteMessage(id);
}
@Override
public List<Message> listMessage() {
//查出父节点, pid =-1
List<Message> messages = messageDao.findMessageParentIdNull((long) -1);
//为每个父评论 找到 它的子代评论
for (Message message : messages) {
Long id = message.getId(); //作为 子代的 pid
String Nickname = message.getNickname();
List<Message> childMessages = messageDao.findMessageParentNotNull(id);
//查出这个父评论的 所有子评论
combineChildren(childMessages, Nickname); //查询完后,所有子代 都添加到 tempReplys中
message.setReplyMessages(tempReplys);
//System.out.println(tempReplys);
tempReplys = new ArrayList<>();//给下一个用,不可以单纯清空内容
}
return messages;
}
private void combineChildren(List<Message> childMessages,String Nickname){
//判读是否有 一级子评论
if(childMessages.size() > 0){
//循环找出 子评论
for (Message childMessage : childMessages) {
String parentNickname1 = childMessage.getNickname(); //作为 二级评论 的 父name
Long childId = childMessage.getId();
childMessage.setParentNickname(Nickname); //在这里才设置 回复的 parentNickname
tempReplys.add(childMessage); //存入 子代评论中
//循环 查出所有子二级评论
recursively(childId,parentNickname1);
}
}
}
private void recursively(Long childId,String parentNickname){
//根据一级评论 id 找到所有二级评论
List<Message> replyMessages = messageDao.findMessageReplyId(childId);
if(replyMessages.size()>0){
for (Message replyMessage : replyMessages) {
String parentNickname2 = replyMessage.getNickname();
Long replyId = replyMessage.getId();
replyMessage.setParentNickname(parentNickname);
tempReplys.add(replyMessage);
//评论可能还有 子代评论,接着找(直到空为止)
recursively(replyId,parentNickname2);
}
}
}
//为了发邮件
@Override
public Message findByParentId(Long id) {
return messageDao.findByParentId(id);
}
}
4. 控制器
为了实现局部刷新,前端改动:
删除操作也改成局部刷新
<a class="reply" data-messageid="1" data-messagenickname="Matt"
th:attr="data-messageid=${message.id},data-messagenickname=${message.nickname}" onclick="reply(this)">回复</a>
<a class="delete" href="#"
th:attr="data-messageid=${message.id}" onclick="shanchu(this)"
th:if="${session.user}">删除</a>
...
...
...
<a class="reply" data-messageid="1" data-messagenickname="Matt"
th:attr="data-messageid=${reply.id},data-messagenickname=${reply.nickname}"
onclick="reply(this)">回复</a>
<a class="delete" href="#"
th:attr="data-messageid=${reply.id}"
onclick="shanchu(this)"
th:if="${session.user}">删除</a>
JS部分:
// 校验信息
$('#messagepost-btn').click(function () {
var boo = $('.ui.form').form('validate form');
if (boo) {
console.log('校验成功');
postData();
} else {
console.log('校验失败');
}
});
//发送请求给后端
function postData() {
$('#message-container').load(/*[[@{/message}]]*/"/message", {
"parentMessage.id": $("[name='parentMessage.id']").val(),
"nickname": $("[name='nickname']").val(),
"email": $("[name='email']").val(),
"content": $("[name='content']").val()
}, function (responseTxt, statusTxt, xhr) {
clearContent();
});
}
// 清除表单
function clearContent() {
$("[name='nickname']").val('');
$("[name='email']").val('');
var user = /*[[${session.user}]]*/ null;
/*因为我们在 admin的logincontroller 把user 传进 session*/
console.log(user);
if( user ){
$("[name='nickname']").val(user.nickname);
$("[name='email']").val(user.email);
}
$("[name='content']").val('');
$("[name='parentMessage.id']").val(-1);
$("[name='content']").attr("placeholder", "请输入评论信息...");
}
function reply(obj) {
//alert("回复");
var messageId = $(obj).data('messageid');
var messageNickname = $(obj).data('messagenickname');
$("[name='content']").attr("placeholder", "@" + messageNickname).focus();
$("[name='parentMessage.id']").val(messageId);
$(window).scrollTo($('message-form'), 500);
}
/*改造成,删除了只改动评论区. 不然模板包太大,评论区又会问题..目前只能这样*/
function shanchu(obj){
var flag = confirm('确定要删除该评论吗?三思啊! 删了可就没了!');
if(flag == true){
var messageId = $(obj).data('messageid');
$("#message-container").load(/*[[@{/messages/delete}]]*/"/messages/delete",{
"messageId" : messageId
});
}
$(window).scrollTo($('#message-container'),500);
}
在controller
包下创建MessageController
类,编写如下代码:(敏感词和邮件发送功能与评论一样,这里不再细说)
@Controller
public class MessageController {
@Autowired
private MessageService messageService;
@Autowired
private MailUtil mailUtil;
@Autowired
private SensitiveFilterUtil sensitiveFilterUtil;
@Value("${message.avatar}")
private String avatar;
private String subject="HXJ的博客留言回复";
@GetMapping("/message")
public String message(Model model){
List<Message> messages = messageService.listMessage();
System.out.println(messages);
model.addAttribute("messages",messages);
return "message";
}
//新增留言
@PostMapping("/message")
public String post(Message message,Model model, HttpSession session){
User user =(User)session.getAttribute("user");
if(user!=null){
message.setAvatar(user.getAvatar());
message.setAdminMessage(true);
}else {
message.setAvatar(avatar);
}
message.setContent(sensitiveFilterUtil.filter(message.getContent()));
//前端传的是 parentMessage.id
if(message.getParentMessage().getId()!=null){
//是评论
message.setParentMessageId(message.getParentMessage().getId());//到impl中 的展示才设置parentNickname
//是子评论
if(message.getParentMessageId()!=-1){
Message parentMessage = messageService.findByParentId(message.getParentMessageId());//获得父评论
//发邮件给父评论
String rpl="亲爱的【"+parentMessage.getNickname()+"】,你在【HXJ的博客】的评论:"+parentMessage.getContent()+".收到了来自【"+
message.getNickname()+"】的回复! 内容如下:\n\n";
mailUtil.sendSimpleMail(parentMessage.getEmail(), subject, rpl+message.getContent());
}
}
messageService.savaMessage(message);
List<Message> messages = messageService.listMessage();
System.out.println(messages);
model.addAttribute("messages",messages);
return "message :: messageList";
}
//删除
@PostMapping("/messages/delete")
public String delete(Model model, @RequestParam("messageId")Long messageId){
messageService.deleteMessage(messageId);
List<Message> messages = messageService.listMessage();
System.out.println(messages);
model.addAttribute("messages",messages);
return "message :: messageList";
}
}
效果示例图如下:
二、归档页(时间轴)
前端直接拿来用,前端所需要的字段较少(但不想再多建一个VO,拿已经有的vo中找一个属性最少的),于是用后台的这个
BlogQuery
。这样
dao
接口Service
接口和mapper
都用已经存在的getAllBlogQuery
接口。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r1VNMra7-1653976189192)(其余页面显示.assets/image-20220531132830021.png)]
- 只需要编写控制器代码:
在controller
包下创建ArchiveShowController
类:
@Controller
public class ArchiveShowController {
@Autowired
private BlogService blogService;
@GetMapping("/archives")
public String archives(Model model){
List<BlogQuery> list = blogService.getAllBlogQuery();
model.addAttribute("blogs",list);
return "archives";
}
}
- 效果示例图如下:
三、友链页
根据前端页面显示,可以直接调用持久层的listFriendLink
接口,只需编写控制层代码就可以了。
- 编写控制器代码:
在controller
包下创建FriendsShowController
类:
@Controller
public class FriendsShowController {
@Autowired
private FriendLinkService friendLinkService;
@GetMapping("/friends")
public String friend(Model model){
List<FriendLink> friendLinks = friendLinkService.listFriendLink();
model.addAttribute("friendlinks",friendLinks);
return "friends";
}
}
- 效果示例图如下:
四、照片墙页面显示
根据前端页面显示,可以直接调用持久层的listPicture
接口,只需编写控制层代码就可以了。
照片墙使用了(lightbox插件)
jkresponsivegallery.js
官方使用例:
- 编写控制器代码:
在controller
包下创建PictureShowController
类:
@Controller
public class PictureShowController {
@Autowired
private PictureService pictureService;
@GetMapping("/picture")
public String picture(Model model){
List<Picture> pictures = pictureService.listPicture();
model.addAttribute("pictures",pictures);
return "picture";
}
}
- 效果示例图如下:
五、关于我页面显示
关于我页面显示是一个静态页面,直接在控制器中返回页面就可以了
- 编写控制器代码:
在controller
包下创建AboutShowController
类:
@Controller
public class AboutShowController {
@GetMapping("/about")
public String about(){
return "about";
}
}
六、音乐盒
音乐盒显示是一个静态页面,直接在控制器中返回页面就可以了
原本oneStar使用了一个轻量的音乐js插件,结果我发现不如直接拉一个网易云外链方便。
- 编写控制器代码:
在controller
包下创建MusicShowController
类:
@Controller
public class MusicShowController {
//就一个静态页面
@GetMapping("/music")
public String music(){
return "music";
}
}
小结
至此所有功能开发已经完成。
如果想更换图片资源使用图床,可以看oneStar的Typora+PicGo+七牛云实现图片上传存储(配置不难,但是用PicGo配置的时候要知道自己的存储区域 官方手册有提到 )
线上部署方面,没有买服务器(没精力管),就做了一个穷人体验版。
使用了VMware Workstation 装个centos7,在虚拟机配置好后,本机也可以实现访问(下一篇文章将讲解我的操作。)