赞
踩
// An highlighted block @Configuration public class RedisTemplateConfig { @Autowired public RedisTemplateConfig(RedisTemplate redisTemplate) { //1.创建jackson序列化方式 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>( Object.class); //2..创建object mapper ObjectMapper objectMapper = new ObjectMapper(); //3.允许访问对象中所有属性 objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //4.转换json过程中保存类的信息 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); //5.设置value的序列化规则和 key的序列化规则 //key String hashkey:String hashvalue: json序列化 StringRedisSerializer stringKeySerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringKeySerializer); //6.jackson2JsonRedisSerializer就是JSON序列号规则, redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); //6.5 设置hash类型key value 序列化方式 redisTemplate.setHashKeySerializer(stringKeySerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); //7工厂创建redisTemplate对象之后在进行配置 redisTemplate.afterPropertiesSet(); } }
/**
* ObjectMapper配置类
*/
@Configuration
public class BeanConfig {
@Bean
public ObjectMapper getObjectMapper() {
return new ObjectMapper();
}
}
/** * 已登录用户信息接口, * 使用业务key保存数据, 使用属性复制 */ @GetMapping("/admin-user") public AdminDTO admin(String token) { Admin admin = (Admin) redisTemplate.opsForValue() .get(RedisPrefix.TOKEN_KEY + token); AdminDTO adminDTO = new AdminDTO(); // 1.属性复制 BeanUtils.copyProperties(admin, adminDTO); return adminDTO; } // 业务key public interface RedisPrefix { /**代表用户认证token key*/ String TOKEN_KEY = "TOKEN:"; /** * 点赞数量业务key */ String VIDEO_LIKE_COUNT_PREFIX = "VIDEO_LIKE_COUNT:"; // 是否点赞业务key String USER_LIKED = "USER_LIKED:"; /** * 播放次数业务key */ String VIDEO_PLAYED_COUNT_PREFIX = "VIDEO_PLAYED_COUNT:"; /** * 用户不喜欢业务key */ String USER_DISLIKE_PREFIX = "USER_DISLIKE:"; String SESSION = "session:"; }
// 自定义接口, 运行时有效, 加载方法上, // 根据request对象,获取redis中的token注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequiredToken { } /** * 自定义拦截器, 拦截所有带有自定义注解的方法请求 */ @Configuration public class MvcConfig implements WebMvcConfigurer { @Autowired // 注入自定义拦截器 private TokenInterceptors tokenInterceptors; @Override public void addInterceptors(InterceptorRegistry registry) { // 拦截所有带有自定义注解的方法请求 registry.addInterceptor(tokenInterceptors) .addPathPatterns("/**"); } } /** * 自定义拦截器,保存用户信息和token */ @Component @Slf4j public class TokenInterceptors implements HandlerInterceptor { @Autowired private StringRedisTemplate redisTemplate; @Autowired private ObjectMapper objectMapper; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 判断方法上是否有自定义注解 RequiredToken boolean requiredToken = ((HandlerMethod) handler).getMethod().isAnnotationPresent(RequiredToken.class); if (requiredToken) { // 1 获取token信息 String token = request.getParameter("token"); log.info("拦截器获取的token信息为{}", token); // 2设置保存的业务key String tokenKey = "session_" + token; // 3 根据token获取用户信息 String userStr = redisTemplate.opsForValue().get(tokenKey); User user = objectMapper.readValue(userStr, User.class); // 获取不到用户信息 if (user == null) throw new RuntimeException("无效的token"); // 4 保存到请求上下文中 request.setAttribute("token", token); request.setAttribute("user", user); } return true; } }
/** * 自定义过滤器配置, 设置需要token才可以访问的接口 */ @Component public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> { @Autowired private StringRedisTemplate redisTemplate; public TokenGatewayFilterFactory() { super(Config.class); } /** * @param config 就是本类中Config的对象 * @return */ @Override public GatewayFilter apply(Config config) { return new GatewayFilter() { /** * 在网关中配置 -Token就可以使用了 * @param exchange 交换机,包装的request,response * @param chain 放行功能 * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 当requiredToken为True的时候才认证 if (config.requiredToken) { if (exchange.getRequest().getQueryParams().get(0) == null) throw new RuntimeException("非法异常!"); // 获取token信息 String token = exchange.getRequest().getQueryParams().get("token").get(0); // 判断token是否存在,不存在直接抛出异常 if (!redisTemplate.hasKey(RedisPreFix.TOKEN_KEY + token)) throw new RuntimeException("不合法的令牌!"); } return chain.filter(exchange); } }; } /** * 用来配置将使用filter时指定值赋值给Config中哪个属性, 多个参数时按顺序返回 */ @Override public List<String> shortcutFieldOrder() { return Arrays.asList("requiredToken", "name"); } /** * 内部类,指定配置中的token需要的参数 */ public static class Config { // 默认为false private boolean requiredToken; public boolean isRequiredToken() { return requiredToken; } public void setRequiredToken(boolean requiredToken) { this.requiredToken = requiredToken; } } }
// 全局路由配置 server: port: 9999 spring: application: name: API-GATEWAY cloud: nacos: server-addr: 192.168.216.128:8848 gateway: routes: #断言 用来配置路由规则 id uri path #admins router - id: admins_router uri: lb://API-ADMINS #负载均衡写法 predicates: #采用这种路径断言办法,请求进入网关断言,但是不能已这种方式发送给controller,因为没有此路径的请求 - Path=/admin/demos,/admin/tokens,/admin/admin-user,/admin/tokens/** filters: - StripPrefix=1 #去掉请求前缀的filter =写int类型 1代表去掉1级前缀 2代表去掉两级 #categary router - id: categary_router uri: lb://API-CATEGARYS predicates: - Path=/categary/** filters: - StripPrefix=1 - Token=true #users router - id: users_router uri: lb://API-USERS predicates: - Path=/admin/users/** filters: - StripPrefix=1 - Token=true #videos router - id: videos_router uri: lb://API-VIDEOS predicates: - Path=/admin/videos/** filters: - StripPrefix=1 - Token=true #跨域配置 globalcors: cors-configurations: '[/**]': allowedOrigins: "*" allowedMethods: "*" allowedHeaders: "*" #配置redis用来做token验证的 redis: port: 6379 host: 192.168.216.128 database: 0 password: 123456
/** * 全局异常配置, 实现ErrorWebExceptionHandler接口 */ @Configuration public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler { @Override //参数1: request response ex:出现异常时异常对象 public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { Map<String, String> result = new HashMap<>(); //1.获取响应对象 ServerHttpResponse response = exchange.getResponse(); //2.response是否结束 多个异常处理时候 if (response.isCommitted()) { return Mono.error(ex); } //2.设置响应头类型 response.getHeaders().setContentType(MediaType.APPLICATION_JSON); //3.设置响应状态吗 if (ex instanceof IllegalTokenException) { response.setStatusCode(HttpStatus.FORBIDDEN); } else { response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); } //4.设置响应内容 return response .writeWith(Mono.fromSupplier(() -> { DataBufferFactory bufferFactory = response.bufferFactory(); result.put("msg", ex.getMessage()); ObjectMapper objectMapper = new ObjectMapper(); try { return bufferFactory.wrap(objectMapper.writeValueAsBytes(result)); } catch (JsonProcessingException e) { e.printStackTrace(); return null; } })); } }
/** * 用户文件上传 * * @param file 上传文件 * @param video 视频对象需要的参数 * @param category_id 分类id * @param request token验证获取用户信息 * @return */ @PostMapping("/user/videos") @RequiredToken public Video publishVideos(MultipartFile file, Video video, Integer category_id, HttpServletRequest request) throws IOException { // 1.获取文件原始名称 getOriginalFilename() String originalFilename = file.getOriginalFilename(); // 2 获取文件名称后缀, .mp4, 没有点 添加上 String ext = FilenameUtils.getExtension(originalFilename); // 3 生成uuid String uuidFileName = UUID.randomUUID().toString().replace("-", ""); // 4 生成uuid文件名名称 如 20220714.MP4 String newFileName = uuidFileName + "" + ext; // 5 上传阿里云oss, 获取路径 String url = OSSUtils.upload(file.getInputStream(), "videos", newFileName); log.info("上传成功返回的路径为:{}", url); // 使用阿里云截取某一帧作为封面 String cover = url + "?x-oss-process=video/snapshot,t_30000,f_jpg,w_0,h_0,m_fast,ar_auto"; log.info("阿里云oss根据ur1截取视频封面: {}", cover); // 获取用户信息 User user = (User) request.getAttribute("user"); video.setUid(user.getId()); // 6 设置视频信息 video.setLink(url); video.setCover(cover); video.setCategoryId(category_id); // 7 调用video微服务保存数据库信息 Video videoResult = videosClient.publish(video); log.info("保存成功之后返回的视频信息:{}", videoResult); return videoResult; } VideoClient层代码, 转发到消费者代码 /** controller * 添加一条视频 * @param video * @return */ @PostMapping("publish") public Video publish(@RequestBody Video video) { log.info("接收到的video对象为:{}", video); return videoService.insert(video); } Service层, mq对象 @Override public Video insert(Video video) { // 更新数据 video.setCreatedAt(new Date()); video.setUpdatedAt(new Date()); video.setLikes(0L); // video.setCategory(categoryClient.findById(video.getCategoryId())); // 保存到数据库 videoMapper.insert(video); // 将video对象转为videoVo对象, VideoVo videoVO = this.getVideoVO(video); // 使用mq异步处理,提升系统响应, 将视频信息写入es索引库 rabbitTemplate.convertAndSend("videos", "", JSONUtils.writeJSON(videoVO)); return video; } 服务消费者代码, 在search微服务里面, 同时保存到el中, 方便搜索 /** * 服务消费者 */ @Component @Slf4j public class VideoConsumer { @Autowired @Qualifier("elasticsearchClient") private RestHighLevelClient restHighLevelClient; @Autowired private ObjectMapper objectMapper; @RabbitListener(bindings = @QueueBinding( value = @Queue, // 绑定队列 exchange = @Exchange(name = "videos", type = "fanout") // 绑定交换机, 默认交换机 )) public void receive(String message) throws IOException { log.info("消费者MQ接收到的video信息为:{}", message); // 1 将mq中的 json转为对象 VideoVo videoVo = objectMapper.readValue(message, VideoVo.class); // 2 创建ES中索引请求对象, 参数1 操作索引, 参数2: 索引类型, 参数3: 文当id IndexRequest indexRequest = new IndexRequest("video", "video", videoVo.getId().toString()); // 3 设置ES文档的内容 indexRequest.source(message, XContentType.JSON); // 4 执行索引操作 IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT); log.info(" video录入ES中的信息为:{}", indexResponse); } }
/** * ES配置类, 不用配置文件中配置 */ @Configuration public class RestClientConfig extends AbstractElasticsearchConfiguration { @Bean @Override public RestHighLevelClient elasticsearchClient() { ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("localhost:9200") // 端口号 .build(); return RestClients.create(clientConfiguration).rest(); } } @Service public class VideoServiceSearchImpl implements VideoSearchService { @Qualifier("elasticsearchClient") @Autowired private RestHighLevelClient restHighLevelClient; @Autowired private ObjectMapper objectMapper; /** * 根据ES分页搜索 * * @param q * @param page * @param rows * @return */ @Override public Map<String, Object> videos(String q, Integer page, Integer rows) { HashMap<String, Object> map = new HashMap<>(); // 1. 计算分页 int start = (page - 1) * rows; // 2.创建索引对象 SearchRequest request = new SearchRequest(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder .from(start).size(rows) // 分页查询 .query(QueryBuilders.termQuery("title", q)); // 根据name字段分词搜索 // 3 设置搜索索引, 搜索类型 request.indices("video").types("video").source(searchSourceBuilder); SearchResponse searchResponse = null; //执行搜索 try { searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT); // 获取条数 Long totalHits = searchResponse.getHits().getTotalHits().value; // 创建VO集合 ArrayList<VideoVo> videoVos = new ArrayList<>(); if (totalHits > 0) { map.put("total_count", totalHits); // 获取数组对象 SearchHit[] hits = searchResponse.getHits().getHits(); // 遍历结果,保存到集合中 for (SearchHit hit : hits) { String videoVOJSON = hit.getSourceAsString(); VideoVo videoVo = objectMapper.readValue(videoVOJSON, VideoVo.class); videoVo.setId(Integer.parseInt(hit.getId())); videoVos.add(videoVo); } } map.put("items", videoVos); return map; } catch (IOException e) { e.printStackTrace(); } return map; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。