1. 持久层接口留言和评论的实现几乎一模一样,还不需要传blogid,设计思路也一样,这里就直接给出代码。(或者直接参照评论代码,直接复制来改)
在dao包下创建MessageDao接口,添加如下接口:(不需要传博客id)
@Mapper
@Repository
public interface MessageDao {
//新增一个留言
int savaMessage(Message message);
//删除一个留言
void deleteMessage(Long id);
//查询父级留言 : 它的评论类的父留言的-1
List findMessageParentIdNull(Long messageParentId);
//查询一级回复 : 根据父留言id
List findMessageParentNotNull(Long parentId);
//查询二级回复 : 根据子回复 id
List findMessageReplyId(Long childId);
//单独查询一条回复,查出父评论,用于发送回复邮件
Message findByParentId(Long id);
}
2.mapper
在mapper目录下创建MessageDao.xml文件,添加如下
3. 业务层insert into t_message (nickname, email, content, avatar, create_time, parent_message_id, admin_message) values (#{nickname},#{email},#{content},#{avatar},#{createTime},#{parentMessageId},#{adminMessage}); delete from t_message where id=#{id};
在service包下创建MessageService接口
public interface MessageService {
//新增一个留言
int savaMessage(Message message);
//删除一个留言 Message message
void deleteMessage(Long id);
List listMessage();
Message findByParentId(Long id);//为了发邮件
}
在Impl包下创建接口实现类:MessageServiceImpl接口实现类
@Service
public class MessageServiceImpl implements MessageService {
@Autowired
private MessageDao messageDao;
//存放迭代子评论的集合
private List 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 listMessage() {
//查出父节点, pid =-1
List messages = messageDao.findMessageParentIdNull((long) -1);
//为每个父评论 找到 它的子代评论
for (Message message : messages) {
Long id = message.getId(); //作为 子代的 pid
String Nickname = message.getNickname();
List childMessages = messageDao.findMessageParentNotNull(id);
//查出这个父评论的 所有子评论
combineChildren(childMessages, Nickname); //查询完后,所有子代 都添加到 tempReplys中
message.setReplyMessages(tempReplys);
//System.out.println(tempReplys);
tempReplys = new ArrayList<>();//给下一个用,不可以单纯清空内容
}
return messages;
}
private void combineChildren(List 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 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. 控制器
为了实现局部刷新,前端改动:
删除操作也改成局部刷新
回复 删除 ... ... ... 回复 删除
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", {
"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 = null;
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",{
"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 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()+"】的回复! 内容如下:nn";
mailUtil.sendSimpleMail(parentMessage.getEmail(), subject, rpl+message.getContent());
}
}
messageService.savaMessage(message);
List 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 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 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 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 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,在虚拟机配置好后,本机也可以实现访问(下一篇文章将讲解我的操作。)



