目录
[一. 功能分析](#一. 功能分析)
[5. 系统信息设置](#5. 系统信息设置)
[6. 新闻类型管理](#6. 新闻类型管理)
[7. 新闻内容管理](#7. 新闻内容管理)
[8. 新闻统计(echars组件)](#8. 新闻统计(echars组件))
[9. 用户审核封禁](#9. 用户审核封禁)
[10. 评论审核](#10. 评论审核)
[11. 敏感词管理](#11. 敏感词管理)
[1. 用户注册](#1. 用户注册)
[2. 用户登录](#2. 用户登录)
[3. 对新闻评论 点赞 收藏](#3. 对新闻评论 点赞 收藏)
[4. 用户个人中心](#4. 用户个人中心)
[二. 数据库设计](#二. 数据库设计)
[三. 核心注意事项](#三. 核心注意事项)
[1. 新闻列表查询](#1. 新闻列表查询)
[(1). 标签](#(1). 标签)
[(2). concat 关键字](#(2). concat 关键字)
[(3). 开关组件](#(3). 开关组件)
[2. 新增新闻界面](#2. 新增新闻界面)
[(1) 两次封装](#(1) 两次封装)
[(2) 解决无法显示发布状态](#(2) 解决无法显示发布状态)
[(4) 文件上传(图片)](#(4) 文件上传(图片))
[4.1 前端提交文件 使用elementUI组件提交文件](#4.1 前端提交文件 使用elementUI组件提交文件)
[4.2 后端接收文件 使用spring+Apache文件上传组件](#4.2 后端接收文件 使用spring+Apache文件上传组件)
[4.3 储存文件 将文件储存在服务器中, 只返回图片在服务器中的地址,在数据库中只存储地址](#4.3 储存文件 将文件储存在服务器中, 只返回图片在服务器中的地址,在数据库中只存储地址)
实际开发涉及多个部分,是一项复杂的工程, 需要协同完成, 例如:
(产品部)市场调研---需求分析---产品设计---开发 (研发部 技术选型 分工)---软件测试---运维
一. 功能分析
后台管理---前台显示---用户功能
(1)后台管理
-
前后端项目搭建
-
管理员登录
-
管理员管理(权限)
-
修改密码
5. 系统信息设置
用途: 维护平台信息, 数据库中只有一条记录 ,只做修改操作
维护的信息: 系统名称 logo(图片) 公司名称 电话 地址 备案号 操作人 操作时间
6. 新闻类型管理
类型(一级)
状态(启用/停用)
7. 新闻内容管理
标题 摘要 封面图 类型 内容 分类(转载/原创) 评论(是否支持) 发布状态(是否发布)
8. 新闻统计(echars组件)
统计数据(柱状图 折线图 饼状体) 地图...
9. 用户审核封禁
10. 评论审核
11. 敏感词管理
(2)前台显示
- 首页:
按分类显示新闻
系统信息
友情链接
- 新闻详细页面:
新闻基本内容
点赞 收藏 评论
(3)用户功能
1. 用户注册
账号 密码 头像 昵称 性别 手机号 状态(启用 禁用) 注册时间
2. 用户登录
3. 对新闻评论 点赞 收藏
4. 用户个人中心
修改个人信息
查看个人评论
查看浏览记录
二. 数据库设计
-
admin 管理员表
-
menu 菜单表
-
admin_menu 权限表
-
news_ type 新闻类型表
id name status(0-启用 1-停用 ) adminid oper_time
- news 新闻表
id title(标题) digest(摘要) img(封面图) content(数据库text类型) typeid(新闻类型)
type(0-原创, 1-转载) link(转载链接) commenttype(是否支持评论 0-支持评论, 1-不支持)
status(发布状态 0-未发布, 1-已发布 ) adminid (操作人) oper_time(操作时间)
- systeminfo 系统信息表
id name logo(varchar 100 给个地址) company_name phone address
filing_number(备案号) adminid oper_time
系统信息只有一条护具,可以在数据库中直接初始化一条,后期做的都是修改操作
- link 友情链接表
id name status(0-启用,1-停用) adminid oper_time
- sensitivewords敏感词表
id name adminid oper_time
- user用户表
id,email(账号), password,img(头像),nick_name,gender,phone,status(0-正常,1-禁用), reg-time
三. 核心注意事项
1. 新闻列表查询
(1). <where> 标签
<where>是mybatis中的动态sql标签, 一旦where标签内部有条件成立,会自动添加where关键字, 同时可以去除where后面紧跟的关键字(例如 and)
当有前提的判断条件时(一般写法)
where a.type = 1
<if test="account!=''">
and a.account=#{account}
</if>
<if test="phone!=''">
and a.phone=#{phone}
</if>
当没有前提的判断条件时 早期的解决方法 where 1 = 1 (没有使用where标签)
where 1 = 1
<if test="title!=''">
n.title like concat('%',#{title},'%')
</if>
在mybatis 之后(使用了where标签)
<where>
<if test="title!=''">
n.title like concat('%',#{title},'%')
</if>
</where>
(2). concat 关键字
模糊查询中需要使用concat(字符串连接函数)
n.title like concat('%',#{title},'%')
XML
<select id="findNewsList" resultMap="newsMap">
select
n.id,
n.title,
n.img,
t.name,
n.status,
n.type,
a.account,
n.oper_time
from
news n
left join news_type t
on n.typeid = t.id
left join admin a
on n.adminid = a.id
<where>
<if test="title!=''">
n.title like concat('%',#{title},'%')
</if>
</where>
</select>
(3). 开关组件
可以添加在需要修改状态的地方 例如 新闻的发布状态
XML
<template slot-scope="scope" >
<el-switch
v-model="scope.row.status"
active-color="#13ce66"
inactive-color="#ff4949"
active-text="已发布"
inactive-text="未发布"
:active-value="1"
:inactive-value="0" @change="changeStatus(scope.row)">
</el-switch>
</template>
2. 新增新闻界面
(1) 两次封装
news中封装了newstype,newstype中封装了id ,为了避免冗余, 需要这么写
<el-form-item label="新闻类型">
<el-select placeholder="请选择" v-model="form.newsType.id">
<el-option
v-for="newsType in newsTypeList"
:key="newsType.id"
:label="newsType.name"
:value="newsType.id">
</el-option>
</el-select>
</el-form-item>
form:{//默认封装到后端的news类里面
title:"",
digest:"",
newsType:{
id:""
},img:"",//向后端提交的图片地址
type:0,
link:"",
commenttype:0,
content:"",
status:0
}
(2) 解决无法显示发布状态
原因: 数据类型不匹配(字符串和整数),导致前端不显示
方法一: 在label前面加冒号 ": " ,可以将字符串的0转为整数的0
<el-form-item label="是否发布">
<el-radio v-model="form.status" :label="0">发布</el-radio>
<el-radio v-model="form.status" :label="1">未发布</el-radio>
</el-form-item>
方法二: 把form表单中type 的0 加上引号 " ",这样也能接收到字符串0
form:{//默认封装到后端的news类里面
title:"",
digest:"",
newsType:{
id:""
},
img:"",//向后端提交的图片地址
type:"0",
link:"",
commenttype:0,
content:"",
status:0
}
(3)实现转载和原创的不同显示
点击转载时出现链接,点击原创时出现文本内容
方法: v-show 例如 v-show= "form.type==0"
v-if 和 v-else 如下:
html
<el-form-item label="类型">
<el-radio v-model="form.type" :label="0">原创</el-radio>
<el-radio v-model="form.type" :label="1">转载</el-radio>
</el-form-item>
<el-form-item label="新闻内容" v-if="form.type==0">
<mavon-editor ref="md" v-model="form.content" :toolbars="toolbars" @imgAdd="imgAdd" />
</el-form-item>
<el-form-item label="链接转载" v-if="form.type==1">
<el-input v-model="form.link" style="width: 500px"></el-input>
</el-form-item>
(4) 文件上传(图片)
4.1 前端提交文件 使用elementUI组件提交文件
<el-form-item label="封面图">
<!-- 使用elementUI文件上传组件 而不是axios组件发送请求
地址不能自动不全,不能自动携带token-->
<el-upload
class="avatar-uploader"
:action="serverAdd" 后端服务器地址,这里直接填入拼接后的地址
:headers="{token:token}"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="imgUrl" :src="imgUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<style scoped >
/*加上 scoped 目的是作用在当前vue文件中*/
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 80px;
height: 80px;
line-height: 80px;
text-align: center;
}
.avatar {
width: 80px;
height: 80px;
display: block;
}
</style>
return{//新增对话框默认是关闭的
dialogVisible:false,
newsTypeList:[],
imgUrl:"",//前端临时显示用的
token**:sessionStorage.getItem("token"),//临时存储中拿到token**
serverAdd:this.serverAddress+"api/newsCtl/uploadImg" ,
form:{//默认封装到后端的news类里面
title:"",
digest:"",
newsType:{
id:""
},
img:"",//向后端提交的图片地址
type:0,
link:"",
commenttype:0,
content:"",
status:0
}
}
把之前所有添加过样式的vue中都添加上scope
//图片上传成功后会自动调用 在前端临时显示图片 resp用来接收后端响应的数据
handleAvatarSuccess(resp, file) {
this.imgUrl = URL.createObjectURL(file.raw);
this.form.img = resp.data;
alert(this.form.img);
},
//文件上传前调用 文件格式验证
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;if (!isJPG) {
this.message.error('上传头像图片只能是 JPG 格式!'); } if (!isLt2M) { this.message.error('上传头像图片大小不能超过 2MB!');
}
return isJPG && isLt2M;
}
4.2 后端接收文件 使用spring+Apache文件上传组件
添加apache 文件上传组件依赖
<!-- 文件上传组件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
添加spring文件解析类
package com.ffyc.news.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@Configuration
public class MultipartConfig {
/**
* 文件上传配置
* @return
*/
@Bean
public CommonsMultipartResolver multipartConfigElement() {
CommonsMultipartResolver multipartresolver = new CommonsMultipartResolver();
multipartresolver.setMaxUploadSize(1024*1024*5);
return multipartresolver;
}
}
java
/**
* 接收上传的文件,post请求 上传到阿里云
* 使用CommonsMultipartFile 直接接收到文件
*/
@PostMapping(path = "/uploadImg")
public Result uploadImg(@RequestParam("file")CommonsMultipartFile commonsMultipartFile) throws IOException {
/*System.out.println(commonsMultipartFile.getName());
System.out.println(commonsMultipartFile.getOriginalFilename());*/
/*InputStream inputStream =commonsMultipartFile.getInputStream();//把文件写入自己的服务器地址
File file = new File("D:\\development\\apache-tomcat-9.0.43\\webapps\\ROOT"+commonsMultipartFile.getOriginalFilename());
commonsMultipartFile.transferTo(file);//把文件输入到指定文件*/
InputStream inputStream = commonsMultipartFile.getInputStream();
//使用阿里云oss对象存储
//为了避免上传的文件重名,重新生成一个文件名
String newFileName = new Date().getTime()+commonsMultipartFile.getOriginalFilename();
//连接oss
OSS ossClient = new OSSClientBuilder().build("https://"+endpoint, accessKeyId, accessKeySecret);
//上传文件
ossClient.putObject(bucketName, newFileName, commonsMultipartFile.getInputStream());
//拼接文件在oss中访问地址
String url = "https://"+bucketName+"."+endpoint+"/"+newFileName;
Result result = new Result(200,"上传成功",url);
return result;
}
代码直接由阿里云提供, 不需要自己手写,仅理解即可,具体步骤看
(5)阿里云OSS对象存储
(6)springboot集成oss
4.3 储存文件 将文件储存在服务器中, 只返回图片在服务器中的地址,在数据库中只存储地址
原来: base64 格式字符串 ---->数据库
markdown上传图片
<el-form-item label="新闻内容" v-if="form.type==0">
<mavon-editor ref="md" v-model="form.content" :toolbars="toolbars" @imgAdd="imgAdd" />
</el-form-item>
//添加图片
imgAdd (pos, file) {
var formdata = new FormData();
formdata.append('file', file);//前端封装图片数据 'file'向后端传递的 name
this.$http({ //调用 java 后端上传图片到服务器端
url:this.serverAdd,//自定义后端服务器地址,后端同文件上传功能
data: formdata, method:"post", headers: { 'Content-Type': 'multipart/form-data','token':this.token} }).then((resp) => {//resp 后端响应数据
//将服务器返回的图片地址插入到编辑器内
this.refs.md.img2Url(pos, resp.data.data);
})
}
在新闻显示页面,添加封面图(与新增新闻的封面图不一样)
<el-table-column prop="img" label="封面图">
<template slot-scope="scope">
<img :src="scope.row.img" style="width: 50px; height: 50px;"/>
</template>
</el-table-column>
(5)阿里云OSS对象存储
访问阿里云官网
点击控制台

支付宝扫码登录,进入控制台, 没有账号注册账号

进入产品列表

选择oss对象存储

开通oss对象存储功能

完成实名认证

进入bucket列表

创建Bucket列表

创建成功,点击名称进入

测试上传文件





到此阿里云oss功能开通测试成功
(6)springboot集成oss
添加依赖
<!--阿里云oss依赖坐标-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
spring配置文件配置
aliyun:
oss:
endpoint: 你自己阿里云账户的endpoint,例如:oss-cn-beijing.aliyuncs.com
accessKeyId: 你自己阿里云账户的accessKeyId
accessKeySecret: 你自己阿里云账户的accessKeySecret
bucketName: 你自己阿里云账户的bucketName
获取accessKeyId




在使用的类中获取配置属性值
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
代码实现
@PostMapping(path = "/uploadFile")
public CommonResult uploadFile(@RequestParam("file")
CommonsMultipartFile multipartFile) throws IOException {
//为了避免上传的文件重名,重新生成一个文件名
String newFileName = new
Date().getTime()+multipartFile.getOriginalFilename();
//连接oss
OSS ossClient = new
OSSClientBuilder().build("https://"+endpoint, accessKeyId,
accessKeySecret);
//长传文件
ossClient.putObject(bucketName, newFileName,
multipartFile.getInputStream());
//拼接文件在oss中访问地址
String url =
"https://"+bucketName+"."+endpoint+"/"+newFileName;
return new CommonResult(200, url,"操作成功");
}
上传成功

