|
|
@@ -0,0 +1,199 @@
|
|
|
+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.planNew.PlanNewCommon;
|
|
|
+import com.weblux.launchredis.utils.PlanNewCommonRedisKey;
|
|
|
+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.List;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Component
|
|
|
+@Slf4j
|
|
|
+public class SyncPlanNewCommonListenToRedis {
|
|
|
+ @Resource
|
|
|
+ private R2dbcEntityTemplate template;
|
|
|
+ @Resource
|
|
|
+ private ReactiveRedisTemplate<String,String> reactiveRedisTemplate;
|
|
|
+
|
|
|
+ private static final String COLUMN="id,name,code,type,poster_template_id,group_id,config_json,import_domain,preview_url";
|
|
|
+ private static final String[] COLUMNARRAY=COLUMN.split(",");
|
|
|
+ /**
|
|
|
+ *同步裂变计划
|
|
|
+ */
|
|
|
+ @Scheduled(fixedDelay = 170000)
|
|
|
+ @SchedulerLock(name = "launch:SyncPlanNewCommonListenToRedis:syncPlanNewCommonPlan", lockAtLeastFor = "PT5S", lockAtMostFor = "PT15M")
|
|
|
+ public void syncPlanNewCommonPlan(){
|
|
|
+ Mono<List<PlanNewCommon>> listMono = template
|
|
|
+ .select(Query.query(Criteria.empty())
|
|
|
+ .columns(COLUMNARRAY),PlanNewCommon.class)
|
|
|
+ .collectList();
|
|
|
+ listMono.flatMap(list -> {
|
|
|
+ List<String> collect = list.stream().map(PlanNewCommon::code).collect(Collectors.toList());
|
|
|
+ Mono<List<String>> listMono1 = reactiveRedisTemplate
|
|
|
+ .opsForSet()
|
|
|
+ .members(PlanNewCommonRedisKey.PLAN_NEW_COMMON_INFO_SET)
|
|
|
+ .collectList()
|
|
|
+ .switchIfEmpty(Mono.just(List.of()));
|
|
|
+ return Mono.zip(Mono.just(collect), listMono1, Mono.just(list)).flatMap(tupleN -> {
|
|
|
+ var mids = tupleN.getT1();
|
|
|
+ var rids = tupleN.getT2();
|
|
|
+ var planNews = tupleN.getT3();
|
|
|
+ // + 全部添加一遍 预防构建宕机 数据不一致 省略数据set hash 一致性判断
|
|
|
+ if (!planNews.isEmpty()) { //增加io 减少逻辑 保证数据一致性
|
|
|
+ Flux.fromIterable(planNews)
|
|
|
+ .flatMap(this::setRedisValue).collectList().subscribe();
|
|
|
+ }
|
|
|
+// List<String> addIds = mids.stream().filter(mid -> !rids.contains(mid)).collect(Collectors.toList());
|
|
|
+// if (!addIds.isEmpty()) {
|
|
|
+// List<PlanNewCommon> addPlanNew = planNews.stream().filter(f -> addIds.contains(f.code())).collect(Collectors.toList());
|
|
|
+// if (!addPlanNew.isEmpty()) {
|
|
|
+// Flux.fromIterable(addPlanNew)
|
|
|
+// .flatMap(this::setRedisValue).collectList().subscribe();
|
|
|
+// }
|
|
|
+// }
|
|
|
+ // -
|
|
|
+ List<String> removeIds = rids.stream().filter(rid -> !mids.contains(rid)).collect(Collectors.toList());
|
|
|
+ if (!removeIds.isEmpty()) {
|
|
|
+ Flux.fromIterable(removeIds).flatMap(this::getDelete).subscribe();
|
|
|
+ }
|
|
|
+ return Mono.empty();
|
|
|
+ }).then();
|
|
|
+ }).subscribe();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @NotNull
|
|
|
+ public Mono<Void> setRedisValue(PlanNewCommon planNew) {
|
|
|
+ reactiveRedisTemplate.opsForSet().add(PlanNewCommonRedisKey.PLAN_NEW_COMMON_INFO_SET, planNew.code()).subscribe();
|
|
|
+ String json= getPlanNewToString(planNew);
|
|
|
+ reactiveRedisTemplate.opsForHash().put(PlanNewCommonRedisKey.PLAN_NEW_COMMON_INFO_HASH,planNew.code(), json).subscribe();
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+ @NotNull
|
|
|
+ public Mono<Void> getDelete(String code) {
|
|
|
+ reactiveRedisTemplate.opsForSet().remove(PlanNewCommonRedisKey.PLAN_NEW_COMMON_INFO_SET, code).subscribe();
|
|
|
+ reactiveRedisTemplate.opsForHash().remove(PlanNewCommonRedisKey.PLAN_NEW_COMMON_INFO_HASH , code).subscribe();
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getPlanNewToString(PlanNewCommon 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("PlanNew 将 record 转换为 JSON 字符串 异常:::"+e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过code从数据库获取PlanNew并缓存redis
|
|
|
+ * @param code
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Mono<PlanNewCommon> byCodeAndMysqlGetPlanNew(String code){
|
|
|
+ Mono<PlanNewCommon> planNewMono = template
|
|
|
+ .selectOne(Query.query(Criteria.where("code").is(code))
|
|
|
+ .columns(COLUMNARRAY), PlanNewCommon.class)//.columns(column.split(","))
|
|
|
+ .switchIfEmpty(Mono.error(new BaseException("计划code异常 返回结果为空"))) //Mono.error(new BaseException("查询海报模版 id:"+f.posterTemplateId()+"=>空"))
|
|
|
+ .onErrorResume(throwable -> Mono.error(new BaseException(throwable.getMessage())));
|
|
|
+ return planNewMono.flatMap(planNew -> {
|
|
|
+ setRedisValue(planNew).subscribe();
|
|
|
+ return Mono.just(planNew);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过redis获取缓存 空或失败 从mysql中取
|
|
|
+ * @param code
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Mono<PlanNewCommon> byRedisGetPlanNewCommonIfErByMysql(String code){
|
|
|
+ return reactiveRedisTemplate.opsForHash()
|
|
|
+ .get(PlanNewCommonRedisKey.PLAN_NEW_COMMON_INFO_HASH , code)
|
|
|
+ .switchIfEmpty(Mono.just(""))
|
|
|
+ .onErrorResume(throwable -> {
|
|
|
+ log.error("接口异常 {} e = {}","获取redis PlanNewCommon 失败 暂时从Mysql获取内容" , LogExceptionStackTrace.erroStackTrace(throwable));
|
|
|
+ return Mono.just("");
|
|
|
+ })
|
|
|
+ .flatMap(s -> {
|
|
|
+ Mono<PlanNewCommon> planNewMono;
|
|
|
+ if(!"".equals(s) && Objects.nonNull(s)){
|
|
|
+ try {
|
|
|
+ PlanNewCommon planNew = strParsePlanNew(String.valueOf(s));
|
|
|
+ planNewMono=Mono.just(planNew);
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("接口异常 {} e = {}","获取redis Value 解析PlanNewCommon失败 暂时从Mysql获取内容" , LogExceptionStackTrace.erroStackTrace(e));
|
|
|
+ planNewMono = byCodeAndMysqlGetPlanNew(code);
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ planNewMono = byCodeAndMysqlGetPlanNew(code);
|
|
|
+ }
|
|
|
+ return planNewMono;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * str 转PlanNew
|
|
|
+ * @param str
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public PlanNewCommon strParsePlanNew(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, PlanNewCommon.class);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new BaseException(" 将 JSON字符串 转换为 PlanNewCommon 异常:::"+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());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|