web项目:博客相册分享网站

效果展示

目前项目已经上线了,地址:http://e601.top/home,主要实现的功能有:

  • [x] 首页博客和相册的展示
  • [x] 网站成员基本信息展示
  • [x] 网站关于页面
  • [x] 博客详情页面,支持代码高亮、数学公式显示
  • [x] 博客评论功能
  • [x] 用户注册和登录
  • [x] 博客编辑和发布以及管理
  • [x] 相册发布和管理

部分需登录才能展示的页面如下:

1.jpg
2.jpg
3.jpg

后端设计

数据库结构

后端主要有Album,Blog,Comment,Tag,Type,User几个实体类,后端的主要工作就是对这些数据库进行增删查改,项目使用的数据库为Mysql 8.0 ,数据库建表语句如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_blog
-- ----------------------------
DROP TABLE IF EXISTS `t_blog`;
CREATE TABLE `t_blog` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`appreciation` bit(1) NOT NULL,
`comment_enable` bit(1) NOT NULL,
`content` longtext NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`description` varchar(255) NULL DEFAULT NULL,
`first_picture` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`flag` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`published` bit(1) NOT NULL,
`recommend` bit(1) NOT NULL,
`share_statement` bit(1) NOT NULL,
`title` varchar(255) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
`views` int(11) NULL DEFAULT NULL,
`type_id` bigint(20) NULL DEFAULT NULL,
`user_id` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
FOREIGN KEY (`type_id`) REFERENCES `t_type` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 62 ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_comment
-- ----------------------------
DROP TABLE IF EXISTS `t_comment`;
CREATE TABLE `t_comment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`blog_id` bigint(20) NULL DEFAULT NULL,
`parent_comment_id` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


-- ----------------------------
-- Table structure for t_type
-- ----------------------------
DROP TABLE IF EXISTS `t_type`;
CREATE TABLE `t_type` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_id` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 58 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_tag
-- ----------------------------
DROP TABLE IF EXISTS `t_tag`;
CREATE TABLE `t_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_id` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 76 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`description` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`moto` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`type` int(11) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`site` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_blog_tag, 中间表用于维护多对多关系
-- 注意其中使用了两个id的联合主键,保证了记录不重复
-- ----------------------------
DROP TABLE IF EXISTS `t_blog_tag`;
CREATE TABLE `t_blog_tag` (
`blog_id` bigint(20) NOT NULL,
`tag_id` bigint(20) NOT NULL,
PRIMARY KEY (`blog_id`, `tag_id`) USING BTREE,
FOREIGN KEY (`blog_id`) REFERENCES `t_blog` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
FOREIGN KEY (`tag_id`) REFERENCES `t_tag` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_album
-- ----------------------------
DROP TABLE IF EXISTS `t_album`;
CREATE TABLE `t_album` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`device` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`views` int(11) NULL DEFAULT NULL,
`like` int(11) NULL DEFAULT NULL,
`description` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
`urls` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`user_id` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 45 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_about
-- ----------------------------
DROP TABLE IF EXISTS `t_about`;
CREATE TABLE `t_about` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`content` longtext NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 201 ROW_FORMAT = Dynamic;

INSERT INTO t_about(content) values ('About');

SET FOREIGN_KEY_CHECKS = 1;

接口设计

为了将登录用户和非登录用户区分,接口上设计成两类:

第一类是无需token即可访问的共有接口,地址是以/public开头的,如获取所有用户的所有博客的接口:

1
@GetMapping("/public/allBlogs")

第二类是需要在请求头中携带token的私有接口,如添加博客的接口:

1
@PostMapping("/private/blog")

在用户登录成功时,给用户发放token,并在请求拦截器中对token进行初步验证,用于验证请求是否携带正确的token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
// 拦截器中不处理异常,而是将其转发至全局异常处理器统一处理
try {
JWTUtil.verify(token);
} catch (Exception e) {
request.setAttribute("interceptorError", e);
request.getRequestDispatcher("/error/throw").forward(request, response);
return false;
}
return true;
}
}

前后端接口间传送的数据为json格式,所有的数据都为以下结构:

1
2
3
4
5
{
"code": xxx,
"message": "",
"data": ""
}

其中 code 为此次请求的响应码,message为此次请求的响应信息,data为此次请求的具体响应数据。例如对于/public/allAlbums发出GET请求时得到如下数据,表明此次请求成功,数据为一个数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{
"code": 200,
"message": "成功",
"data": [{
"id": 64,
"title": "还是对返回",
"device": "大师傅士大夫",
"views": 0,
"like": 0,
"description": "啊手动阀手动阀",
"createTime": "2022-05-09T03:32:38.000+00:00",
"updateTime": "2022-05-09T03:32:38.000+00:00",
"urls": "http://127.0.0.1:8086/images/album/4/c81c30b5-cd02-44b7-8273-41db53ccb1ac.png",
"userId": null,
"user": {
"id": 4,
"nickname": "小切",
"username": "xiaoqie",
"password": null,
"email": "fanxiaodong@buaa.edu.cn",
"avatar": "http://localhost:8086/images/userAvatar/d3ca84d4-68f5-4e20-b2ef-19e716bd03d3.jpg",
"type": 0,
"createTime": "2022-05-03T08:41:17.000+00:00",
"updateTime": "2022-05-03T08:41:35.000+00:00",
"moto": "所念皆星河",
"description": "好想吃冰激凌",
"site": "https://xiaodongfan.com",
"blogNum": null,
"albumNum": null
}
}, {
"id": 67,
"title": "撒打发士大夫",
"device": "撒打发士大夫",
"views": 0,
"like": 0,
"description": "撒打发士大夫",
"createTime": "2022-05-10T09:33:32.000+00:00",
"updateTime": "2022-05-10T09:33:32.000+00:00",
"urls": "http://localhost:8086/images/album/4/ec95bf92-dabb-4527-adc1-dbf9f963fa5a.JPG",
"userId": null,
"user": {
"id": 4,
"nickname": "小切",
"username": "xiaoqie",
"password": null,
"email": "fanxiaodong@buaa.edu.cn",
"avatar": "http://localhost:8086/images/userAvatar/d3ca84d4-68f5-4e20-b2ef-19e716bd03d3.jpg",
"type": 0,
"createTime": "2022-05-03T08:41:17.000+00:00",
"updateTime": "2022-05-03T08:41:35.000+00:00",
"moto": "所念皆星河",
"description": "好想吃冰激凌",
"site": "https://xiaodongfan.com",
"blogNum": null,
"albumNum": null
}
}]
}

前端设计

这个项目采用前后端分离的主要原因还是自己前端功底不行😂,而使用vue进行前端开发能够提高开发的效率。

路由设计

全部页面的路由如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: index,
children: [
{
path: '',
component: home,
},
{
path: '/users',
component: users,
},
{
path: '/about',
component: about,
},
{
path: '/detail/:blogId',
component: detail,
},
{
path: '/person/:userId',
component: personPage,
},
]
},
{
path: '/admin',
component: login,
},
{
path: '/signUp',
component: signUp,
},
{
// 用户id作为路径参数
path: '/admin/:id',
component: blogAdmin,
children: [
{
path: 'publish',
component: publish,
meta: 'needAuth'
},
{
path: 'album',
component: album,
meta: 'needAuth'
},
{
path: 'manage',
component: manage,
meta: 'needAuth'
},
{
path: 'edit/:blogId',
component: edit,
meta: 'needAuth'
},
{
path: 'newAlbum',
component: newAlbum,
meta: 'needAuth'
},
{
path: 'editAlbum/:albumId',
component: editAlbum,
meta: 'needAuth'
},
]
}
]
})

根据token设置前端路由守卫,对需要拦截的页面进行拦截:

1
2
3
4
5
6
7
8
// 路由守卫
router.beforeEach((to, from, next) => {
if (to.meta === 'needAuth' && !window.sessionStorage.getItem('token')) {
return next('/admin')
} else {
return next()
}
})

接口设计

使用axios想后端服务器发送请求获取数据,设置请求拦截器和响应拦截器,其中请求拦截器为请求头增加token,响应拦截器拦截服务器的token过期消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import axios from "axios";
import Vue from "vue";

let baseUrl;
if (process.env.NODE_ENV === 'production') {
baseUrl = 'http://e601.top:8086/'
} else {
baseUrl = 'http://localhost:8086/'
}

const service = axios.create({
baseURL: baseUrl,
timeout: 6000 // 请求超时时间
})

// 请求拦截器
service.interceptors.request.use(config => {
let token = window.sessionStorage.getItem('token')
if (token) {
config.headers.Authorization = token
}
return config
})

// 相应拦截器
service.interceptors.response.use(response => {
if(response.data.code === 506) {
Vue.prototype.$message.error("token超时,请重新登录!");
}
return response.data
}, error => {
console.log(error.response)
return Promise.reject(error)
})

export {service as axios}

项目上线

环境为centos 7,使用 nginx 运行前端服务器,后端服务器为Spring Boot直接生成可执行 jar 包直接在服务器上运行。

总结

本文之间简单的挑了些项目重点进行了介绍,详细内容见代码:https://github.com/xiaoqieF/E601official