Browse Source

模型计算管理开发

ZhuDeKang 3 tháng trước cách đây
mục cha
commit
c37cb6862c

+ 4 - 170
ruoyi-admin/src/test/java/com/ruoyi/JasyptTest.java

@@ -3,6 +3,7 @@ package com.ruoyi;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import com.ruoyi.common.utils.sm4.SM4Util;
 import com.ruoyi.model.dpp.dal.dataobject.etl.DppEtlTaskDO;
 import com.ruoyi.model.etl.mapper.DppEtlTaskMapper;
 import com.ruoyi.model.etl.mapper.TestDppEtlTaskMapper;
@@ -20,180 +21,13 @@ import java.util.Map;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 
-@SpringBootTest(classes = RuoYiApplication.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@SpringBootTest(classes = RuoYiApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
 @RunWith(SpringRunner.class)
 public class JasyptTest {
 
 
     @Test
-    public void comprehensiveDiagnosis() {
-        System.out.println("=== 全面诊断 MyBatis-Plus Join 问题 ===");
-
-        // 1. 检查上下文
-        assertNotNull(applicationContext);
-
-        // 2. 检查 Mapper 注入
-        assertNotNull(dppEtlTaskMapper);
-        System.out.println("✅ Mapper 注入成功");
-
-        // 3. 检查 BaseMapperX 继承
-        testBaseMapperXInheritance();
-
-        // 4. 检查 MyBatis-Plus Join 配置
-        checkMyBatisPlusJoinAutoConfiguration();
-
-        // 5. 测试其他 MPJ 方法(如果 selectJoinPage 不行)
-        testOtherMPJMethods();
-    }
-
-    private void testOtherMPJMethods() {
-        System.out.println("=== 测试其他 MyBatis-Plus Join 方法 ===");
-
-        MPJLambdaWrapper<DppEtlTaskDO> wrapper = new MPJLambdaWrapper<>();
-        wrapper.eq(DppEtlTaskDO::getDelFlag, "0");
-
-        // 测试 selectJoinList
-        try {
-            List<DppEtlTaskDO> result = dppEtlTaskMapper.selectJoinList(DppEtlTaskDO.class, wrapper);
-            System.out.println("✅ selectJoinList 工作正常,结果数量: " + result.size());
-        } catch (Exception e) {
-            System.out.println("❌ selectJoinList 也失败: " + e.getMessage());
-        }
-
-        // 测试 selectJoinOne
-        try {
-            DppEtlTaskDO result = dppEtlTaskMapper.selectJoinOne(DppEtlTaskDO.class, wrapper);
-            System.out.println("✅ selectJoinOne 工作正常");
-        } catch (Exception e) {
-            System.out.println("❌ selectJoinOne 失败: " + e.getMessage());
-        }
-    }
-
-
-
-
-    @Test
-    public void checkMyBatisPlusJoinAutoConfiguration() {
-        System.out.println("=== 检查 MyBatis-Plus Join 自动配置 ===");
-
-        try {
-            // 检查 MPJInterceptor 是否存在
-            Object mpjInterceptor = applicationContext.getBean("mpjInterceptor");
-            System.out.println("✅ MPJInterceptor 存在: " + mpjInterceptor.getClass().getName());
-        } catch (Exception e) {
-            System.out.println("❌ MPJInterceptor 不存在: " + e.getMessage());
-        }
-
-        // 检查 MyBatis-Plus Join 相关的 Bean
-        String[] beanNames = applicationContext.getBeanNamesForType(com.github.yulichang.interceptor.MPJInterceptor.class);
-        System.out.println("找到的 MPJInterceptor Beans: " + Arrays.toString(beanNames));
-    }
-
-
-    @Test
-    public void testBaseMapperXInheritance() {
-        System.out.println("=== 验证 BaseMapperX 继承关系 ===");
-
-        // 检查 Mapper 的实际类型
-        Class<?> mapperClass = dppEtlTaskMapper.getClass();
-        System.out.println("Mapper 实际类: " + mapperClass.getName());
-
-        // 检查实现的接口
-        Class<?>[] interfaces = mapperClass.getInterfaces();
-        for (Class<?> iface : interfaces) {
-            System.out.println("实现的接口: " + iface.getName());
-        }
-
-        // 检查是否是 MPJBaseMapper 的实例
-        boolean isMPJBaseMapper = dppEtlTaskMapper instanceof com.github.yulichang.base.MPJBaseMapper;
-        System.out.println("是否是 MPJBaseMapper 实例: " + isMPJBaseMapper);
-
-        // 尝试通过反射查看方法
-        try {
-            java.lang.reflect.Method method = dppEtlTaskMapper.getClass().getMethod("selectJoinPage",
-                    com.baomidou.mybatisplus.core.metadata.IPage.class, Class.class, com.github.yulichang.wrapper.MPJLambdaWrapper.class);
-            System.out.println("找到 selectJoinPage 方法: " + method);
-        } catch (NoSuchMethodException e) {
-            System.out.println("未找到 selectJoinPage 方法");
-        }
-    }
-
-
-
-    @Autowired(required = false)  // 设置为非必需,避免启动失败
-    private TestDppEtlTaskMapper dppEtlTaskMapper;
-
-    @Autowired
-    private ApplicationContext applicationContext;
-
-    @Test
-    public void testMyBatisPlusJoinConfiguration() {
-        System.out.println("=== 开始诊断 Mapper 注入问题 ===");
-
-        // 检查 ApplicationContext 是否正常
-        System.out.println("ApplicationContext: " + (applicationContext != null ? "正常" : "null"));
-
-        // 检查所有 Bean
-        String[] beanNames = applicationContext.getBeanDefinitionNames();
-        boolean foundMapper = false;
-        for (String beanName : beanNames) {
-            if (beanName.toLowerCase().contains("dppetltaskmapper") ||
-                    beanName.contains("DppEtlTaskMapper")) {
-                System.out.println("找到 Mapper Bean: " + beanName);
-                foundMapper = true;
-            }
-        }
-
-        if (!foundMapper) {
-            System.out.println("未找到 DppEtlTaskMapper Bean");
-            // 检查所有 Mapper 类型的 Bean
-            try {
-                Map<String, Object> mappers = applicationContext.getBeansWithAnnotation(org.apache.ibatis.annotations.Mapper.class);
-                System.out.println("找到的 Mapper Beans: " + mappers.keySet());
-            } catch (Exception e) {
-                System.out.println("获取 Mapper Beans 时出错: " + e.getMessage());
-            }
-        }
-
-        // 检查 Mapper 是否注入
-        if (dppEtlTaskMapper == null) {
-            System.out.println("❌ DppEtlTaskMapper 注入失败");
-            // 尝试手动获取
-            try {
-                TestDppEtlTaskMapper mapper = applicationContext.getBean(TestDppEtlTaskMapper.class);
-                System.out.println("✅ 通过 ApplicationContext 获取 Mapper 成功");
-                dppEtlTaskMapper = mapper;
-            } catch (Exception e) {
-                System.out.println("❌ 通过 ApplicationContext 获取 Mapper 也失败: " + e.getMessage());
-                return; // 如果获取不到,直接返回
-            }
-        } else {
-            System.out.println("✅ DppEtlTaskMapper 注入成功");
-        }
-
-        // 如果 Mapper 存在,继续测试
-        if (dppEtlTaskMapper != null) {
-            testSelectJoinPage();
-        }
-    }
-
-    private void testSelectJoinPage() {
-        System.out.println("=== 开始测试 selectJoinPage 方法 ===");
-
-        MPJLambdaWrapper<DppEtlTaskDO> wrapper = new MPJLambdaWrapper<>();
-        wrapper.eq(DppEtlTaskDO::getDelFlag, "0");
-
-        try {
-            IPage<DppEtlTaskDO> result = dppEtlTaskMapper.selectJoinPage(
-                    new Page<>(1, 5),
-                    DppEtlTaskDO.class,
-                    wrapper
-            );
-            System.out.println("✅ MyBatis-Plus Join 工作正常!");
-            System.out.println("查询到 " + result.getRecords().size() + " 条记录");
-        } catch (Exception e) {
-            System.out.println("❌ MyBatis-Plus Join 配置有问题: " + e.getMessage());
-            e.printStackTrace();
-        }
+    public void test() {
+        System.out.println(SM4Util.selfCheck());
     }
 }

+ 16 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/MdFlowDispatchController.java

@@ -6,12 +6,14 @@ import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.ExecutorName;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.interfaces.domain.vo.FlowDispatchBatchVo;
 import com.ruoyi.interfaces.domain.vo.MdFlowDispatchVo;
 import com.ruoyi.interfaces.service.IMdFlowDispatchService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 
@@ -68,5 +70,19 @@ public class MdFlowDispatchController extends BaseController {
         return success(delete);
     }
 
+    /**
+     * 计算任务执行结果
+     * @param par
+     * @return
+     */
+
+    @GetMapping("/getBatchDataTable")
+    public TableDataInfo getBatchDataTable(FlowDispatchBatchVo par) {
+        startPage();
+        List<FlowDispatchBatchVo> data = mdFlowDispatchService.selectFlowDispatchBatch(par);
+        return getDataTable(data);
+    }
+
+
 
 }

+ 0 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/PtServiceController.java

@@ -213,7 +213,6 @@ public class PtServiceController extends BaseController {
             serviceRunLogService.insertPtServiceRunLog(ptServiceLog);
             return new AjaxResult(HttpStatus.HTTP_ERROR, "测试出错,请检查参数与路由");
         }
-
         ptServiceLog.setSenState("1");
         serviceRunLogService.insertPtServiceRunLog(ptServiceLog);
         return success(map.get("result"));

+ 28 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/vo/FlowDispatchBatchVo.java

@@ -0,0 +1,28 @@
+package com.ruoyi.interfaces.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+import java.util.List;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FlowDispatchBatchVo extends MdFlowDispatchVo {
+
+    private Long jobId;
+    private Integer taskBatchStatus;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createDt;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateDt;
+
+    private Long executionAt;
+
+    private Integer operationReason;
+
+
+}

+ 91 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/vo/JobBatchResponseVo.java

@@ -0,0 +1,91 @@
+package com.ruoyi.interfaces.domain.vo;
+
+import com.aizuda.snailjob.model.request.CallbackConfig;
+import com.aizuda.snailjob.model.request.DecisionConfigRequest;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class JobBatchResponseVo {
+
+    private Long id;
+
+    /**
+     * 组名称
+     */
+    private String groupName;
+
+    /**
+     * 名称
+     */
+    private String jobName;
+
+    /**
+     * 任务类型
+     */
+    private String taskType;
+
+
+
+    /**
+     * 任务信息id
+     */
+    private Long jobId;
+
+    /**
+     * 任务状态
+     */
+    private Integer taskBatchStatus;
+
+    /**
+     * 创建时间
+     */
+    private Date createDt;
+
+    /**
+     * 更新时间
+     */
+    private Date updateDt;
+
+    /**
+     * 任务执行时间
+     */
+    private Long executionAt;
+    /**
+     * 操作原因
+     */
+    private Integer operationReason;
+
+    /**
+     * 执行器类型 1、Java
+     */
+    private Integer executorType;
+
+    /**
+     * 执行器名称
+     */
+    private String executorInfo;
+
+    /**
+     * 工作流的回调节点信息
+     */
+    private CallbackConfig callback;
+
+    /**
+     * 工作流的决策节点信息
+     */
+    private DecisionConfigRequest decision;
+
+    /**
+     * 工作流批次id
+     */
+    private Long workflowTaskBatchId;
+
+    /**
+     * 工作流节点id
+     */
+    private Long workflowNodeId;
+
+
+}

+ 37 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/vo/MdFlowDispatchVo.java

@@ -21,6 +21,10 @@ public class MdFlowDispatchVo extends JobRequestVo {
      * 流程id
      */
     private Long appId;
+    /**
+     * 流程图名称
+     */
+    private String appTitle;
 
 
     /**
@@ -59,12 +63,44 @@ public class MdFlowDispatchVo extends JobRequestVo {
 
     private String flowParamString;
 
-
     /**
      * 下次触发时间
      */
     private Long nextTriggerAt;
 
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date beginTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+
+
+
+
+    public Date getBeginTime() {
+        return beginTime;
+    }
+
+    public void setBeginTime(Date beginTime) {
+        this.beginTime = beginTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getAppTitle() {
+        return appTitle;
+    }
+
+    public void setAppTitle(String appTitle) {
+        this.appTitle = appTitle;
+    }
+
     public Long getNextTriggerAt() {
         return nextTriggerAt;
     }

+ 3 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/mapper/MdFlowDispatchMapper.java

@@ -2,6 +2,7 @@ package com.ruoyi.interfaces.mapper;
 
 import com.ruoyi.common.annotation.DataSource;
 import com.ruoyi.common.enums.DataSourceType;
+import com.ruoyi.interfaces.domain.vo.FlowDispatchBatchVo;
 import com.ruoyi.interfaces.domain.vo.MdFlowDispatchVo;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -19,4 +20,6 @@ public interface MdFlowDispatchMapper {
     Long updateMdFlowJob(MdFlowDispatchVo flowDispatchVo);
 
     Long deleteMdFlowJobById(String id);
+
+    List<FlowDispatchBatchVo> selectFlowDispatchBatch(FlowDispatchBatchVo par);
 }

+ 3 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/IMdFlowDispatchService.java

@@ -1,5 +1,6 @@
 package com.ruoyi.interfaces.service;
 
+import com.ruoyi.interfaces.domain.vo.FlowDispatchBatchVo;
 import com.ruoyi.interfaces.domain.vo.MdFlowDispatchVo;
 
 import java.util.List;
@@ -14,4 +15,6 @@ public interface IMdFlowDispatchService {
     Long update(MdFlowDispatchVo flowDispatchVo);
 
     Long delete(String id);
+
+    List<FlowDispatchBatchVo> selectFlowDispatchBatch(FlowDispatchBatchVo par);
 }

+ 8 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/MdFlowDispatchServiceImpl.java

@@ -3,6 +3,7 @@ package com.ruoyi.interfaces.service.impl;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.uuid.IdUtils;
+import com.ruoyi.interfaces.domain.vo.FlowDispatchBatchVo;
 import com.ruoyi.interfaces.domain.vo.MdFlowDispatchVo;
 import com.ruoyi.interfaces.mapper.MdFlowDispatchMapper;
 import com.ruoyi.interfaces.service.IMdAppFlowService;
@@ -11,6 +12,7 @@ import com.ruoyi.interfaces.service.SnailJobService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 
@@ -112,4 +114,10 @@ public class MdFlowDispatchServiceImpl implements IMdFlowDispatchService {
 
         return flowDispatchMapper.deleteMdFlowJobById(id);
     }
+
+    @Override
+    public List<FlowDispatchBatchVo> selectFlowDispatchBatch(FlowDispatchBatchVo par) {
+
+        return flowDispatchMapper.selectFlowDispatchBatch(par);
+    }
 }

+ 2 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/snailJob/ServiceLogStatisJob.java

@@ -53,7 +53,9 @@ public class ServiceLogStatisJob {
                 jobScheduleMapper.insertScheduleByProCode(PRO_CODE, schedule);
             }
         } else {
+            if (StringUtils.isNotNull(hashMap.get("beginTime")))
             schedule = DateUtils.dateTime(DateUtils.YYYY_MM_DD, hashMap.get("beginTime").toString());
+            if (StringUtils.isNotNull(hashMap.get("endTime")))
             endTime = DateUtils.dateTime(DateUtils.YYYY_MM_DD, hashMap.get("endTime").toString());
         }
 

+ 73 - 0
ruoyi-api-patform/src/main/resources/mapper/interfaces/MdFlowDispatchMapper.xml

@@ -142,4 +142,77 @@
             fj.FLOW_JOB_ID = #{flowJobId}
         </where>
     </select>
+    <resultMap id="FlowDispatchBatchMap" type="com.ruoyi.interfaces.domain.vo.FlowDispatchBatchVo">
+        <result property="flowJobId" column="FLOW_JOB_ID"/>
+        <result property="flowJobName" column="FLOW_JOB_NAME"/>
+        <result property="chargeBy" column="CHARGE_BY"/>
+        <result property="chargePhone" column="CHARGE_PHONE"/>
+        <result property="appId" column="APP_ID"/>
+        <result property="appTitle" column="APP_TITLE"/>
+        <result property="id" column="Id"/>
+        <result property="groupName" column="GROUP_NAME"/>
+        <result property="jobName" column="JOB_NAME"/>
+        <result property="taskType" column="TASK_TYPE"/>
+        <result property="jobId" column="JOB_ID"/>
+        <result property="taskBatchStatus" column="TASK_BATCH_STATUS"/>
+        <result property="createDt" column="CREATE_DT"  />
+        <result property="updateDt" column="UPDATE_DT"  />
+        <result property="executionAt" column="EXECUTION_AT"  />
+        <result property="operationReason" column="OPERATION_REASON"/>
+        <result property="executorType" column="EXECUTOR_TYPE"/>
+        <result property="executorInfo" column="EXECUTOR_INFO"/>
+
+    </resultMap>
+    <select id="selectFlowDispatchBatch" resultMap="FlowDispatchBatchMap" >
+        SELECT
+        f.FLOW_JOB_ID,
+        f.FLOW_JOB_NAME ,
+        f.CHARGE_BY ,
+        f.CHARGE_PHONE ,
+        f.app_id,
+        app.APP_TITLE,
+        job.Id,
+        job.GROUP_NAME,
+        job.JOB_NAME,
+        job.TASK_TYPE,
+        job.JOB_ID,
+        job.TASK_BATCH_STATUS,
+        to_char(job.CREATE_DT,'yyyy-mm-dd hh24:MI:ss') AS CREATE_DT,
+        to_char(job.UPDATE_DT,'yyyy-mm-dd hh24:MI:ss') AS UPDATE_DT,
+        job.EXECUTION_AT,
+        job.OPERATION_REASON,
+        job.EXECUTOR_TYPE,
+        job.EXECUTOR_INFO
+        FROM MD_FLOW_JOB f
+        left join MD_APP app on f.app_id = app.app_id
+        LEFT JOIN (
+        SELECT batch.*,
+        job.job_name,
+        job.task_type,
+        job.block_strategy,
+        job.trigger_type,
+        job.executor_type,
+        job.executor_info
+        FROM snail_job.sj_job_task_batch batch
+        JOIN snail_job.sj_job job ON batch.job_id = job.id
+        ) job ON f.ID = job.job_id
+        <where>
+            <if test="appId != null">
+                and f.app_id = #{appId}
+            </if>
+            <if test="flowJobName != null and flowJobName != ''">
+                and f.FLOW_JOB_NAME  like CONCAT('%', #{flowJobName}, '%')
+            </if>
+            <if test="taskBatchStatus != null">
+                and job.TASK_BATCH_STATUS = #{taskBatchStatus}
+            </if>
+            <if test="beginTime != null">
+                and job.CREATE_DT &gt;= #{beginTime}
+            </if>
+            <if test="endTime != null">
+                and job.CREATE_DT &lt;= #{endTime}
+            </if>
+        </where>
+        ORDER BY f.CREATE_TIME DESC
+    </select>
 </mapper>

+ 8 - 0
ruoyi-common/pom.xml

@@ -247,6 +247,14 @@
             <artifactId>guava</artifactId>
             <version>20.0</version>
         </dependency>
+
+        <!-- Bouncy Castle 提供SM4算法支持 -->
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.70</version>
+        </dependency>
+
         <dependency>
             <groupId>com.koal</groupId>
             <artifactId>kms-sdk</artifactId>

+ 126 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/sm4/SM4Util.java

@@ -0,0 +1,126 @@
+package com.ruoyi.common.utils.sm4;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.Security;
+import java.util.Base64;
+
+/**
+ * SM4加密算法静态工具类
+ * 使用硬编码固定密钥,通过静态方法直接调用
+ */
+public class SM4Util {
+
+    static {
+        // 添加BouncyCastleProvider
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    // 算法名称
+    private static final String ALGORITHM_NAME = "SM4";
+    private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
+
+    // 硬编码的固定密钥(16字节)
+    // 在实际项目中,请替换为您自己的密钥
+    private static final byte[] SECRET_KEY = {
+        (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67,
+        (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF,
+        (byte) 0xFE, (byte) 0xDC, (byte) 0xBA, (byte) 0x98,
+        (byte) 0x76, (byte) 0x54, (byte) 0x32, (byte) 0x10
+    };
+
+    /**
+     * 加密字符串
+     *
+     * @param data 待加密的字符串
+     * @return Base64编码的加密结果
+     */
+    public static String encrypt(String data) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
+            SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY, ALGORITHM_NAME);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
+            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
+            return Base64.getEncoder().encodeToString(encrypted);
+        } catch (Exception e) {
+            throw new RuntimeException("SM4加密失败", e);
+        }
+    }
+
+    /**
+     * 解密字符串
+     *
+     * @param encryptedData Base64编码的加密数据
+     * @return 解密后的原始字符串
+     */
+    public static String decrypt(String encryptedData) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
+            SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY, ALGORITHM_NAME);
+            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
+            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
+            byte[] decrypted = cipher.doFinal(encryptedBytes);
+            return new String(decrypted, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            throw new RuntimeException("SM4解密失败", e);
+        }
+    }
+
+    /**
+     * 加密字节数组
+     *
+     * @param data 待加密的字节数组
+     * @return 加密后的字节数组
+     */
+    public static byte[] encrypt(byte[] data) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
+            SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY, ALGORITHM_NAME);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
+            return cipher.doFinal(data);
+        } catch (Exception e) {
+            throw new RuntimeException("SM4加密失败", e);
+        }
+    }
+
+    /**
+     * 解密字节数组
+     *
+     * @param encryptedData 加密的字节数组
+     * @return 解密后的原始字节数组
+     */
+    public static byte[] decrypt(byte[] encryptedData) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
+            SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY, ALGORITHM_NAME);
+            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
+            return cipher.doFinal(encryptedData);
+        } catch (Exception e) {
+            throw new RuntimeException("SM4解密失败", e);
+        }
+    }
+
+    /**
+     * 验证加密解密功能是否正常
+     */
+    public static boolean selfCheck() {
+        try {
+            String testData = "SM4测试数据123";
+            String encrypted = encrypt(testData);
+            String decrypted = decrypt(encrypted);
+            return testData.equals(decrypted);
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * 获取密钥信息(用于调试)
+     */
+    public static String getKeyInfo() {
+        return "SM4密钥已硬编码在工具类中,长度: " + SECRET_KEY.length + " 字节";
+    }
+}