|
|
@@ -0,0 +1,209 @@
|
|
|
+package com.webflux.launchadmin.mysql.task;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.core.JsonParser;
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.databind.DeserializationContext;
|
|
|
+import com.fasterxml.jackson.databind.JsonDeserializer;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.fasterxml.jackson.databind.SerializationFeature;
|
|
|
+import com.fasterxml.jackson.databind.module.SimpleModule;
|
|
|
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
|
+import com.plumelog.core.util.LogExceptionStackTrace;
|
|
|
+import com.webflux.launchadmin.global.BaseException;
|
|
|
+import com.webflux.launchadmin.mysql.entity.listening.ListeningPlanNew;
|
|
|
+import com.webflux.launchadmin.mysql.entity.planNew.Image;
|
|
|
+import com.weblux.launchredis.utils.FissionRedisKey;
|
|
|
+import com.weblux.launchredis.utils.ImageRedisKey;
|
|
|
+import com.weblux.launchredis.utils.LinteningAudioRedisKey;
|
|
|
+import com.weblux.launchredis.utils.LinteningRedisKey;
|
|
|
+import jakarta.annotation.Resource;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
|
|
|
+import org.jetbrains.annotations.NotNull;
|
|
|
+import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
|
|
+import org.springframework.data.redis.core.ReactiveRedisTemplate;
|
|
|
+import org.springframework.data.relational.core.query.Criteria;
|
|
|
+import org.springframework.data.relational.core.query.Query;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import reactor.core.publisher.Flux;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.time.ZonedDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Component
|
|
|
+@Slf4j
|
|
|
+public class SyncImageToRedis {
|
|
|
+ @Resource
|
|
|
+ private R2dbcEntityTemplate template;
|
|
|
+ @Resource
|
|
|
+ private ReactiveRedisTemplate<String,String> reactiveRedisTemplate;
|
|
|
+
|
|
|
+
|
|
|
+ private static final String COLUMN="id,image,type,group_type,group_type_name,group_type_id,size_type";
|
|
|
+ private static final String[] COLUMNARRAY=COLUMN.split(",");
|
|
|
+ /**
|
|
|
+ *同步裂变计划
|
|
|
+ */
|
|
|
+// Mono<List<Image>> imageMono = template.select(Query.query(Criteria.where("type").is(1)
|
|
|
+// .and("group_type_id").is(f.groupId())
|
|
|
+// .and("deleted_at").isNull()), Image.class)
|
|
|
+// .onErrorResume(throwable -> Mono.error(new BaseException("获取群封面异常"))).collectList();
|
|
|
+ @Scheduled(fixedDelay = 120000)
|
|
|
+ @SchedulerLock(name = "launch:SyncImageToRedis:syncImagePlan", lockAtLeastFor = "PT5S", lockAtMostFor = "PT15M")
|
|
|
+ public void syncImagePlan(){
|
|
|
+ Query query = Query.query(Criteria
|
|
|
+ .where("deleted_at").isNull()
|
|
|
+ .and("group_type_id").isNotNull()
|
|
|
+ .and("type").isNotNull())
|
|
|
+ .columns(COLUMNARRAY);
|
|
|
+ Mono<List<Image>> listMono = template
|
|
|
+ .select(query,Image.class)
|
|
|
+ .collectList();
|
|
|
+ listMono.flatMap(list -> {
|
|
|
+ //根据groupId分组 在根据type分组
|
|
|
+ Map<Integer, List<Image>> collect1 = list.stream().collect(Collectors.groupingBy(Image::groupTypeId));
|
|
|
+ collect1.forEach(((x,y)->{
|
|
|
+ Map<Integer, List<Image>> collect = y.stream().collect(Collectors.groupingBy(Image::type));
|
|
|
+ collect.forEach((x1,y1)-> y1.forEach(image-> setRedisValue(image, x+"-"+x1).subscribe())); }));
|
|
|
+ return Mono.empty();
|
|
|
+ }).subscribe();
|
|
|
+ Query queryDelete = Query.query(Criteria
|
|
|
+ .where("deleted_at").isNotNull()
|
|
|
+ .and("group_type_id").isNotNull()
|
|
|
+ .and("type").isNotNull())
|
|
|
+ .columns(COLUMNARRAY);
|
|
|
+ Mono<List<Image>> listMonoDe = template
|
|
|
+ .select(queryDelete,Image.class)
|
|
|
+ .collectList();
|
|
|
+ listMonoDe.flatMap(list -> {
|
|
|
+ //根据groupId分组 在根据type分组
|
|
|
+ Map<Integer, List<Image>> collect1 = list.stream().collect(Collectors.groupingBy(Image::groupTypeId));
|
|
|
+ collect1.forEach(((x,y)->{
|
|
|
+ Map<Integer, List<Image>> collect = y.stream().collect(Collectors.groupingBy(Image::type));
|
|
|
+ collect.forEach((x1,y1)-> y1.forEach(image->
|
|
|
+ deleteRedisValue(image, x+"-"+x1).subscribe()
|
|
|
+ ));
|
|
|
+ }));
|
|
|
+ return Mono.empty();
|
|
|
+ }).subscribe();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public Mono<Void> deleteRedisValue(Image image,String key) {
|
|
|
+ reactiveRedisTemplate.opsForSet().remove(ImageRedisKey.IMAGE_INFO_SET+key, String.valueOf(image.id())).subscribe();
|
|
|
+ reactiveRedisTemplate.opsForHash().remove(ImageRedisKey.IMAGE_INFO_HASH, String.valueOf(image.id())).subscribe();
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Mono<Void> setRedisValue(Image image,String key) {
|
|
|
+ reactiveRedisTemplate.opsForSet().add(ImageRedisKey.IMAGE_INFO_SET+key,String.valueOf(image.id()) ).subscribe();
|
|
|
+ String json= getImageToString(image);
|
|
|
+ reactiveRedisTemplate.opsForHash().put(ImageRedisKey.IMAGE_INFO_HASH, String.valueOf(image.id()), json).subscribe();
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过code从数据库获取Image并缓存redis
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Mono<List<Image>> byCodeAndMysqlGetImage(String groupId,String type){
|
|
|
+ Mono<List<Image>> imageMono = template.select(Query.query(Criteria.where("type").is(type)
|
|
|
+ .and("group_type_id").is(groupId)
|
|
|
+ .and("deleted_at").isNull()), Image.class)
|
|
|
+ .onErrorResume(throwable -> Mono.error(new BaseException("获取群封面异常"))).collectList();
|
|
|
+ return imageMono.flatMap(images -> {
|
|
|
+ images.forEach(image -> {
|
|
|
+ setRedisValue(image,image.groupTypeId()+"-"+image.type()).subscribe();
|
|
|
+ });
|
|
|
+ return Mono.just(images);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过redis获取缓存 空或失败 从mysql中取
|
|
|
+ * @param groupId
|
|
|
+ * @param type
|
|
|
+ * @return {@link Mono}<{@link List}<{@link Image}>>
|
|
|
+ */
|
|
|
+ public Mono<List<Image>> byRedisGetImageIfErByMysql(String groupId,String type){
|
|
|
+ String key = groupId+"-"+type;
|
|
|
+ return reactiveRedisTemplate.opsForSet()
|
|
|
+ .members(ImageRedisKey.IMAGE_INFO_SET + key)
|
|
|
+ .collectList()
|
|
|
+ .switchIfEmpty(Mono.just(List.of()))
|
|
|
+ .onErrorResume(throwable -> {
|
|
|
+ log.error("接口异常 {} e = {}", "获取redis Image 失败 暂时从Mysql获取内容", LogExceptionStackTrace.erroStackTrace(throwable));
|
|
|
+ return Mono.just(List.of());
|
|
|
+ }).flatMap(listKey -> {
|
|
|
+ if (Objects.nonNull(listKey) && !listKey.isEmpty()) {
|
|
|
+ return Flux.fromIterable(listKey).flatMap(f -> reactiveRedisTemplate
|
|
|
+ .opsForHash()
|
|
|
+ .get(ImageRedisKey.IMAGE_INFO_HASH, f))
|
|
|
+ .flatMap(str -> {
|
|
|
+ if (Objects.nonNull(str) && !"".equals(str)) {
|
|
|
+ return Mono.just(strParseImage(String.valueOf(str)));
|
|
|
+ }
|
|
|
+ return Mono.empty();
|
|
|
+ }).collectList();
|
|
|
+ } else {
|
|
|
+ return byCodeAndMysqlGetImage(groupId, type);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private String getImageToString(Image planNew) {
|
|
|
+ ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+ objectMapper.registerModule(new JavaTimeModule());
|
|
|
+ objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
|
|
+ try {
|
|
|
+ // 将 record 转换为 JSON 字符串
|
|
|
+ return objectMapper.writeValueAsString(planNew);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new BaseException("Image 将 record 转换为 JSON 字符串 异常:::"+e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * str 转Image
|
|
|
+ * @param str
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Image strParseImage(String str) {
|
|
|
+ // 创建 ObjectMapper 实例并配置
|
|
|
+ ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+ objectMapper.registerModule(new JavaTimeModule());
|
|
|
+ SimpleModule module = new SimpleModule();
|
|
|
+ module.addDeserializer(ZonedDateTime.class, new CustomZonedDateTimeDeserializer());
|
|
|
+ objectMapper.registerModule(module);
|
|
|
+ try {
|
|
|
+ // 将 JSON 字符串转换为 record
|
|
|
+ return objectMapper.readValue(str, Image.class);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new BaseException(" 将 JSON字符串 转换为 Image 异常:::"+e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 自定义反序列化器
|
|
|
+ public static class CustomZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {
|
|
|
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ @Override
|
|
|
+ public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt)
|
|
|
+ throws IOException, JsonProcessingException {
|
|
|
+ String date = p.getText();
|
|
|
+ LocalDateTime localDateTime = LocalDateTime.parse(date, FORMATTER);
|
|
|
+ // 使用系统默认时区,可以根据需要修改
|
|
|
+ return ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|