IDEA SpringCloud SpringBoot 评论过滤功能(学习文章)


注:【网页版】右上方的悬浮框( 有目录索引 )


一、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 项目

  1. 创建无模版 Maven 项目:day20200519_springcloud_lx
  2. 删除 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 ClientSpring 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 ClientSpring 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">&times;</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>&nbsp;&empty;&nbsp;评论内容
					        	</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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值