文章目录
注:【网页版】右上方的悬浮框( 有目录索引 )
一、MySql 创建
表内容,后续由 java 代码实现
CREATE TABLE `user_comment` (
`comment_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '评论序列号',
`comment_article_id` bigint(32) NOT NULL COMMENT '被评论的文章 id',
`comment_date` date NOT NULL COMMENT '发表评论 时间',
`comment_content` varchar(300) COLLATE utf8_unicode_ci NOT NULL COMMENT '发表评论 内容',
`comment_writer_id` bigint(32) NOT NULL COMMENT '发表评论作者 id',
`comment_writer_nickname` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '发表评论作者 昵称',
PRIMARY KEY (`comment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
二、搭建后台接口
2-1】创建主 Maven 项目
- 创建无模版 Maven 项目:day20200519_springcloud_lx
- 删除 Maven 下 src 目录
2-2】server 所需配置
- 依赖:
Spring Cloud Discovery - Eureka Server
- 启动类注解:
@EnableEurekaServer
- // localhost:7776 访问,浏览器,可视化操作页面
# yml 配置信息
server:
port: 7776
eureka:
client:
service-url:
defaultZone: http://localhost:7776/eureka # 注册中心的地址
register-with-eureka: false # 是否把当前项目注册到中心
fetch-registry: false # 是否在注册中心检索此项目
2-3】provider 所需配置
新建 provider 模块,会出现如下弹窗,选择如下即可
- 依赖:
Spring Cloud Discovery - Eureka Discovery Client
此外,还有 ssm 三个依赖 - 启动类注解:
@EnableEurekaClient
# yml 配置信息
server:
port: 8090
spring:
datasource:
url: jdbc:mysql:///bug?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
username: root
hikari: # 连接池 [译:光]
maximum-pool-size: 200
minimum-idle: 10
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
default-property-inclusion: non_null # ajax 去 null 值
application:
name: provider # 注册到注册中心的名称
eureka:
client:
service-url:
defaultZone: http://localhost:7776/eureka # 注册中心的位置
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.debj.provider.entity
configuration:
call-setters-on-nulls: true # mybatis null 值不会封装至 map 中
map-underscore-to-camel-case: true # 匹配驼峰命名规则的实体类字段
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 输出 sql 日志
【2-3-1】使用逆向工程
生成文件,记得加注解
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<!-- plugin 放在插件中 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<overwrite>true</overwrite>
<configurationFile>src/main/resources/mbg.xml</configurationFile>
</configuration>
</plugin>
resources 下 mbg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动:选择本地硬盘的数据库驱动包 -->
<classPathEntry location="D:\_ruanJian\Jar\mysql-connector-java-5.1.47\mysql-connector-java-5.1.47.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--1:连接数据库的配置-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql:///bug"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--java生成bean 的位置-->
<javaModelGenerator
targetPackage="com.debj.provider.entity"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--生成映射文件的位置-->
<sqlMapGenerator
targetPackage="mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--生成接口文件的位置-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.debj.provider.dao"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--映射表生成实体类-->
<table tableName="user_comment" domainObjectName="UserComment"/>
</context>
</generatorConfiguration>
【2-3-2】生成测试数据
@SpringBootTest
class ProviderApplicationTests {
@Resource
private UserCommentMapper userCommentMapper;
@Test
void contextLoads() {
UserComment userComment = new UserComment();
for(int i=0;i<20;i++){
userComment.setCommentArticleId(333L);
userComment.setCommentDate(new Date());
userComment.setCommentContent(UUID.randomUUID().toString());
userComment.setCommentWriterId(520L);
userComment.setCommentWriterNickname("spiderMan");
userCommentMapper.insertSelective(userComment);
}
}
}
【2-3-3】其它代码
@RestController
@CrossOrigin
@RequestMapping(value = "/userComment")
public class UserCommentController {
@Resource
private UserCommentService service;
@GetMapping(value = "/getAll")
public Map getAllInfo(){
return service.getAllInfo();
}
@PostMapping(value = "addOne")
public Map addOneInfo(
@RequestParam Map map){
System.out.println(map);
return service.addOneInfo(map);
}
}
public interface UserCommentService {
Map getAllInfo();
Map addOneInfo(Map map);
}
@Service
@Transactional
public class UserCommentServiceImpl implements UserCommentService {
@Resource
private UserCommentMapper mapper;
// 返回 Map
Map<String,Object> returnMap = new HashMap<>();
@Override
public Map getAllInfo() {
returnMap.put("data",mapper.selectByExample(null));
returnMap.put("code","100");
return returnMap;
}
@Override
public Map addOneInfo(Map map) {
UserComment userComment = new UserComment();
userComment.setCommentArticleId(Long.parseLong((String) map.get("commentArticleId")));
userComment.setCommentDate(new Date());
userComment.setCommentContent((String) map.get("commentContent"));
userComment.setCommentWriterId(Long.parseLong((String) map.get("commentWriterId")));
userComment.setCommentWriterNickname((String) map.get("commentWriterNickname"));
if(mapper.insertSelective(userComment)==1){
returnMap.put("info","添加成功");
returnMap.put("code","100");
} else {
returnMap.put("info","添加失败");
returnMap.put("code","200");
}
return returnMap;
}
}
2-4】customer 所需配置
- 依赖:
Spring Cloud Discovery - Eureka Discovery Client
、Spring Cloud Routing - OpenFeign
此外,还有spring web
依赖 - 启动类注解:
@EnableEurekaClient
、@EnableFeignClients
server:
port: 8080
spring:
application:
name: consumer
eureka:
client:
service-url:
defaultZone: http://localhost:7776/eureka # 注册中心的地址
【2-4-1】feign 包
@FeignClient(value = "provider",fallback = FeignUserCommentServiceImpl.class)
public interface FeignUserCommentService {
@GetMapping(value = "/userComment/getAll")
public Map getAllInfo();
@PostMapping(value = "/userComment/addOne")
public Map addOneInfo(
@RequestParam Map map);
}
@Component
public class FeignUserCommentServiceImpl implements FeignUserCommentService {
@Override
public Map getAllInfo() {
return null;
}
@Override
public Map addOneInfo(Map map) {
return null;
}
}
【2-4-2】其它代码
@RestController
@CrossOrigin
@RequestMapping(value = "/userComment")
public class UserCommentController {
@Resource
private LocalService service;
@GetMapping(value = "/getAll")
public Map getAllInfo(){
return service.getAllInfo();
}
@PostMapping(value = "addOne")
public Map addOneInfo(
@RequestParam Map map){
System.out.println(map);
return service.addOneInfo(map);
}
}
public interface LocalService {
Map getAllInfo();
Map addOneInfo(Map map);
}
@Service
public class LocalServiceImpl implements LocalService {
@Resource
private FeignUserCommentService feignUserCommentService;
@Override
public Map getAllInfo() {
return feignUserCommentService.getAllInfo();
}
@Override
public Map addOneInfo(Map map) {
return feignUserCommentService.addOneInfo(map);
}
}
2-5】gateway 所需配置
- 依赖:
Spring Cloud Discovery - Eureka Discovery Client
、Spring Cloud Routing - Zuul [Maintenance]
- 启动类注解:
@EnableEurekaClient
、@EnableZuulProxy
server:
port: 8070
zuul:
routes:
consumer: /consumer/**
eureka:
client:
service-url:
defaultZone: http://localhost:7779/eureka
spring:
application:
name: gateway
【2-5-1】MyFilter 代码
public class MyFilter extends ZuulFilter {
private String[] split;
{
FileReader fileReader = null;
try {
fileReader = new FileReader(ResourceUtils.getFile("classpath:words.txt"));
StringBuffer stringBuffer = new StringBuffer();
char[] chars = new char[1024];
int len = -1;
while((len = fileReader.read(chars))!=-1){
stringBuffer.append(chars);
}
split = stringBuffer.toString().split(",");
System.out.println(Arrays.asList(split).toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String filterWord(String param){
// 对输入的文本进行过滤 自定义方法
for(String word:split){
if(param.indexOf(word)!=-1){
// 有敏感词需要过滤
StringBuffer newWord= new StringBuffer();
for(int i=0;i<word.length();i++){
newWord.append("*");
}
param=param.replaceAll(word,newWord.toString());
}
}
return param;
}
public static void main(String[] args) { // 测试
System.out.println(new MyFilter().filterWord("你是个沙雕沙雕的草泥马草泥马-沙雕沙雕的草泥马草泥马a!"));
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
// 先获取请求参数的 map 集合,获取当前的请求上下文环境
RequestContext context = RequestContext.getCurrentContext();
// 获得 request
HttpServletRequest request = context.getRequest();
// 获得所有的请求参数组成的 Map
Map<String, String[]> parameterMap = request.getParameterMap();
if(parameterMap==null){
return null;
}
// 构造新的过滤后的参数 Map
HashMap<String, List<String>> newParamsMap = new HashMap<>();
// 新建保存参数值的 list
List<String> newValueList = null;
// 遍历参数的 key,获得第一个参数值
for (String key : parameterMap.keySet()){
System.out.println("key:"+key);
// 循环遍历每个键,对应的值
for(String value: parameterMap.get(key)){
newValueList = new ArrayList<String>();
System.out.println(key+",键对应的值有其:"+value);
if(key.equals("commentContent")){
newValueList.add(filterWord(value));
} else {
newValueList.add(value);
}
}
newParamsMap.put(key,newValueList);
}
// 重新设置查询参数
context.setRequestQueryParams(newParamsMap);
return null;
}
}
// 启动类,配置 Bean
@Bean
public MyFilter getMyFilter(){
return new MyFilter();
}
// words.txt 示例文本
// 沙雕,草泥马,多余文本
三、简单页面
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="css/index.css" />
<!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
<!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
<!--[if lt IE 9]>
<script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->
<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="bootstrap-3.3.7-dist/js/jquery-1.12.4.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
<script src="bootstrap-3.3.7-dist/js/vue.js"></script>
</head>
<body>
<div id="app">
<div class="container col-xs-6 col-xs-offset-3">
<div class="row text-center" style="margin: 50px auto;">
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
添加新评论
</button>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div class="modal-body">
<form id="addOneInfoForm" @submit.prevent="addOneInfo">
<p><input type="hidden" placeholder="" v-model="addOneInfoData.commentArticleId" ></p>
<p class="text-left">
<textarea placeholder="评论内容" style="resize: none;" cols="50" rows="4" v-model="addOneInfoData.commentContent">
</textarea> ∅ 评论内容
</p>
<p><input type="hidden" placeholder="" v-model="addOneInfoData.commentWriterId" /></p>
<p><input type="hidden" placeholder="" v-model="addOneInfoData.commentWriterNickname" /></p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" form="addOneInfoForm" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<ul id="pl">
<li v-for="item,index in listInfoData.data" :class="index%2==0?'Gray':''" style="height: 50px;">
<span>{{item.commentContent}}</span>
<span class="spanRight">{{item.commentWriterNickname}}</span>
<br />
<span class="spanRight">{{item.commentDate}}</span>
</li>
</ul>
</div>
</div>
</div>
<script src="js/index.js"></script>
</body>
</html>
var vue = new Vue({
el:"#app",
data:{
listInfoData:{},
addOneInfoData:{
"commentArticleId":"333",
"commentWriterId":"520",
"commentWriterNickname":"spiderMan",
"commentContent":""
}
},
methods:{
getAllInfo:function(){
$.get("http://localhost:8066/consumer/userComment/getAll",function(res){
vue.listInfoData=res;
})
},
addOneInfo:function(){
$.post("http://localhost:8066/consumer/userComment/addOne",vue.addOneInfoData,function(res){
vue.getAllInfo();
$("#myModal").modal("hide");
alert(res.info)
})
}
},
mounted:function(){
this.getAllInfo();
}
})
.spanRight{
display: inline-block;
position: relative;
float: right;
}
.Gray{
background-color: darkseagreen;
}