|
|
@@ -0,0 +1,270 @@
|
|
|
+package com.webflux.launchadmin.mysql.service.applet;
|
|
|
+
|
|
|
+import cn.hutool.core.util.RandomUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.webflux.launchadmin.global.BaseException;
|
|
|
+import com.webflux.launchadmin.mysql.controller.applet.res.AppletH5Data;
|
|
|
+import com.webflux.launchadmin.mysql.controller.applet.res.DataRes;
|
|
|
+import com.webflux.launchadmin.mysql.entity.applet.AppletIds;
|
|
|
+import com.webflux.launchadmin.mysql.entity.planNew.PlanNewCommon;
|
|
|
+import com.webflux.launchadmin.mysql.service.sourceMaterial.SourceMaterialInterface;
|
|
|
+import com.webflux.launchcommon.returnObj.Paged;
|
|
|
+import com.webflux.launchcommon.returnObj.RStatus;
|
|
|
+import io.lettuce.core.RedisClient;
|
|
|
+import io.lettuce.core.api.StatefulRedisConnection;
|
|
|
+import io.lettuce.core.api.reactive.RedisReactiveCommands;
|
|
|
+import jakarta.annotation.PostConstruct;
|
|
|
+import jakarta.annotation.Resource;
|
|
|
+import org.apache.logging.log4j.spi.LoggerContextKey;
|
|
|
+import org.jetbrains.annotations.NotNull;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
+import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
|
|
+import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
|
|
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
|
|
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
|
|
+import org.springframework.data.redis.core.ReactiveRedisTemplate;
|
|
|
+import org.springframework.data.redis.serializer.RedisSerializationContext;
|
|
|
+import org.springframework.data.redis.serializer.StringRedisSerializer;
|
|
|
+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.Service;
|
|
|
+import reactor.core.publisher.Flux;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.net.URI;
|
|
|
+import java.net.http.HttpClient;
|
|
|
+import java.net.http.HttpRequest;
|
|
|
+import java.net.http.HttpResponse;
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
+import java.util.concurrent.locks.Lock;
|
|
|
+import java.util.concurrent.locks.ReentrantLock;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class AppletService implements AppletServiceInterface {
|
|
|
+
|
|
|
+ private static final String keyEnd="_access_token";
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private R2dbcEntityTemplate r2dbcEntityTemplate;
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ReactiveRedisTemplate<String,Object> reactiveRedisTemplates;
|
|
|
+ private static final String wxPosturl="https://api.weixin.qq.com/wxa/generatescheme?access_token=";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存applet
|
|
|
+ */
|
|
|
+ private static final CopyOnWriteArrayList<String> lists=new CopyOnWriteArrayList<>();
|
|
|
+
|
|
|
+ private RedisReactiveCommands<String, String> reactiveRedisTemplateApplet;
|
|
|
+
|
|
|
+ public AppletService() {
|
|
|
+ // 手动创建 RedisClient 实例并连接到 Redis 服务器
|
|
|
+ connection();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void connection(){
|
|
|
+ RedisClient redisClient = RedisClient.create("redis://:FQYxUmMtBCcyS22@r-bp1583bbf9fdfee4pd.redis.rds.aliyuncs.com:56565/99");
|
|
|
+ StatefulRedisConnection<String, String> connection = redisClient.connect();
|
|
|
+ // 获取 Reactive API 命令接口
|
|
|
+ this.reactiveRedisTemplateApplet = connection.reactive();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 核对+初始化
|
|
|
+ */
|
|
|
+ @Scheduled(fixedDelay = 160000)
|
|
|
+ public void getAppletIds(){
|
|
|
+ r2dbcEntityTemplate.select(AppletIds.class).all().collectList().flatMap(list->{
|
|
|
+ List<String> collect = list.stream().map(AppletIds::appletId).collect(Collectors.toList());
|
|
|
+ if (!collect.isEmpty()) {
|
|
|
+ List<String> addIps = getAddIps(collect, lists);
|
|
|
+ if (!addIps.isEmpty()) {
|
|
|
+ lists.addAll(addIps);
|
|
|
+ }
|
|
|
+ List<String> removers = getAddIps(lists, collect);
|
|
|
+ if(!removers.isEmpty()){
|
|
|
+ lists.removeAll(removers);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Mono.just(true);
|
|
|
+ }).subscribe();
|
|
|
+ }
|
|
|
+ @NotNull
|
|
|
+ private List<String> getAddIps(List<String> ips, List<String> ip_white_list) {
|
|
|
+ return ips.stream().filter(o -> !ip_white_list.contains(o)).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+ @Autowired
|
|
|
+ private SourceMaterialInterface sourceMaterialInterface;
|
|
|
+ private final Lock lock = new ReentrantLock();
|
|
|
+ private Mono<String> ping(){
|
|
|
+ try {
|
|
|
+ lock.lock();
|
|
|
+ return reactiveRedisTemplateApplet.ping().flatMap(response -> {
|
|
|
+ if(!"PONG".equals(response)){
|
|
|
+ connection();
|
|
|
+ }
|
|
|
+ return Mono.just("SUCCESS");
|
|
|
+ });
|
|
|
+ }finally {
|
|
|
+ lock.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public Mono<String> getTonke(String appletIds) {
|
|
|
+ return ping().then(reactiveRedisTemplateApplet.get(appletIds + keyEnd)
|
|
|
+ .switchIfEmpty(Mono.error(new BaseException("获取token结果为空 appletId:"+appletIds)))
|
|
|
+ .flatMap(ret -> {
|
|
|
+ if (Objects.nonNull(ret) &&!"".equals(ret) ) {
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(ret);
|
|
|
+ if (jsonObject.containsKey("access_token")) {
|
|
|
+ String access_token = jsonObject.get("access_token", String.class);
|
|
|
+ return Mono.just(access_token);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Mono.error(new BaseException("redis获取token结果异常 body::"+ret));
|
|
|
+ }).onErrorResume(throwable ->
|
|
|
+ Mono.error(new BaseException(StrUtil
|
|
|
+ .format("获取:{} 失败:{}", appletIds, throwable.getMessage())))));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<String> getTonke1(String appletIds) {
|
|
|
+ return reactiveRedisTemplates.opsForValue().get("ip_rate_limit_range").flatMap(f-> Mono.just(String.valueOf(f)));
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public Mono<String> getWxScheme(Mono<String> token){
|
|
|
+ var bodys= """
|
|
|
+ { "expire_interval":1,
|
|
|
+ "expire_type":1 }
|
|
|
+ """;
|
|
|
+ return token.flatMap(tk->{
|
|
|
+ HttpClient client = HttpClient.newHttpClient();
|
|
|
+ HttpRequest request = HttpRequest.newBuilder()
|
|
|
+ .uri(URI.create(wxPosturl+tk))
|
|
|
+ .POST(HttpRequest.BodyPublishers.ofString(bodys))
|
|
|
+ .header("Content-Type", "application/json")
|
|
|
+ .build();
|
|
|
+ HttpResponse<String> response = null;
|
|
|
+ try {
|
|
|
+ response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+ String body = response.body();
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.containsKey("errcode")
|
|
|
+ && jsonObject.get("errcode", Integer.class) == 0
|
|
|
+ && jsonObject.containsKey("openlink")) {
|
|
|
+ String openlink = jsonObject.get("openlink", String.class);
|
|
|
+ return Mono.just(openlink);
|
|
|
+ }else {
|
|
|
+ return Mono.error(new BaseException(StrUtil.format("获取openlink异常 body::{}",body)));
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ return Mono.error(new BaseException("请求wx generatescheme io 异常 :"+e.getMessage()));
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ return Mono.error(new BaseException("请求wx generatescheme Interrupted 异常 :"+e.getMessage()));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<RStatus<Paged<AppletIds>>> getList() {
|
|
|
+ return sourceMaterialInterface.page(List.of(), AppletIds.class, 1000, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<RStatus<Boolean>> add(String appletId) {
|
|
|
+ Mono<Long> applet_id = r2dbcEntityTemplate.count(Query.query(Criteria.where("applet_id").is(appletId)), AppletIds.class);
|
|
|
+ Mono<Boolean> mono = applet_id.flatMap(f -> {
|
|
|
+ if (f > 0) {
|
|
|
+ return Mono.error(new BaseException("小程序id 已存在"));
|
|
|
+ }
|
|
|
+ AppletIds appletIds = new AppletIds(null, appletId);
|
|
|
+ return r2dbcEntityTemplate.insert(appletIds).flatMap(appletIds1 -> {
|
|
|
+ lists.add(appletId);
|
|
|
+ return Mono.just(true);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ return RStatus.success(mono);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<RStatus<Boolean>> delete(Long id) {
|
|
|
+ Mono<Boolean> mono = r2dbcEntityTemplate.selectOne(Query.query(Criteria.where("id").is(id)), AppletIds.class)
|
|
|
+ .switchIfEmpty(Mono.error(new BaseException("id 异常")))
|
|
|
+ .flatMap(obj -> r2dbcEntityTemplate.delete(Query.query(Criteria.where("id").is(id)), AppletIds.class)
|
|
|
+ .flatMap(num -> {
|
|
|
+ if(num>0){
|
|
|
+ lists.remove(obj.appletId());
|
|
|
+ return Mono.just(false);
|
|
|
+ }
|
|
|
+ return Mono.just(true);
|
|
|
+ }));
|
|
|
+ return RStatus.success(mono);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Mono<String> getOLink(String unique){
|
|
|
+ if (lists.isEmpty()) {
|
|
|
+ return Mono.error(new BaseException("请维护 appletId "));
|
|
|
+ }
|
|
|
+ String s = lists.get(RandomUtil.randomInt(0, lists.size()));
|
|
|
+ return getWxScheme(getTonke(s))
|
|
|
+ .flatMap(link->
|
|
|
+ reactiveRedisTemplates.opsForValue()
|
|
|
+ .set(unique, s, Duration.ofMinutes(getSecondsUntilNextMidnight()))
|
|
|
+ .flatMap(f->Mono.just(link)));
|
|
|
+ }
|
|
|
+ private static long getSecondsUntilNextMidnight() {
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime midnight = now.toLocalDate().plusDays(1).atStartOfDay().plusHours(0).plusSeconds(1).plusMinutes(0);
|
|
|
+ return Duration.between(now, midnight).toMinutes();
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public Mono<RStatus<AppletH5Data>> getOpenLink(String unique) {
|
|
|
+ Mono<AppletH5Data> appletH5DataMono1 = reactiveRedisTemplates
|
|
|
+ .opsForValue().get(unique)
|
|
|
+ .switchIfEmpty(getOLink(unique))
|
|
|
+ .flatMap(s -> {
|
|
|
+ AppletH5Data appletH5Data = new AppletH5Data();
|
|
|
+ appletH5Data.setOpenlink(String.valueOf(s));
|
|
|
+ return Mono.just(appletH5Data);
|
|
|
+ });
|
|
|
+ return RStatus.success(appletH5DataMono1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<RStatus<String>> pushState(String unique, Long id, String state) {
|
|
|
+ return reactiveRedisTemplates.opsForValue().set(unique+"-"+id,state,Duration.ofHours(48)).flatMap(f->{
|
|
|
+ if (f) {
|
|
|
+ return RStatus.success(Mono.just("SUCCESS"));
|
|
|
+ }else {
|
|
|
+ return RStatus.fail(Mono.just("FAIL"));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<RStatus<DataRes>> getUniqueState(String unique, Long id) {
|
|
|
+ Mono<DataRes> dataResMono = reactiveRedisTemplates.opsForValue().get(unique +"-"+ id).flatMap(f -> {
|
|
|
+ DataRes dataRes = new DataRes();
|
|
|
+ if (Objects.nonNull(f) && "1".equals(f)) {
|
|
|
+ dataRes.setState(1);
|
|
|
+ } else if (Objects.nonNull(f) && "2".equals(f)) {
|
|
|
+ dataRes.setState(2);
|
|
|
+ }else {
|
|
|
+ dataRes.setState(0);
|
|
|
+ }
|
|
|
+ return Mono.just(dataRes);
|
|
|
+ });
|
|
|
+ return RStatus.success(dataResMono);
|
|
|
+ }
|
|
|
+}
|