Browse Source

代码优化

Woody 1 month ago
parent
commit
b8b38a7413

+ 66 - 0
framework-common/src/main/java/com/chelvc/framework/common/model/BiPair.java

@@ -0,0 +1,66 @@
+package com.chelvc.framework.common.model;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import lombok.Data;
+
+/**
+ * 多部分对象
+ *
+ * @author Woody
+ * @date 2025/3/15
+ */
+@Data
+public class BiPair<L, M, R> implements Serializable {
+    private L left;
+    private M middle;
+    private R right;
+
+    public BiPair() {
+        this(null, null, null);
+    }
+
+    public BiPair(L left, M middle, R right) {
+        this.left = left;
+        this.middle = middle;
+        this.right = right;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.left, this.middle, this.right);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (!(obj instanceof BiPair)) {
+            return false;
+        }
+        BiPair<?, ?, ?> other = (BiPair<?, ?, ?>) obj;
+        return Objects.equals(this.left, other.getLeft()) && Objects.equals(this.middle, other.getMiddle())
+                && Objects.equals(this.right, other.getRight());
+    }
+
+    @Override
+    public String toString() {
+        return "(" + this.left + "," + this.middle + "," + this.right + ")";
+    }
+
+    /**
+     * 构建多部分对象实例
+     *
+     * @param left   左边部分
+     * @param middle 中间部分
+     * @param right  右边部分
+     * @param <L>    左边类型
+     * @param <M>    中间类型
+     * @param <R>    右边类型
+     * @return 多部分对象实例
+     */
+    public static <L, M, R> BiPair<L, M, R> of(L left, M middle, R right) {
+        return new BiPair<>(left, middle, right);
+    }
+}

+ 1 - 1
framework-common/src/main/java/com/chelvc/framework/common/model/Pair.java

@@ -52,7 +52,7 @@ public class Pair<K, V> implements Map.Entry<K, V>, Serializable {
 
     @Override
     public String toString() {
-        return "(" + this.key + ',' + this.value + ')';
+        return "(" + this.key + "," + this.value + ")";
     }
 
     /**

+ 10 - 0
framework-group/src/main/java/com/chelvc/framework/group/GroupHandler.java

@@ -17,4 +17,14 @@ public interface GroupHandler {
      * @return 分组结果
      */
     Caps execute(String scene, Object... args);
+
+    /**
+     * 分组完成回调方法
+     *
+     * @param scene 分组场景
+     * @param group 分组结果
+     * @param args  方法输入/出参数数组
+     */
+    default void complete(String scene, Caps group, Object... args) {
+    }
 }

+ 33 - 15
framework-group/src/main/java/com/chelvc/framework/group/context/GroupMethodInterceptor.java → framework-group/src/main/java/com/chelvc/framework/group/GroupingInterceptor.java

@@ -1,4 +1,4 @@
-package com.chelvc.framework.group.context;
+package com.chelvc.framework.group;
 
 import java.util.Collections;
 import java.util.List;
@@ -11,14 +11,14 @@ import java.util.stream.Stream;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.context.ThreadContextHolder;
+import com.chelvc.framework.common.model.BiPair;
 import com.chelvc.framework.common.model.Caps;
 import com.chelvc.framework.common.model.Invoking;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
-import com.chelvc.framework.group.GroupHandler;
-import com.chelvc.framework.group.GroupStore;
 import com.chelvc.framework.group.annotation.Group;
 import com.chelvc.framework.group.annotation.Groups;
+import com.chelvc.framework.group.context.GroupContextHolder;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import lombok.RequiredArgsConstructor;
@@ -31,7 +31,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
 
 /**
- * 场景分组方法拦截器
+ * 场景分组拦截器
  *
  * @author Woody
  * @date 2024/11/24
@@ -40,7 +40,7 @@ import org.springframework.stereotype.Component;
 @Aspect
 @Component
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
-public class GroupMethodInterceptor {
+public class GroupingInterceptor {
     private final GroupStore store;
     private final ApplicationContext applicationContext;
     private final Map<Class<?>, GroupHandler> handlers = Maps.newConcurrentMap();
@@ -80,28 +80,46 @@ public class GroupMethodInterceptor {
      * @param args   分组执行参数
      */
     private void execute(List<Group> groups, Object... args) {
-        Map<String, Caps> stores = null;
+        // 执行场景分组
+        List<BiPair<Group, GroupHandler, Caps>> values = Lists.newLinkedList();
         for (Group group : groups) {
             try {
                 GroupHandler handler = this.initializeGroupHandler(group.handler());
                 Caps value = handler.execute(group.scene(), args);
-                if (value != null && group.storing()) {
-                    if (stores == null) {
-                        stores = Maps.newHashMapWithExpectedSize(groups.size());
-                    }
-                    stores.put(group.scene(), value);
-                } else {
+                if (!group.storing()) {
                     GroupContextHolder.setGroup(group.scene(), ObjectUtils.ifNull(value, Caps.NONE));
                 }
+                if (value != null) {
+                    values.add(BiPair.of(group, handler, value));
+                }
             } catch (Exception e) {
-                log.error("Group handle failed: {}", group.scene(), e);
+                log.error("Group execute failed: {}", group.scene(), e);
             }
         }
+
+        // 保存场景分组,以最终结果为准
+        Map<String, Caps> stores = values.stream().filter(pair -> pair.getLeft().storing())
+                .collect(Collectors.toMap(pair -> pair.getLeft().scene(), BiPair::getRight));
         try {
             stores = ObjectUtils.ifEmpty(stores, this.store::set, Collections::emptyMap);
             stores.forEach((scene, group) -> GroupContextHolder.setGroup(scene, ObjectUtils.ifNull(group, Caps.NONE)));
         } catch (Exception e) {
-            log.error("Group handle failed", e);
+            log.error("Group storing failed", e);
+            return;
+        }
+
+        // 执行分组完成回调方法
+        for (BiPair<Group, GroupHandler, Caps> pair : values) {
+            Group group = pair.getLeft();
+            Caps value = pair.getRight();
+            GroupHandler handler = pair.getMiddle();
+            if (!group.storing() || value == stores.get(group.scene())) {
+                try {
+                    handler.complete(group.scene(), value, args);
+                } catch (Exception e) {
+                    log.error("Group complete failed: {}", group.scene(), e);
+                }
+            }
         }
     }
 
@@ -170,7 +188,7 @@ public class GroupMethodInterceptor {
                 }
             });
         } catch (Exception e) {
-            log.error("Group handle failed", e);
+            log.error("Group loading failed", e);
             return point.proceed();
         }
 

+ 4 - 7
framework-group/src/main/java/com/chelvc/framework/group/SimpleGroupHandler.java

@@ -1,6 +1,7 @@
 package com.chelvc.framework.group;
 
-import com.chelvc.framework.base.context.SessionContextHolder;
+import java.util.concurrent.ThreadLocalRandom;
+
 import com.chelvc.framework.common.model.Caps;
 
 /**
@@ -12,11 +13,7 @@ import com.chelvc.framework.common.model.Caps;
 public class SimpleGroupHandler implements GroupHandler {
     @Override
     public Caps execute(String scene, Object... args) {
-        Long id = SessionContextHolder.getId();
-        if (id == null) {
-            return null;
-        }
-        int mod = (int) Math.abs(id % 2);
-        return mod == 0 ? Caps.A : Caps.B;
+        int random = ThreadLocalRandom.current().nextInt(2);
+        return random == 0 ? Caps.A : Caps.B;
     }
 }

+ 3 - 3
framework-security/src/main/java/com/chelvc/framework/security/firewall/DefaultFirewallProcessor.java

@@ -80,7 +80,7 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
      */
     protected List<String> getAccessControls(List<Pair<String, Rule>> pairs) {
         List<String> keys = Lists.newArrayListWithCapacity(pairs.size());
-        pairs.forEach(pair -> keys.add("acl:" + pair.getKey() + ":" + pair.getValue().getId()));
+        pairs.forEach(pair -> keys.add(pair.getValue().getAclKey(pair.getKey())));
         RedisTemplate<String, String> template = RedisContextHolder.getDefaultTemplate();
         return Objects.requireNonNull(template.opsForValue().multiGet(keys));
     }
@@ -100,7 +100,7 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
             public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                 pairs.forEach(pair -> {
                     Rule rule = pair.getValue();
-                    K key = (K) ("access:" + pair.getKey() + ":" + rule.getId());
+                    K key = (K) rule.getAccessKey(pair.getKey());
                     long mark = timestamp - rule.getCycle() * 1000;
 
                     // 添加访问记录
@@ -135,7 +135,7 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
                 public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                     matches.forEach(pair -> {
                         Rule rule = pair.getValue();
-                        K key = (K) ("acl:" + pair.getKey() + ":" + rule.getId());
+                        K key = (K) rule.getAclKey(pair.getKey());
                         V value = (V) rule.getAction();
                         operations.opsForValue().setIfAbsent(key, value, rule.getExpiration(), TimeUnit.SECONDS);
                     });

+ 20 - 0
framework-security/src/main/java/com/chelvc/framework/security/firewall/Rule.java

@@ -86,4 +86,24 @@ public class Rule implements Serializable {
         }
         return this.id;
     }
+
+    /**
+     * 获取规则ACL标识
+     *
+     * @param principal 主体标识
+     * @return ACL标识
+     */
+    public String getAclKey(String principal) {
+        return "acl:" + principal + ":" + this.getId();
+    }
+
+    /**
+     * 获取规则访问标识
+     *
+     * @param principal 主体标识
+     * @return 访问标识
+     */
+    public String getAccessKey(String principal) {
+        return "access:" + principal + ":" + this.getId();
+    }
 }

+ 23 - 0
framework-sms/src/main/java/com/chelvc/framework/sms/NormalSmsHandler.java

@@ -1,5 +1,11 @@
 package com.chelvc.framework.sms;
 
+import java.util.Collection;
+import java.util.Map;
+
+import com.chelvc.framework.common.model.BiPair;
+import com.chelvc.framework.common.model.Pair;
+
 /**
  * 普通短信操作接口
  *
@@ -15,4 +21,21 @@ public interface NormalSmsHandler {
      * @return true/false
      */
     boolean send(String content, String... mobiles);
+
+    /**
+     * 发送短信
+     *
+     * @param pairs 手机号/短息内容/附加信息集合
+     * @return 手机号/消息ID映射表
+     */
+    Map<String, Long> send(Collection<BiPair<String, String, String>> pairs);
+
+    /**
+     * 发送短信
+     *
+     * @param content 短信内容
+     * @param pairs   手机号/附加信息集合
+     * @return 手机号/消息ID映射表
+     */
+    Map<String, Long> send(String content, Collection<Pair<String, String>> pairs);
 }

+ 36 - 8
framework-sms/src/main/java/com/chelvc/framework/sms/support/DelegatingNormalSmsHandler.java

@@ -1,9 +1,16 @@
 package com.chelvc.framework.sms.support;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
-import java.util.function.Predicate;
+import java.util.Map;
+import java.util.function.Function;
 
+import com.chelvc.framework.common.model.BiPair;
+import com.chelvc.framework.common.model.Pair;
 import com.chelvc.framework.common.util.AssertUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.sms.NormalSmsHandler;
 import com.google.common.collect.Lists;
 import lombok.NonNull;
@@ -28,15 +35,14 @@ public class DelegatingNormalSmsHandler implements NormalSmsHandler {
      * 执行短信发送处理
      *
      * @param callback 短信处理回调函数
-     * @return true/false
+     * @param <T>      结果类型
+     * @return 发送结果
      */
-    private boolean execute(Predicate<NormalSmsHandler> callback) {
+    private <T> T execute(Function<NormalSmsHandler, T> callback) {
         Exception unsupported = null;
         for (NormalSmsHandler handler : this.handlers) {
             try {
-                if (callback.test(handler)) {
-                    return true;
-                }
+                return callback.apply(handler);
             } catch (Exception e) {
                 if (e instanceof UnsupportedOperationException) {
                     unsupported = e;
@@ -48,11 +54,33 @@ public class DelegatingNormalSmsHandler implements NormalSmsHandler {
         if (unsupported != null) {
             log.warn("Normal SMS send failed: {}", unsupported.getMessage());
         }
-        return false;
+        return null;
     }
 
     @Override
     public boolean send(@NonNull String content, @NonNull String... mobiles) {
-        return mobiles.length > 0 && this.execute(handler -> handler.send(content, mobiles));
+        if (mobiles.length > 0) {
+            Boolean success = this.execute(handler -> handler.send(content, mobiles));
+            return Boolean.TRUE.equals(success);
+        }
+        return false;
+    }
+
+    @Override
+    public Map<String, Long> send(@NonNull Collection<BiPair<String, String, String>> pairs) {
+        if (ObjectUtils.isEmpty(pairs)) {
+            return Collections.emptyMap();
+        }
+        Map<String, Long> result = this.execute(handler -> handler.send(pairs));
+        return ObjectUtils.ifNull(result, Collections::emptyMap);
+    }
+
+    @Override
+    public Map<String, Long> send(@NonNull String content, @NonNull Collection<Pair<String, String>> pairs) {
+        if (StringUtils.isEmpty(content) || ObjectUtils.isEmpty(pairs)) {
+            return Collections.emptyMap();
+        }
+        Map<String, Long> result = this.execute(handler -> handler.send(content, pairs));
+        return ObjectUtils.ifNull(result, Collections::emptyMap);
     }
 }