init commit

This commit is contained in:
iqudoo
2026-06-05 17:18:53 +08:00
commit 7c8c166637
311 changed files with 43782 additions and 0 deletions

0
.eslintignore Normal file
View File

37
.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
#### Release And Logs ###
target/
logs/
opt/
runtime/
#### Maven ###
.mvn/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
### PID log
hs_err_pid*
replay_pid*
### MAC OS
.DS_Store

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# tape-springboot-framework
> 后端服务

151
ddl.sql Normal file
View File

@@ -0,0 +1,151 @@
-- ----------------------------
-- Table structure for t_system_admin_info
-- ----------------------------
DROP TABLE IF EXISTS `t_system_admin_info`;
CREATE TABLE `t_system_admin_info` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`username` varchar(32) NOT NULL DEFAULT '' COMMENT '登录账号',
`nickname` varchar(128) NOT NULL DEFAULT '' COMMENT '账号昵称',
`pwd_md5` varchar(32) NOT NULL DEFAULT '' COMMENT '密码密文',
`pwd_slot` varchar(32) NOT NULL DEFAULT '' COMMENT '加密盐数',
`opt_remark` varchar(256) NOT NULL DEFAULT '' COMMENT '备注信息',
`opt_status` int NOT NULL DEFAULT '0' COMMENT '状态标志',
`opt_super` int NOT NULL DEFAULT '0' COMMENT '超管标志',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) NOT NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE,
UNIQUE KEY `idx_unique_username` (`username`,`delete_token`) USING BTREE,
KEY `idx_create_time` (`create_time`) USING BTREE,
KEY `idx_update_time` (`update_time`) USING BTREE,
KEY `idx_hidden_delete` (`is_hidden`,`is_delete`) USING BTREE
) ENGINE=InnoDB COMMENT='系统管理员信息表';
-- ----------------------------
-- Table structure for t_system_async_task_info
-- ----------------------------
DROP TABLE IF EXISTS `t_system_async_task_info`;
CREATE TABLE `t_system_async_task_info` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`biz_type` varchar(128) NOT NULL DEFAULT '' COMMENT '业务类型',
`biz_id` varchar(128) NOT NULL DEFAULT '' COMMENT '业务编号',
`trans_id` varchar(128) NOT NULL DEFAULT '' COMMENT '任务编号',
`task_type` varchar(64) NOT NULL DEFAULT '' COMMENT '任务类型',
`task_params` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务参数',
`call_timing` bigint NOT NULL DEFAULT '0' COMMENT '定时执行',
`start_time` bigint NOT NULL DEFAULT '0' COMMENT '开始时间',
`end_time` bigint NOT NULL DEFAULT '0' COMMENT '结束时间',
`use_time` bigint NOT NULL DEFAULT '0' COMMENT '总共耗时',
`total_count` int NOT NULL DEFAULT '0' COMMENT '总共数量',
`success_count` int NOT NULL DEFAULT '0' COMMENT '成功数量',
`failure_count` int NOT NULL DEFAULT '0' COMMENT '失败数量',
`ignore_count` int NOT NULL DEFAULT '0' COMMENT '忽略数量',
`retry_count` int NOT NULL DEFAULT '0' COMMENT '重试次数',
`opt_status` int NOT NULL DEFAULT '0' COMMENT '状态标志',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) NOT NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE
) ENGINE=InnoDB COMMENT='系统异步任务信息表';
-- ----------------------------
-- Table structure for t_system_crawler_cache_data
-- ----------------------------
DROP TABLE IF EXISTS `t_system_crawler_cache_data`;
CREATE TABLE `t_system_crawler_cache_data` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`crawler_time` bigint unsigned NOT NULL DEFAULT '0' COMMENT '抓取时间',
`crawler_type` varchar(128) NOT NULL DEFAULT '' COMMENT '抓取类型',
`crawler_keyword` varchar(128) NOT NULL DEFAULT '' COMMENT '抓取关键词',
`cache_data` mediumtext NOT NULL COMMENT '缓存内容',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE
) ENGINE=InnoDB COMMENT='系统爬虫数据缓存信息';
-- ----------------------------
-- Table structure for t_system_global_cache
-- ----------------------------
DROP TABLE IF EXISTS `t_system_global_cache`;
CREATE TABLE `t_system_global_cache` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`cache_time` bigint unsigned NOT NULL DEFAULT '0' COMMENT '缓存时间',
`expire_time` bigint unsigned NOT NULL DEFAULT '0' COMMENT '过期时间',
`cache_key` varchar(128) NOT NULL DEFAULT '' COMMENT '缓存主键',
`cache_data` mediumtext NOT NULL COMMENT '缓存内容',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) NOT NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE
) ENGINE=InnoDB COMMENT='系统通用缓存信息表';
-- ----------------------------
-- Table structure for t_system_global_file_transfer
-- ----------------------------
DROP TABLE IF EXISTS `t_system_global_file_transfer`;
CREATE TABLE `t_system_global_file_transfer` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`original_url` varchar(512) NOT NULL DEFAULT '' COMMENT '原来地址',
`saved_url` varchar(512) NOT NULL DEFAULT '' COMMENT '转存地址',
`file_size` bigint unsigned NOT NULL DEFAULT '0' COMMENT '文件大小',
`file_hash` varchar(32) NOT NULL DEFAULT '' COMMENT '文件哈希',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) NOT NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE
) ENGINE=InnoDB COMMENT='系统文件转存关系表';
-- ----------------------------
-- Table structure for t_system_global_session
-- ----------------------------
DROP TABLE IF EXISTS `t_system_global_session`;
CREATE TABLE `t_system_global_session` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`user_guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT '账号GUID',
`user_group` varchar(32) NOT NULL DEFAULT '' COMMENT '账号分组',
`session_key` varchar(128) NOT NULL DEFAULT '' COMMENT '会话KEY',
`origin_platform` varchar(32) NOT NULL DEFAULT '' COMMENT '来源平台',
`origin_domain` varchar(128) NOT NULL DEFAULT '' COMMENT '来源域名',
`origin_ip` varchar(128) NOT NULL DEFAULT '' COMMENT '来源IP地址',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) NOT NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE
) ENGINE=InnoDB COMMENT='系统全局会话信息表';
-- ----------------------------
-- Table structure for t_system_global_setting
-- ----------------------------
DROP TABLE IF EXISTS `t_system_global_setting`;
CREATE TABLE `t_system_global_setting` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'GUID',
`setting_key` varchar(128) NOT NULL DEFAULT '' COMMENT '配置KEY',
`setting_value` mediumtext NOT NULL COMMENT '配置内容',
`opt_remark` varchar(256) NOT NULL DEFAULT '' COMMENT '备注信息',
`is_hidden` int NOT NULL DEFAULT '0' COMMENT '隐藏标志',
`is_delete` int NOT NULL DEFAULT '0' COMMENT '删除标志',
`delete_token` varchar(32) NOT NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int NOT NULL DEFAULT '0' COMMENT '数据版本',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE
) ENGINE=InnoDB COMMENT='系统全局配置信息表';

388
pom.xml Normal file
View File

@@ -0,0 +1,388 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress VulnerableLibrariesLocal -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.iqudoo.platform</groupId>
<artifactId>tape-springboot-framework</artifactId>
<version>1.0-SNAPSHOT</version>
<description>Tape SpringBoot Backend Framework</description>
<packaging>war</packaging>
<properties>
<jwtauth.version>3.4.0</jwtauth.version>
<fastjson.version>1.2.73</fastjson.version>
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>
<antlr4.version>4.9.2</antlr4.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<server-port>9081</server-port>
<spring-environment>local</spring-environment>
<app-time-zone>Asia/Shanghai</app-time-zone>
<tape-req-limit>200</tape-req-limit>
</properties>
</profile>
<profile>
<id>qa</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<server-port>9080</server-port>
<spring-environment>qa</spring-environment>
<app-time-zone>Asia/Shanghai</app-time-zone>
<tape-req-limit>200</tape-req-limit>
</properties>
</profile>
<profile>
<id>pre</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<server-port>9080</server-port>
<spring-environment>pre</spring-environment>
<app-time-zone>Asia/Shanghai</app-time-zone>
<tape-req-limit>200</tape-req-limit>
</properties>
</profile>
<profile>
<id>prod</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<server-port>9080</server-port>
<spring-environment>prod</spring-environment>
<app-time-zone>Asia/Shanghai</app-time-zone>
<tape-req-limit>200</tape-req-limit>
</properties>
</profile>
</profiles>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>${antlr4.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring-retry 重试框架 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>${antlr4.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwtauth.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- SSH tunnel (SOCKS5) support for HttpUtil -->
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.84</version>
</dependency>
<!-- Apache Commons Lang -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- 性能监控 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.4.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.13.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.13.0</version>
</dependency>
<!-- 阿里云OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.11.1</version>
</dependency>
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>application</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>default-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/classes</outputDirectory>
<useDefaultDelimiters>false</useDefaultDelimiters>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<includes>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources/</directory>
<excludes>
<exclude>**/*.yml</exclude>
<exclude>**/*.xml</exclude>
</excludes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<packagingExcludes>
WEB-INF/classes/mybatis.properties,
WEB-INF/classes/mybatis.generator.xml
</packagingExcludes>
<webResources>
<resource>
<directory>src/lib</directory>
<targetPath>WEB-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.18</version>
<configuration>
<outputDirectory>${outputDirectory}</outputDirectory>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!-- mybatis -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<configurationFile>src/main/resources/mybatis.generator.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<phase>deploy</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.iqudoo.framework</groupId>
<artifactId>tape-mybaits-generator-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<systemPath>${project.basedir}/src/lib/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar
</systemPath>
<scope>system</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

Binary file not shown.

View File

@@ -0,0 +1,172 @@
package com.iqudoo.framework.tape;
import com.iqudoo.framework.tape.base.action.bean.ActionState;
import com.iqudoo.framework.tape.spring.RunnerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.io.*;
import java.net.NetworkInterface;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Enumeration;
import java.util.UUID;
public class Tape {
public static <T> T getService(Class<T> clazz) throws Throwable {
return getService(clazz, null);
}
public static <T> T getService(Class<T> clazz, String name) throws Throwable {
T service = null;
try {
if (name != null && !name.isEmpty()) {
service = RunnerContext.getBean(name, clazz);
} else {
service = RunnerContext.getBean(clazz);
}
} catch (Throwable ignored) {
}
if (service == null) {
throw new ActionState(clazz.getName() + " not found");
}
return service;
}
public static Logger getLogger(Class<?> clazz) {
return LoggerFactory.getLogger(clazz);
}
public static String getNodeId() {
return PersistentNodeIdGenerator.getPersistentNodeId();
}
private static class PersistentNodeIdGenerator {
// 适配系统目录分隔符Windows下自动转为\Linux下为/
private static final String NODE_ID_DIR = "." + File.separator + "runtime" + File.separator + "opt";
private static final String NODE_ID_FILE = NODE_ID_DIR + File.separator + "node-id.dat";
private static final String HASH_ALGORITHM = "SHA-256";
private static final int NODE_ID_LENGTH = 16;
/**
* 获取持久化的节点ID核心方法首次生成永久不变
*
* @return 唯一且不变的节点ID
*/
public static String getPersistentNodeId() {
// 1. 先读取持久化文件中的ID优先
String nodeId = readNodeIdFromFile();
if (StringUtils.hasText(nodeId)) {
return nodeId;
}
// 2. 无文件则生成唯一ID
nodeId = generateUniqueId();
// 3. 写入文件持久化(自动创建目录)
writeNodeIdToFile(nodeId);
return nodeId;
}
/**
* 从持久化文件读取节点ID
*/
private static String readNodeIdFromFile() {
File file = new File(NODE_ID_FILE);
if (!file.exists()) {
return null;
}
try (BufferedReader reader = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) {
String id = reader.readLine();
// 校验ID格式避免文件被篡改
if (id != null && id.trim().length() == NODE_ID_LENGTH) {
return id.trim();
}
return null;
} catch (IOException e) {
System.err.println("读取节点ID文件失败" + e.getMessage());
return null;
}
}
/**
* 将节点ID写入持久化文件核心自动创建父目录
*/
private static void writeNodeIdToFile(String nodeId) {
// 1. 先创建父目录(递归创建,确保多级目录都能生成)
File dir = new File(NODE_ID_DIR);
if (!dir.exists()) {
boolean dirCreated = dir.mkdirs();
if (!dirCreated) {
throw new RuntimeException("自动创建节点ID目录失败" + NODE_ID_DIR + ",请检查目录权限");
}
}
// 2. 写入文件
File file = new File(NODE_ID_FILE);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, StandardCharsets.UTF_8))) {
writer.write(nodeId);
} catch (IOException e) {
throw new RuntimeException("写入节点ID文件失败" + e.getMessage() + ",请检查目录读写权限");
}
}
/**
* 生成唯一ID首次生成时使用
*/
private static String generateUniqueId() {
try {
// 拼接硬件特征MAC+系统信息,提升唯一性)
String hardwareInfo = getSystemIdentifier();
MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] hashBytes = digest.digest(hardwareInfo.getBytes(StandardCharsets.UTF_8));
// 转为16进制并截取固定长度
StringBuilder hexStr = new StringBuilder();
for (byte b : hashBytes) {
hexStr.append(String.format("%02x", b));
}
return hexStr.substring(0, NODE_ID_LENGTH);
} catch (Exception e) {
// 极端兜底直接用UUID前16位
return UUID.randomUUID().toString().replace("-", "").substring(0, NODE_ID_LENGTH);
}
}
/**
* 获取稳定的系统标识优先环境变量其次MAC
*/
private static String getSystemIdentifier() {
// 优先读取环境变量(容器化部署时手动指定,最稳定)
String envId = System.getenv("NODE_ID");
if (StringUtils.hasText(envId)) {
return envId;
}
// 兜底读取MAC地址
return getMacAddress();
}
private static String getMacAddress() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (!ni.isLoopback() && ni.isUp() && ni.getHardwareAddress() != null) {
byte[] mac = ni.getHardwareAddress();
StringBuilder macStr = new StringBuilder();
for (byte b : mac) {
macStr.append(String.format("%02x", b));
}
return macStr.toString();
}
}
} catch (Exception e) {
// 忽略异常
}
return "";
}
}
}

View File

@@ -0,0 +1,33 @@
package com.iqudoo.framework.tape.base.action.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionConfig {
/* 标签 */
String[] tag() default "";
/* 场景 */
String[] scene() default {};
/* 用户组 */
String[] group() default {};
/* 原始数据 */
boolean isRaw() default false;
/* 模拟模式 */
boolean isMock() default false;
/* 忽略请求限流 */
boolean ignoreLimit() default false;
/* 可选认证状态 */
boolean ignoreAuth() default false;
}

View File

@@ -0,0 +1,24 @@
package com.iqudoo.framework.tape.base.action.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ActionMapping {
/* 方法名 */
String[] action();
/* 支持环境 */
String[] actives() default {};
/* 排序 */
int sort() default 0;
}

View File

@@ -0,0 +1,21 @@
package com.iqudoo.framework.tape.base.action.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ActionRetry {
/* 失败重试次数 */
int failRetry() default 0;
/* 重试失败错误消息 */
String failRetryErrorMsg() default "";
}

View File

@@ -0,0 +1,80 @@
package com.iqudoo.framework.tape.base.action.bean;
import com.iqudoo.framework.tape.Tape;
import com.iqudoo.framework.tape.base.action.interfaces.IActionToken;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class ActionAuthInfo {
private Long userGuid;
private String userGroup;
private String originPlatform;
private String sessionKey;
private Map<String, String> attribute;
public Long getUserGuid() {
return userGuid;
}
public void setUserGuid(Long userGuid) {
this.userGuid = userGuid;
}
public String getUserGroup() {
return userGroup;
}
public void setUserGroup(String group) {
this.userGroup = group;
}
public String getOriginPlatform() {
return originPlatform;
}
public void setOriginPlatform(String platform) {
this.originPlatform = platform;
}
public String getSessionKey() {
return sessionKey;
}
public void setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
}
public void setAttribute(String name, String value) {
if (value == null) {
return;
}
if (attribute == null) {
attribute = new HashMap<>();
}
attribute.put(name, value);
}
public String getAttribute(String name, String defValue) {
if (attribute == null) {
return defValue;
}
String value = attribute.get(name);
if (value == null) {
return defValue;
}
try {
return value;
} catch (Throwable ignored) {
}
return defValue;
}
public ActionTokenInfo createToken() throws Throwable {
return Tape.getService(IActionToken.class)
.createToken(this);
}
}

View File

@@ -0,0 +1,43 @@
package com.iqudoo.framework.tape.base.action.bean;
public class ActionDocInfo {
private String apiDocId = "";
private String[] apiDocActions = new String[0];
private Class<?> apiDocResponseClass = Void.class;
private Class<?>[] apiDocResponseParametricTypes = new Class[]{};
public String getApiDocId() {
return apiDocId;
}
public void setApiDocId(String apiDocId) {
this.apiDocId = apiDocId;
}
public String[] getApiDocActions() {
return apiDocActions;
}
public void setApiDocActions(String[] apiDocActions) {
this.apiDocActions = apiDocActions;
}
public Class<?> getApiDocResponseClass() {
return apiDocResponseClass;
}
public void setApiDocResponseClass(Class<?> apiDocResponseClass) {
this.apiDocResponseClass = apiDocResponseClass;
}
public Class<?>[] getApiDocResponseParametricTypes() {
return apiDocResponseParametricTypes;
}
public void setApiDocResponseParametricTypes(Class<?>[] apiDocResponseParametricTypes) {
this.apiDocResponseParametricTypes = apiDocResponseParametricTypes;
}
}

View File

@@ -0,0 +1,164 @@
package com.iqudoo.framework.tape.base.action.bean;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqudoo.framework.tape.Tape;
import com.iqudoo.framework.tape.base.action.handler.ActionHandler;
import org.slf4j.Logger;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@SuppressWarnings({"LombokSetterMayBeUsed", "LombokGetterMayBeUsed", "unused", "unchecked", "MissingSerialAnnotation"})
public class ActionParam implements Serializable {
private static final long serialVersionUID = 1L;
@JsonIgnore
private Logger logger = null;
@JsonIgnore
private ActionRequest actionRequest;
@JsonIgnore
private ActionHandler<?, ?> actionHandler;
@JsonIgnore
private ActionAuthInfo actionAuthInfo;
@JsonIgnore
private final Map<String, Object> attribute = new ConcurrentHashMap<>();
// 是否抛出异常
private boolean throwThrowable = false;
/**
* 设置属性值
*
* @param name 属性名
* @param value 属性值
*/
public void setAttribute(String name, Object value) {
if (value == null) {
return;
}
attribute.put(name, value);
}
/**
* 获取属性值
*
* @param name 属性名
* @param def 默认值
* @param <T> 属性值类型
* @return 属性值
*/
public <T> T getAttribute(String name, T def) {
Object value = attribute.get(name);
if (value == null) {
return def;
}
try {
return (T) value;
} catch (Throwable ignored) {
}
return def;
}
public ActionRequest getActionRequest() {
return actionRequest;
}
public void setActionRequest(ActionRequest actionRequest) {
this.actionRequest = actionRequest;
}
public ActionHandler<?, ?> getActionHandler() {
return actionHandler;
}
public void setActionHandler(ActionHandler<?, ?> actionHandler) {
this.actionHandler = actionHandler;
}
public void setThrowThrowable(boolean throwThrowable) {
this.throwThrowable = throwThrowable;
}
public boolean isThrowThrowable() {
return throwThrowable;
}
public ActionAuthInfo getActionAuthInfo() {
if (actionAuthInfo == null) {
actionAuthInfo = new ActionAuthInfo();
actionAuthInfo.setUserGuid(0L);
actionAuthInfo.setUserGroup("unknown");
actionAuthInfo.setOriginPlatform("unknown");
actionAuthInfo.setSessionKey("unknown");
return actionAuthInfo;
}
return actionAuthInfo;
}
public void setActionAuthInfo(ActionAuthInfo actionAuthInfo) {
this.actionAuthInfo = actionAuthInfo;
}
public Logger getLogger() {
if (logger == null) {
logger = Tape.getLogger(getClass());
}
return logger;
}
public Long getCurrentUserGuid() {
try {
if (actionAuthInfo == null) {
return 0L;
}
return actionAuthInfo.getUserGuid();
} catch (Throwable ignored) {
}
return 0L;
}
public String getCurrentUserGroup() {
try {
if (actionAuthInfo == null) {
return "unknown";
}
return actionAuthInfo.getUserGroup();
} catch (Throwable ignored) {
}
return "unknown";
}
public String getClientIpAddress() {
String ip = "unknown";
if (actionRequest != null) {
ip = actionRequest.getReqIp();
}
return ip;
}
public String getClientDomain() {
String originDomain = "unknown";
if (actionRequest != null) {
originDomain = actionRequest.getReqDomain();
}
return originDomain;
}
public String getRequestPlatform() {
String platform = "unknown";
if (actionRequest != null) {
platform = actionRequest.getReqPlatform();
}
return platform;
}
public String getSessionPlatform() {
String platform = "unknown";
if (actionAuthInfo != null) {
platform = actionAuthInfo.getOriginPlatform();
}
return platform;
}
}

View File

@@ -0,0 +1,56 @@
package com.iqudoo.framework.tape.base.action.bean;
import com.alibaba.fastjson.JSONObject;
public class ActionParameter {
private String transId;
private String token;
private String action;
private String platform;
private JSONObject params;
public String getTransId() {
return transId;
}
public void setTransId(String transId) {
this.transId = transId;
}
public String getToken() {
return token;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getPlatform() {
if (platform == null) {
return "unknown";
}
return platform;
}
public void setToken(String token) {
this.token = token;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public JSONObject getParams() {
return params;
}
public void setParams(JSONObject params) {
this.params = params;
}
}

View File

@@ -0,0 +1,101 @@
package com.iqudoo.framework.tape.base.action.bean;
import com.alibaba.fastjson.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class ActionRequest extends _Attribute {
private int retryIndex = 0;
private ActionServletRequest actionServletRequest;
public ActionRequest(ActionParameter actionParameter, ActionServletRequest servletRequest) {
if (servletRequest != null) {
setAttribute("request-headers", servletRequest.getHeaders());
setAttribute("request-params", actionParameter.getParams());
setAttribute("request-platform", actionParameter.getPlatform());
setAttribute("request-domain", servletRequest.getOriginDomain());
setAttribute("request-ip", servletRequest.getIpAddress());
this.actionServletRequest = servletRequest;
}
setAttribute("request-parameter", actionParameter);
}
public ActionServletRequest getActionServletRequest() {
return actionServletRequest;
}
public Long getTimestamp() {
return getAttribute("request-timestamp", 0L);
}
public void setTimestamp(Long timestamp) {
setAttribute("request-timestamp", timestamp);
}
public ActionParameter getActionParameter() {
return getAttribute("request-parameter", null);
}
public ActionAuthInfo getAuthInfo() {
return getAttribute("request-auth-info", null);
}
public void setAuthInfo(ActionAuthInfo authInfo) {
setAttribute("request-auth-info", authInfo);
}
public Throwable getAuthError() {
return getAttribute("request-auth-error", null);
}
public void setAuthError(Throwable throwable) {
setAttribute("request-auth-error", throwable);
}
public String getReqIp() {
return getAttribute("request-ip", "");
}
public String getReqDomain() {
return getAttribute("request-domain", "");
}
public String getReqPlatform() {
return getAttribute("request-platform", "");
}
public JSONObject getReqParams() {
return getAttribute("request-params", new JSONObject());
}
public Map<String, String> getReqHeaders() {
return getAttribute("request-headers", new HashMap<>());
}
public String getReqHeader(String name) {
try {
return getReqHeaders().get(name);
} catch (Throwable ignored) {
}
return null;
}
public String getReqParam(String name) {
try {
return getReqParams().getString(name);
} catch (Throwable ignored) {
}
return null;
}
public int getRetryIndex() {
return retryIndex;
}
public void setRetryIndex(int retryIndex) {
this.retryIndex = retryIndex;
}
}

View File

@@ -0,0 +1,90 @@
package com.iqudoo.framework.tape.base.action.bean;
import java.io.Serializable;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class ActionResponse<DATA> implements Serializable {
private int code;
private String msg;
private String action;
private String transId;
private String nodeId;
private Integer retryCount;
private Long timestamp;
private Long useTime;
private DATA data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getTransId() {
return transId;
}
public void setTransId(String transId) {
this.transId = transId;
}
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public Integer getRetryCount() {
return retryCount;
}
public void setRetryCount(Integer retryCount) {
this.retryCount = retryCount;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public Long getUseTime() {
return useTime;
}
public void setUseTime(Long useTime) {
this.useTime = useTime;
}
public DATA getData() {
return data;
}
public void setData(DATA data) {
this.data = data;
}
}

View File

@@ -0,0 +1,87 @@
package com.iqudoo.framework.tape.base.action.bean;
import com.iqudoo.framework.tape.modules.utils.ServletUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class ActionServletRequest {
private String ipAddress;
private String originDomain;
private Map<String, String> queryParams;
private Map<String, String> headers;
private final String rawBody;
private final HttpServletRequest servletRequest;
public ActionServletRequest(HttpServletRequest servletRequest, String rawBody) {
this.servletRequest = servletRequest;
this.rawBody = rawBody;
// queryParams
Map<String, String> qParams = new HashMap<>();
Map<String, String[]> requestParams = servletRequest.getParameterMap();
if (requestParams != null && !requestParams.isEmpty()) {
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
qParams.put(entry.getKey(), entry.getValue()[0]);
}
}
setQueryParams(qParams);
// headerMap
Map<String, String> headerMap = new HashMap<>();
Enumeration<String> headerNames = servletRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = servletRequest.getHeader(headerName);
headerMap.put(headerName, headerValue);
}
setHeaders(headerMap);
// ipAddress
setIpAddress(ServletUtil.getIpAddress(servletRequest));
// originDomain
setOriginDomain(ServletUtil.getDomain(servletRequest));
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getOriginDomain() {
return originDomain;
}
public void setOriginDomain(String originDomain) {
this.originDomain = originDomain;
}
public Map<String, String> getQueryParams() {
return queryParams;
}
public void setQueryParams(Map<String, String> queryParams) {
this.queryParams = queryParams;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public HttpServletRequest getServletRequest() {
return servletRequest;
}
public String getRawBody() {
return rawBody;
}
}

View File

@@ -0,0 +1,17 @@
package com.iqudoo.framework.tape.base.action.bean;
import javax.servlet.http.HttpServletResponse;
public class ActionServletResponse {
private final HttpServletResponse servletResponse;
public ActionServletResponse(HttpServletResponse servletResponse) {
this.servletResponse = servletResponse;
}
public HttpServletResponse getServletResponse() {
return servletResponse;
}
}

View File

@@ -0,0 +1,69 @@
package com.iqudoo.framework.tape.base.action.bean;
@SuppressWarnings({"unused", "LombokGetterMayBeUsed"})
public class ActionState extends Throwable {
/* 成功 */
public final static ActionState SUCCESS = new ActionState(0, "成功");
/* 错误码:未知错误 */
public final static ActionState UNKNOWN_ERROR = new ActionState(-1, "未知错误");
/* 错误码:空指针异常 */
public static final ActionState NULL_POINTER_ERROR = new ActionState(-2, "服务器空指针异常");
/* 错误码:数据已存在 */
public static final ActionState DATABASE_RECORD_EXISTS = new ActionState(98, "数据已存在,请检查");
/* 错误码数据库SQL错误 */
public static final ActionState DATABASE_SQL_ERROR = new ActionState(99, "数据库SQL错误");
/* 错误码:无效的事件 */
public final static ActionState INVALID_ACTION = new ActionState(100, "找不到接口");
/* 错误码:无效的参数 */
public final static ActionState INVALID_PARAMS = new ActionState(101, "无效的参数");
/* 错误码:鉴权失败 */
public final static ActionState UN_AUTHORIZED = new ActionState(103, "鉴权失败,请重新登录");
/* 错误码:没有权限 */
public final static ActionState NO_PERMISSION = new ActionState(104, "没有权限访问该接口");
/* 错误码:请求超过限流 */
public final static ActionState REQUEST_LIMIT = new ActionState(400, "服务器繁忙,请稍后再试");
/* 错误码:多次重试失败 */
public static final ActionState REQUEST_TOO_MANY_FAIL = new ActionState(401, "服务器繁忙,请稍后再试");
/* 错误码:请求过于频繁,乐观锁 */
public static ActionState getRequestToFrequentError() {
return new ActionState(402, "请求过于频繁,请稍后再试");
}
private final int code;
private final String msg;
public ActionState(String message) {
super(message);
this.code = UNKNOWN_ERROR.getCode();
this.msg = message;
}
public ActionState(int code, String message) {
super(message);
this.code = code;
this.msg = message;
}
public ActionState(String message, Throwable cause) {
super(message, cause);
this.code = UNKNOWN_ERROR.getCode();
this.msg = message;
}
public ActionState(int code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.msg = message;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}

View File

@@ -0,0 +1,13 @@
package com.iqudoo.framework.tape.base.action.bean;
public class ActionStateCanRetry extends ActionState {
public ActionStateCanRetry(int code, String message) {
super(code, message);
}
public ActionStateCanRetry(int code, String message, Throwable cause) {
super(code, message, cause);
}
}

View File

@@ -0,0 +1,41 @@
package com.iqudoo.framework.tape.base.action.bean;
public class ActionTokenInfo {
private String token;
private String refreshToken;
private Long expiresIn;
private Long refreshExpiresIn;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public Long getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Long expiresIn) {
this.expiresIn = expiresIn;
}
public Long getRefreshExpiresIn() {
return refreshExpiresIn;
}
public void setRefreshExpiresIn(Long refreshExpiresIn) {
this.refreshExpiresIn = refreshExpiresIn;
}
}

View File

@@ -0,0 +1,39 @@
package com.iqudoo.framework.tape.base.action.bean;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@SuppressWarnings("LombokGetterMayBeUsed")
class _Attribute {
private final Map<String, Object> attribute = new ConcurrentHashMap<>();
public void removeAttribute(String name) {
attribute.remove(name);
}
public void setAttribute(String name, Object value) {
if (value == null) {
return;
}
attribute.put(name, value);
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name, T def) {
Object value = attribute.get(name);
if (value == null) {
return def;
}
try {
return (T) value;
} catch (Throwable ignored) {
}
return def;
}
public Map<String, Object> getAttribute() {
return attribute;
}
}

View File

@@ -0,0 +1,169 @@
package com.iqudoo.framework.tape.base.action.handler;
import com.iqudoo.framework.tape.base.action.bean.ActionDocInfo;
import com.iqudoo.framework.tape.base.action.bean.ActionRequest;
import com.iqudoo.framework.tape.base.action.bean.ActionServletResponse;
import java.io.OutputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@SuppressWarnings({"BooleanMethodIsAlwaysInverted", "LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public abstract class ActionHandler<T, R> {
private int sort;
private String[] actions = new String[0];
private String[] groups = new String[0];
private String[] scenes = new String[0];
private String[] tag = new String[0];
private int failRetry = 0;
private String failRetryErrorMsg;
private boolean rawData = false;
private boolean mockData = false;
private boolean ignoreLimit = false;
private boolean ignoreAuth = false;
private ActionDocInfo actionDocInfo;
private ActionRequest actionRequest;
private ActionServletResponse actionServletResponse;
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public String[] getActions() {
return actions;
}
public void setActions(String[] actions) {
this.actions = actions;
}
public String[] getGroups() {
return groups;
}
public void setGroups(String[] groups) {
this.groups = groups;
}
public String[] getScenes() {
return scenes;
}
public void setScenes(String[] scenes) {
this.scenes = scenes;
}
public String[] getTag() {
return tag;
}
public void setTag(String[] tag) {
this.tag = tag;
}
public int getFailRetry() {
return failRetry;
}
public void setFailRetry(int failRetry) {
this.failRetry = failRetry;
}
public String getFailRetryErrorMsg() {
return failRetryErrorMsg;
}
public void setFailRetryErrorMsg(String failRetryErrorMsg) {
this.failRetryErrorMsg = failRetryErrorMsg;
}
public boolean isRawData() {
return rawData;
}
public void setRawData(boolean rawData) {
this.rawData = rawData;
}
public boolean isMockData() {
return mockData;
}
public void setMockData(boolean mockData) {
this.mockData = mockData;
}
public boolean isIgnoreLimit() {
return ignoreLimit;
}
public void setIgnoreLimit(boolean ignoreLimit) {
this.ignoreLimit = ignoreLimit;
}
public boolean isIgnoreAuth() {
return ignoreAuth;
}
public void setIgnoreAuth(boolean ignoreAuth) {
this.ignoreAuth = ignoreAuth;
}
public ActionDocInfo getActionDocInfo() {
return actionDocInfo;
}
public void setActionDocInfo(ActionDocInfo actionDocInfo) {
this.actionDocInfo = actionDocInfo;
}
public ActionRequest getActionRequest() {
return actionRequest;
}
public void setActionRequest(ActionRequest actionRequest) {
this.actionRequest = actionRequest;
}
public ActionServletResponse getActionServletResponse() {
return actionServletResponse;
}
public void setActionServletResponse(ActionServletResponse actionServletResponse) {
this.actionServletResponse = actionServletResponse;
}
public OutputStream getServletOutputStream() throws Throwable {
return getActionServletResponse().getServletResponse().getOutputStream();
}
public R onMockRequest(T param) throws Throwable {
return null;
}
public void onPreValidateRequest(T param) throws Throwable {
// pre validate request
}
public void onValidateRequest(T param) throws Throwable {
// validate request
}
public abstract R onHandleRequest(T param) throws Throwable;
public Type getParamType() {
Type type = null;
Type superType = getClass().getGenericSuperclass();
if (superType instanceof ParameterizedType) {
Type[] types = ((ParameterizedType) superType).getActualTypeArguments();
type = types[0];
}
return type;
}
}

View File

@@ -0,0 +1,5 @@
package com.iqudoo.framework.tape.base.action.handler;
public abstract class DefaultActionHandler extends SimpleActionHandler<Object> {
}

View File

@@ -0,0 +1,7 @@
package com.iqudoo.framework.tape.base.action.handler;
import com.iqudoo.framework.tape.modules.proto.Proto;
public abstract class ProtoActionHandler<T> extends ActionHandler<T, Proto<?>> {
}

View File

@@ -0,0 +1,19 @@
package com.iqudoo.framework.tape.base.action.handler;
import java.lang.reflect.Type;
public abstract class SimpleActionHandler<T> extends ActionHandler<Void, T> {
@Override
public T onHandleRequest(Void param) throws Throwable {
return onSimpleHandle();
}
public abstract T onSimpleHandle() throws Throwable;
@Override
public Type getParamType() {
return Void.class;
}
}

View File

@@ -0,0 +1,21 @@
package com.iqudoo.framework.tape.base.action.handler;
import com.iqudoo.framework.tape.modules.proto.Proto;
import java.lang.reflect.Type;
public abstract class SimpleProtoActionHandler extends ProtoActionHandler<Void> {
@Override
public Proto<?> onHandleRequest(Void param) throws Throwable {
return onSimpleHandle();
}
public abstract Proto<?> onSimpleHandle() throws Throwable;
@Override
public Type getParamType() {
return Void.class;
}
}

View File

@@ -0,0 +1,12 @@
package com.iqudoo.framework.tape.base.action.interfaces;
import com.iqudoo.framework.tape.base.action.bean.ActionParameter;
import com.iqudoo.framework.tape.base.action.bean.ActionServletRequest;
import com.iqudoo.framework.tape.base.action.bean.ActionServletResponse;
public interface IActionCall {
<T, R> Object call(ActionParameter actionParameter, ActionServletRequest servletRequest,
ActionServletResponse servletResponse);
}

View File

@@ -0,0 +1,15 @@
package com.iqudoo.framework.tape.base.action.interfaces;
import com.iqudoo.framework.tape.base.action.handler.ActionHandler;
import java.util.List;
public interface IActionDiscover {
List<ActionHandler<?, ?>> getHandlerList();
<T, R> ActionHandler<T, R> getActionHandlerByAction(String action);
<T, R> ActionHandler<T, R> getActionHandlerByApiDocId(String action);
}

View File

@@ -0,0 +1,16 @@
package com.iqudoo.framework.tape.base.action.interfaces;
import com.alibaba.fastjson.JSONObject;
import com.iqudoo.framework.tape.base.action.bean.ActionServletRequest;
import com.iqudoo.framework.tape.base.action.bean.ActionServletResponse;
import java.util.Map;
public interface IActionDispatch {
Object doRequest(JSONObject body,
Map<String, String> params,
ActionServletRequest servletRequest,
ActionServletResponse servletResponse) throws Throwable;
}

View File

@@ -0,0 +1,33 @@
package com.iqudoo.framework.tape.base.action.interfaces;
import com.iqudoo.framework.tape.base.action.bean.ActionAuthInfo;
import com.iqudoo.framework.tape.base.action.bean.ActionTokenInfo;
import com.iqudoo.framework.tape.base.docs.annotation.ApiDocFieldConfiguration;
public interface IActionToken {
ActionTokenInfo createToken(ActionAuthInfo actionAuthInfo) throws Throwable;
ActionTokenInfo refreshToken(String refreshToken) throws Throwable;
ActionAuthInfo parseToken(String token) throws Throwable;
Throwable getAuthError();
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
class RefreshTokenParam {
@ApiDocFieldConfiguration(desc = "刷新TOKEN")
private String refreshToken;
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
}
}

View File

@@ -0,0 +1,15 @@
package com.iqudoo.framework.tape.base.docs.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiDocActionConfiguration {
/* 返回值类型 */
Class<?>[] response() default Void.class;
}

View File

@@ -0,0 +1,21 @@
package com.iqudoo.framework.tape.base.docs.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiDocControllerConfiguration {
/* 接口名称 */
String name();
/* 接口描述 */
String desc() default "";
/* 接口排序 */
int sort() default 0;
}

View File

@@ -0,0 +1,15 @@
package com.iqudoo.framework.tape.base.docs.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiDocEnumConfiguration {
/* 枚举别名 */
String[] alias() default "";
}

View File

@@ -0,0 +1,27 @@
package com.iqudoo.framework.tape.base.docs.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiDocFieldConfiguration {
/* 描述 */
String desc() default "";
/* 是否必填 */
boolean required() default false;
/* 忽略字段 */
boolean ignore() default false;
/* 泛型位置 */
int[] parametricIndex() default {};
/* 枚举类型 */
Class<?> enumClass() default Void.class;
}

View File

@@ -0,0 +1,300 @@
package com.iqudoo.framework.tape.base.docs.bean;
import java.io.Serializable;
import java.util.List;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed", "unused"})
public class ApiDocActionInfo {
private String id;
private String[] action;
private String[] groups;
private String[] scenes;
private Class<?> param;
private Class<?> response;
private Class<?> handler;
private BeamInfo paramModel;
private BeamInfo responseModel;
private int sort;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String[] getAction() {
return action;
}
public void setAction(String[] action) {
this.action = action;
}
public String[] getGroups() {
return groups;
}
public void setGroups(String[] groups) {
this.groups = groups;
}
public String[] getScenes() {
return scenes;
}
public void setScenes(String[] scenes) {
this.scenes = scenes;
}
public Class<?> getParam() {
return param;
}
public void setParam(Class<?> param) {
this.param = param;
}
public Class<?> getResponse() {
return response;
}
public void setResponse(Class<?> response) {
this.response = response;
}
public Class<?> getHandler() {
return handler;
}
public void setHandler(Class<?> handler) {
this.handler = handler;
}
public BeamInfo getParamModel() {
return paramModel;
}
public void setParamModel(BeamInfo paramModel) {
this.paramModel = paramModel;
}
public BeamInfo getResponseModel() {
return responseModel;
}
public void setResponseModel(BeamInfo responseModel) {
this.responseModel = responseModel;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public static class BeamInfo implements Serializable {
private String modelPath;
private String className;
private String simpleName;
private boolean basicProperty;
private List<BeamField> fields;
private List<Class<?>> parametricTypes;
private List<BeamInfo> parametricModels;
private int parametricLength;
public String getModelPath() {
return modelPath;
}
public void setModelPath(String modelPath) {
this.modelPath = modelPath;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getSimpleName() {
return simpleName;
}
public void setSimpleName(String simpleName) {
this.simpleName = simpleName;
}
public boolean isBasicProperty() {
return basicProperty;
}
public void setBasicProperty(boolean basicProperty) {
this.basicProperty = basicProperty;
}
public List<BeamField> getFields() {
return fields;
}
public void setFields(List<BeamField> fields) {
this.fields = fields;
}
public List<Class<?>> getParametricTypes() {
return parametricTypes;
}
public void setParametricTypes(List<Class<?>> parametricTypes) {
this.parametricTypes = parametricTypes;
}
public List<BeamInfo> getParametricModels() {
return parametricModels;
}
public void setParametricModels(List<BeamInfo> parametricModels) {
this.parametricModels = parametricModels;
}
public int getParametricLength() {
return parametricLength;
}
public void setParametricLength(int parametricLength) {
this.parametricLength = parametricLength;
}
}
public static class BeamField implements Serializable {
private String fieldPath;
private String name;
private String typeName;
private String desc;
private Class<?> type;
private boolean required;
private boolean basicProperty;
private Object defaultValue;
private BeamInfo model;
private List<Class<?>> parametricTypes;
private List<BeamInfo> parametricModels;
private int parametricLength;
private ApiDocEnumInfo enumInfo;
public String getFieldPath() {
return fieldPath;
}
public void setFieldPath(String fieldPath) {
this.fieldPath = fieldPath;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public boolean isBasicProperty() {
return basicProperty;
}
public void setBasicProperty(boolean basicProperty) {
this.basicProperty = basicProperty;
}
public Object getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
public BeamInfo getModel() {
return model;
}
public void setModel(BeamInfo model) {
this.model = model;
}
public List<Class<?>> getParametricTypes() {
return parametricTypes;
}
public void setParametricTypes(List<Class<?>> parametricTypes) {
this.parametricTypes = parametricTypes;
}
public List<BeamInfo> getParametricModels() {
return parametricModels;
}
public void setParametricModels(List<BeamInfo> parametricModels) {
this.parametricModels = parametricModels;
}
public int getParametricLength() {
return parametricLength;
}
public void setParametricLength(int parametricLength) {
this.parametricLength = parametricLength;
}
public ApiDocEnumInfo getEnumInfo() {
return enumInfo;
}
public void setEnumInfo(ApiDocEnumInfo enumInfo) {
this.enumInfo = enumInfo;
}
}
}

View File

@@ -0,0 +1,61 @@
package com.iqudoo.framework.tape.base.docs.bean;
public class ApiDocControllerInfo {
private String name;
private Class<?> controller;
private String path;
private String method;
private String desc;
private int sort;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Class<?> getController() {
return controller;
}
public void setController(Class<?> controller) {
this.controller = controller;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
}

View File

@@ -0,0 +1,36 @@
package com.iqudoo.framework.tape.base.docs.bean;
import java.util.List;
import java.util.Map;
public class ApiDocEnumInfo {
private String name;
private Class<?> type;
private List<Map<String, Object>> properties;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
public List<Map<String, Object>> getProperties() {
return properties;
}
public void setProperties(List<Map<String, Object>> properties) {
this.properties = properties;
}
}

View File

@@ -0,0 +1,13 @@
package com.iqudoo.framework.tape.base.docs.interfaces;
import com.iqudoo.framework.tape.base.docs.bean.ApiDocActionInfo;
import java.util.List;
public interface IDocAction {
List<ApiDocActionInfo> getActionList() throws Throwable;
ApiDocActionInfo getActionById(String id) throws Throwable;
}

View File

@@ -0,0 +1,11 @@
package com.iqudoo.framework.tape.base.docs.interfaces;
import com.iqudoo.framework.tape.base.docs.bean.ApiDocControllerInfo;
import java.util.List;
public interface IDocController {
List<ApiDocControllerInfo> getControllerList() throws Throwable;
}

View File

@@ -0,0 +1,15 @@
package com.iqudoo.framework.tape.base.docs.interfaces;
import com.iqudoo.framework.tape.base.docs.bean.ApiDocEnumInfo;
import java.util.List;
public interface IDocEnum {
ApiDocEnumInfo getEnumInfo(Class<?> clazz) throws Throwable;
List<ApiDocEnumInfo> getEnumList() throws Throwable;
ApiDocEnumInfo getEnumByName(String enumName) throws Throwable;
}

View File

@@ -0,0 +1,12 @@
package com.iqudoo.framework.tape.modules;
import com.iqudoo.framework.tape.modules.async.AsyncHandler;
import com.iqudoo.framework.tape.modules.async.AsyncScheduler;
public interface IModuleAsync {
<D, T> void doAsyncTask(T params, AsyncHandler<D, T> asyncHandler) throws Throwable;
void doScheduleTask(AsyncScheduler asyncScheduler) throws Throwable;
}

View File

@@ -0,0 +1,18 @@
package com.iqudoo.framework.tape.modules;
import com.iqudoo.framework.tape.modules.cache.CacheKey;
import com.iqudoo.framework.tape.modules.cache.CacheLoader;
public interface IModuleCache {
void setCacheData(CacheKey key, Object value) throws Throwable;
<D, T> D getCacheData(CacheKey key, T params, CacheLoader<D, T> cacheLoader) throws Throwable;
void removeCacheDataByPrefix(String prefix) throws Throwable;
void removeCacheData(CacheKey key) throws Throwable;
void clearCache() throws Throwable;
}

View File

@@ -0,0 +1,10 @@
package com.iqudoo.framework.tape.modules;
import com.iqudoo.framework.tape.modules.crawler.CrawlerKey;
import com.iqudoo.framework.tape.modules.crawler.CrawlerLoader;
public interface IModuleCrawler {
<D, T> D getCrawlerData(CrawlerKey key, T params, CrawlerLoader<D, T> crawlerLoader) throws Throwable;
}

View File

@@ -0,0 +1,51 @@
package com.iqudoo.framework.tape.modules;
import java.util.List;
public interface IModuleFactory {
void register(Object object, Class<?> factoryClass, String name, String description);
<T> T get(Class<T> factoryClass, String name) throws Throwable;
<T> List<T> getList(Class<T> factoryClass) throws Throwable;
String getName(Class<?> factoryClass, Object factoryObject) throws Throwable;
List<String> getNameList(Class<?> factoryClass) throws Throwable;
boolean has(Class<?> factoryClass, String name) throws Throwable;
FactoryInfo getFactoryInfo(Class<?> factoryClass, String name) throws Throwable;
class FactoryInfo {
private String name;
private String description;
private Class<?> factoryClass;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Class<?> getFactoryClass() {
return factoryClass;
}
public void setFactoryClass(Class<?> factoryClass) {
this.factoryClass = factoryClass;
}
}
}

View File

@@ -0,0 +1,26 @@
package com.iqudoo.framework.tape.modules;
import com.iqudoo.framework.tape.modules.fileManager.FileConfig;
import com.iqudoo.framework.tape.modules.fileManager.FileInfo;
import java.util.List;
public interface IModuleFileManager {
FileInfo getFile(String filePath, FileConfig config);
List<FileInfo> getFiles(String folderPath, FileConfig config);
void createFolder(String folderPath, FileConfig config) throws Throwable;
void renameFile(String targetPath, String filePath, FileConfig config) throws Throwable;
void deleteFile(String targetPath, FileConfig config) throws Throwable;
boolean hasFile(String filePath, FileConfig config);
boolean hasFolder(String filePath, FileConfig config) throws Throwable;
boolean isFolder(String filePath, FileConfig config) throws Throwable;
}

View File

@@ -0,0 +1,13 @@
package com.iqudoo.framework.tape.modules;
public interface IModuleLock {
void lock(String key) throws Throwable;
void lock(String key, long timeoutMs) throws Throwable;
void lock(String key, long timeoutMs, Throwable throwable) throws Throwable;
void unlock(String key) throws Throwable;
}

View File

@@ -0,0 +1,12 @@
package com.iqudoo.framework.tape.modules;
import com.iqudoo.framework.tape.modules.session.GetSessionParam;
import com.iqudoo.framework.tape.modules.session.RefreshSessionParam;
public interface IModuleSession {
String getSession(GetSessionParam param) throws Throwable;
String refreshSession(RefreshSessionParam param) throws Throwable;
}

View File

@@ -0,0 +1,382 @@
package com.iqudoo.framework.tape.modules;
import com.iqudoo.framework.tape.base.docs.annotation.ApiDocFieldConfiguration;
import java.util.List;
public interface IModuleSetting {
List<SettingInfo> getList(String group);
SettingInfo getSettingInfo(String group, String key);
<D> D getSetting(Class<D> type, String... bizArgs) throws Throwable;
void clearCache(String key, String... bizArgs);
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed", "unused"})
class SettingInfo {
@ApiDocFieldConfiguration(desc = "配置KEY")
private String key;
@ApiDocFieldConfiguration(desc = "配置类型")
private Class<?> type;
@ApiDocFieldConfiguration(desc = "配置标题")
private String title;
@ApiDocFieldConfiguration(desc = "配置说明")
private String desc;
@ApiDocFieldConfiguration(desc = "备注信息")
private String readme;
@ApiDocFieldConfiguration(desc = "排序编号")
private int sort = 0;
@ApiDocFieldConfiguration(desc = "是否隐藏: 0或1")
private boolean isHidden;
@ApiDocFieldConfiguration(desc = "是否公开")
private boolean isPublic;
@ApiDocFieldConfiguration(desc = "字段列表")
private List<FieldInfo> fields;
@ApiDocFieldConfiguration(desc = "默认值")
private String defaultValue;
@ApiDocFieldConfiguration(desc = "刷新页面")
private boolean refreshPage;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getReadme() {
return readme;
}
public void setReadme(String readme) {
this.readme = readme;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public boolean isHidden() {
return isHidden;
}
public void setHidden(boolean hidden) {
isHidden = hidden;
}
public boolean isPublic() {
return isPublic;
}
public void setPublic(boolean aPublic) {
isPublic = aPublic;
}
public List<FieldInfo> getFields() {
return fields;
}
public void setFields(List<FieldInfo> fields) {
this.fields = fields;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public boolean isRefreshPage() {
return refreshPage;
}
public void setRefreshPage(boolean refreshPage) {
this.refreshPage = refreshPage;
}
}
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed", "unused"})
class FieldInfo {
@ApiDocFieldConfiguration(desc = "字段名称")
private String name;
@ApiDocFieldConfiguration(desc = "字段标题")
private String label;
@ApiDocFieldConfiguration(desc = "提示文案")
private String tips;
@ApiDocFieldConfiguration(desc = "字段类型",
enumClass = SettingFieldType.class)
private String type;
@ApiDocFieldConfiguration(desc = "选择项每行一个选项key|value分割")
private String options;
@ApiDocFieldConfiguration(desc = "文本最大长度")
private int textLenMax;
@ApiDocFieldConfiguration(desc = "文本行数")
private int textRows;
@ApiDocFieldConfiguration(desc = "文件大小限制单位M")
private int fileSize;
@ApiDocFieldConfiguration(desc = "文件数量限制")
private int fileCount;
@ApiDocFieldConfiguration(desc = "数值最大值")
private double numMax;
@ApiDocFieldConfiguration(desc = "数值最小值")
private double numMin;
@ApiDocFieldConfiguration(desc = "数值步进大小")
private double numStep;
@ApiDocFieldConfiguration(desc = "数值小数点位数")
private int numPrecision;
@ApiDocFieldConfiguration(desc = "默认值")
private Object defaultValue;
@ApiDocFieldConfiguration(desc = "显示公式")
private String show;
@ApiDocFieldConfiguration(desc = "是否必填")
private boolean required;
@ApiDocFieldConfiguration(desc = "是否禁用")
private boolean disabled;
@ApiDocFieldConfiguration(desc = "是否敏感")
private boolean isSensitive;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getTips() {
return tips;
}
public void setTips(String tips) {
this.tips = tips;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
public int getTextLenMax() {
return textLenMax;
}
public void setTextLenMax(int textLenMax) {
this.textLenMax = textLenMax;
}
public int getFileSize() {
return fileSize;
}
public void setFileSize(int fileSize) {
this.fileSize = fileSize;
}
public int getFileCount() {
return fileCount;
}
public void setFileCount(int fileCount) {
this.fileCount = fileCount;
}
public double getNumMax() {
return numMax;
}
public void setNumMax(double numMax) {
this.numMax = numMax;
}
public double getNumMin() {
return numMin;
}
public void setNumMin(double numMin) {
this.numMin = numMin;
}
public double getNumStep() {
return numStep;
}
public void setNumStep(double numStep) {
this.numStep = numStep;
}
public int getNumPrecision() {
return numPrecision;
}
public void setNumPrecision(int numPrecision) {
this.numPrecision = numPrecision;
}
public int getTextRows() {
return textRows;
}
public void setTextRows(int textRows) {
this.textRows = textRows;
}
public Object getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
public String getShow() {
return show;
}
public void setShow(String show) {
this.show = show;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public boolean isSensitive() {
return isSensitive;
}
public void setSensitive(boolean sensitive) {
isSensitive = sensitive;
}
}
@SuppressWarnings("LombokGetterMayBeUsed")
enum SettingFieldType {
// 自动类型
AUTO("auto", "自动"),
// 单行文本
TYPE_TEXT("text", "单行文本"),
// 多行文本
TYPE_TEXTAREA("textarea", "多行文本"),
// 开关类型
TYPE_BOOLEAN("boolean", "开关"),
// 数字类型
TYPE_NUMBER("number", "数字"),
// 单选框
TYPE_RADIO("radio", "单选框"),
// 时间类型
TYPE_TIMESTAMP("timestamp", "时间戳"),
// 多选框
TYPE_CHECKBOX("checkbox", "多选框"),
// 下拉选择
TYPE_SELECT("select", "下拉选择"),
// 图片类型
TYPE_IMAGE("image", "图片"),
// 文件类型
TYPE_FILE("file", "文件"),
// 文件Base64类型
TYPE_FILE_BASE64("fileBase64", "文件Base64");
private final String value;
private final String desc;
SettingFieldType(String value, String desc) {
this.value = value;
this.desc = desc;
}
public String getValue() {
return value;
}
public String getDesc() {
return desc;
}
}
}

View File

@@ -0,0 +1,24 @@
package com.iqudoo.framework.tape.modules.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface FactoryMapping {
/* 方法名 */
String name();
/* 支持环境 */
String[] actives() default {};
/* 说明信息 */
String description() default "";
}

View File

@@ -0,0 +1,45 @@
package com.iqudoo.framework.tape.modules.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface SettingConfiguration {
/* 配置分类 */
String group();
/* 配置KEY */
String key();
/* 配置标题 */
String title() default "";
/* 配置说明 */
String desc() default "";
/* 配置提示 */
String readme() default "";
/* 排序 */
int sort() default 0;
/* 是否隐藏 */
boolean isHidden() default false;
/* 是否公开 */
boolean isPublic() default false;
/* 刷新页面 */
boolean refreshPage() default false;
/* 缓存时长 */
long cacheTime() default 10000;
}

View File

@@ -0,0 +1,62 @@
package com.iqudoo.framework.tape.modules.annotation;
import com.iqudoo.framework.tape.modules.IModuleSetting;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SettingField {
/* 备注说明 */
String label() default "";
/* 输入提示 */
String tips() default "";
/* 选项信息 */
String[] options() default "";
/* 是否显示 */
String show() default "(record) => true";
/* 是否必填 */
boolean required() default false;
/* 是否禁用 */
boolean disabled() default false;
/* 是否敏感 */
boolean isSensitive() default false;
/* 最大长度 */
int lenMax() default Integer.MAX_VALUE;
/* 文件大小(MB) */
int fileSize() default 2;
/* 文件大小 */
int fileCount() default 1;
/* 最大数字 */
double numMax() default Integer.MAX_VALUE;
/* 最小数字 */
double numMin() default Integer.MIN_VALUE;
/* 数字步长 */
float numStep() default 1;
/* 保留小数 */
int numPrecision() default 0;
/* 输入行数 */
int rows() default 2;
/* 属性类型 */
IModuleSetting.SettingFieldType type() default IModuleSetting.SettingFieldType.AUTO;
}

View File

@@ -0,0 +1,223 @@
package com.iqudoo.framework.tape.modules.asserts;
import java.util.List;
/**
* 断言工具类
*/
public class AssertUtil {
/**
* 断言对象不是无效的
*
* @param obj 对象
* @param checker 断言处理器
*/
public static <T> void oneNotInvalid(T obj, AssertValidator<T> checker) {
oneNotInvalidCustom(new Object[]{obj}, new AssertNotInvalidValidator<>(null, new AssertValidator<Object>() {
@Override
@SuppressWarnings("unchecked")
public boolean assertFlag(Object obj) {
return checker.check((T) obj);
}
@Override
public String assertMessage() {
if (checker != null) {
return checker.assertMessage();
}
return null;
}
}));
}
/**
* 断言对象不是无效的
*
* @param obj 对象
* @param message 错误提示
*/
public static void oneNotInvalid(Object obj, String message) {
oneNotInvalidCustom(new Object[]{obj}, new AssertNotInvalidValidator<>(message));
}
/**
* 断言至少一个对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param message 错误提示
*/
public static void oneNotInvalid(Object obj1, Object obj2, String message) {
oneNotInvalidCustom(new Object[]{obj1, obj2}, new AssertNotInvalidValidator<>(message));
}
/**
* 断言至少一个对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param checker 断言处理器
*/
public static void oneNotInvalid(Object obj1, Object obj2, AssertValidator<Object> checker) {
oneNotInvalidCustom(new Object[]{obj1, obj2}, new AssertNotInvalidValidator<>(null, checker));
}
/**
* 断言至少一个对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param obj3 对象3
* @param message 错误提示
*/
public static void oneNotInvalid(Object obj1, Object obj2, Object obj3, String message) {
oneNotInvalidCustom(new Object[]{obj1, obj2, obj3}, new AssertNotInvalidValidator<>(message));
}
/**
* 断言至少一个对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param obj3 对象3
* @param checker 断言处理器
*/
public static void oneNotInvalid(Object obj1, Object obj2, Object obj3, AssertValidator<Object> checker) {
oneNotInvalidCustom(new Object[]{obj1, obj2, obj3}, new AssertNotInvalidValidator<>(null, checker));
}
/**
* 断言至少一个对象不是无效的
*
* @param obj 对象数组
* @param checker 断言处理器
*/
public static <T> void oneNotInvalidCustom(T[] obj, AssertValidator<T> checker) {
boolean assertFlag = true;
for (T object : obj) {
if (!checker.check(object)) {
assertFlag = false;
break;
}
}
if (!assertFlag) {
return;
}
throw new IllegalArgumentException(checker.getAssertMessage());
}
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
/**
* 断言全部对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param message 错误提示
*/
public static void allNotInvalid(Object obj1, Object obj2, String message) {
allNotInvalidCustom(new Object[]{obj1, obj2}, new AssertNotInvalidValidator<>(message));
}
/**
* 断言全部对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param checker 断言处理器
*/
public static void allNotInvalid(Object obj1, Object obj2, AssertValidator<Object> checker) {
allNotInvalidCustom(new Object[]{obj1, obj2}, new AssertNotInvalidValidator<>(null, checker));
}
/**
* 断言全部对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param obj3 对象3
* @param message 错误提示
*/
public static void allNotInvalid(Object obj1, Object obj2, Object obj3, String message) {
allNotInvalidCustom(new Object[]{obj1, obj2, obj3}, new AssertNotInvalidValidator<>(message));
}
/**
* 断言全部对象不是无效的
*
* @param obj1 对象1
* @param obj2 对象2
* @param obj3 对象3
* @param checker 断言处理器
*/
public static void allNotInvalid(Object obj1, Object obj2, Object obj3, AssertValidator<Object> checker) {
allNotInvalidCustom(new Object[]{obj1, obj2, obj3}, new AssertNotInvalidValidator<>(null, checker));
}
/**
* 断言全部对象不是无效的
*
* @param obj 对象数组
* @param checker 断言处理器
*/
public static <T> void allNotInvalidCustom(T[] obj, AssertValidator<T> checker) {
boolean assertFlag = false;
for (T object : obj) {
if (checker.check(object)) {
assertFlag = true;
break;
}
}
if (!assertFlag) {
return;
}
throw new IllegalArgumentException(checker.getAssertMessage());
}
/**
* 默认的不为NULL断言验证器
*
* @param <T> 对象类型
*/
private static class AssertNotInvalidValidator<T> extends AssertValidator<T> {
private final String message;
public AssertNotInvalidValidator(String message) {
this(message, null);
}
public AssertNotInvalidValidator(String message, AssertValidator<T> next) {
super(next);
this.message = message;
}
@Override
public String assertMessage() {
return message;
}
@Override
public boolean assertFlag(T obj) {
if (obj == null) {
return true;
}
if (obj instanceof String) {
return ((String) obj).trim().length() == 0;
}
if (obj instanceof List) {
return ((List<?>) obj).size() == 0;
}
return false;
}
}
}

View File

@@ -0,0 +1,46 @@
package com.iqudoo.framework.tape.modules.asserts;
public abstract class AssertValidator<T> {
private final AssertValidator<T> next;
public AssertValidator() {
this.next = null;
}
public AssertValidator(AssertValidator<T> next) {
this.next = next;
}
public abstract boolean assertFlag(T obj);
public abstract String assertMessage();
/////////////////////////////////////////////////
/////////////////////////////////////////////////
protected boolean check(T obj) {
try {
if (assertFlag(obj)) {
return true;
}
} catch (Throwable ignored) {
return true;
}
if (next != null) {
return next.check(obj);
}
return false;
}
protected String getAssertMessage() {
String assertMessage = assertMessage();
if (assertMessage == null) {
if (next != null) {
assertMessage = next.getAssertMessage();
}
}
return assertMessage;
}
}

View File

@@ -0,0 +1,22 @@
package com.iqudoo.framework.tape.modules.asserts;
public class AssertValidatorArrayEmpty<T> extends AssertValidator<T[]> {
private final String message;
public AssertValidatorArrayEmpty(String message) {
super(null);
this.message = message;
}
@Override
public boolean assertFlag(T[] obj) {
return obj.length <= 0;
}
@Override
public String assertMessage() {
return message;
}
}

View File

@@ -0,0 +1,24 @@
package com.iqudoo.framework.tape.modules.asserts;
import java.util.List;
public class AssertValidatorListEmpty<T> extends AssertValidator<List<T>> {
private final String message;
public AssertValidatorListEmpty(String message) {
super(null);
this.message = message;
}
@Override
public boolean assertFlag(List<T> obj) {
return obj.size() <= 0;
}
@Override
public String assertMessage() {
return message;
}
}

View File

@@ -0,0 +1,19 @@
package com.iqudoo.framework.tape.modules.async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.List;
public interface AsyncHandler<D, T> {
ThreadPoolTaskExecutor taskExecutor();
long sleepTime() throws Throwable;
void doExecute(D data) throws Throwable;
void onComplete(T params) throws Throwable;
List<D> loadTasks(T params, int poolSize) throws Throwable;
}

View File

@@ -0,0 +1,11 @@
package com.iqudoo.framework.tape.modules.async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
public interface AsyncScheduler {
ThreadPoolTaskExecutor taskExecutor();
void scheduleTask() throws Throwable;
}

View File

@@ -0,0 +1,68 @@
package com.iqudoo.framework.tape.modules.cache;
import com.fasterxml.jackson.databind.JavaType;
/**
* 缓存数据
*
* @param <D>
*/
public class Cache<D, T> {
private final GetterCacheProxy<D> getterCacheProxy;
private final SetterCacheProxy<D, T> setterCacheProxy;
private final CacheLoader<D, T> cacheLoader;
public Cache(GetterCacheProxy<D> getterCacheProxy,
SetterCacheProxy<D, T> setterCacheProxy,
CacheLoader<D, T> cacheLoader) {
this.getterCacheProxy = getterCacheProxy;
this.setterCacheProxy = setterCacheProxy;
this.cacheLoader = cacheLoader;
}
public GetterCacheProxy<D> getGetterCacheProxy() {
return getterCacheProxy;
}
public SetterCacheProxy<D, T> getSetterCacheProxy() {
return setterCacheProxy;
}
public CacheLoader<D, T> getCacheLoader() {
return cacheLoader;
}
public D readData(CacheKey cacheKey, T loadParams) throws Throwable {
D data = null;
if (getGetterCacheProxy().has(cacheKey)) {
try {
data = getGetterCacheProxy().onGet(cacheKey, getCacheLoader().getType());
} catch (Throwable ignored) {
}
}
if (data == null) {
data = getCacheLoader().onLoad(cacheKey, loadParams);
if (data != null) {
getSetterCacheProxy().onSet(cacheKey, data, loadParams);
}
}
return data;
}
public abstract static class GetterCacheProxy<D> {
public abstract boolean has(CacheKey cacheKey) throws Throwable;
public abstract D onGet(CacheKey cacheKey, JavaType resultType) throws Throwable;
}
public abstract static class SetterCacheProxy<D, T> {
public abstract void onSet(CacheKey cacheKey, D newValue, T params) throws Throwable;
}
}

View File

@@ -0,0 +1,52 @@
package com.iqudoo.framework.tape.modules.cache;
public class CacheKey {
public static CacheKey builder(String key) {
return new CacheKey().setCacheKey(key);
}
public static CacheKey builder(String format, Object... args) {
return new CacheKey().setCacheKey(String.format(format, args));
}
private String cacheKey;
private boolean forceRequest = false;
private long expireMillis = 0L;
private CacheKey() {
}
public String getCacheKey() {
return cacheKey;
}
public CacheKey setCacheKey(String cacheKey) {
this.cacheKey = cacheKey;
return this;
}
public boolean isForceRequest() {
return forceRequest;
}
public CacheKey setForceRequest(boolean forceRequest) {
this.forceRequest = forceRequest;
return this;
}
public long getExpireMillis() {
return expireMillis;
}
public CacheKey setExpireMillis(long expireMillis) {
this.expireMillis = expireMillis;
return this;
}
@Override
public String toString() {
return this.cacheKey;
}
}

View File

@@ -0,0 +1,22 @@
package com.iqudoo.framework.tape.modules.cache;
import com.fasterxml.jackson.databind.JavaType;
import com.iqudoo.framework.tape.modules.utils.JsonUtil;
import com.iqudoo.framework.tape.modules.utils.ReflectUtil;
import java.lang.reflect.Type;
public abstract class CacheLoader<D, T> {
public JavaType getType() {
try {
Type type = ReflectUtil.getType(getClass(), 0);
return JsonUtil.constructJavaType(type);
} catch (Throwable ignored) {
}
return null;
}
public abstract D onLoad(CacheKey cacheKey, T params) throws Throwable;
}

View File

@@ -0,0 +1,68 @@
package com.iqudoo.framework.tape.modules.crawler;
import com.fasterxml.jackson.databind.JavaType;
/**
* 缓存数据
*
* @param <D>
*/
public class Crawler<D, T> {
private final GetterCrawlerProxy<D> getterCrawlerProxy;
private final SetterCrawlerProxy<D, T> setterCrawlerProxy;
private final CrawlerLoader<D, T> crawlerLoader;
public Crawler(GetterCrawlerProxy<D> getterCrawlerProxy,
SetterCrawlerProxy<D, T> setterCrawlerProxy,
CrawlerLoader<D, T> crawlerLoader) {
this.getterCrawlerProxy = getterCrawlerProxy;
this.setterCrawlerProxy = setterCrawlerProxy;
this.crawlerLoader = crawlerLoader;
}
public GetterCrawlerProxy<D> getGetterCrawlerProxy() {
return getterCrawlerProxy;
}
public SetterCrawlerProxy<D, T> getSetterCrawlerProxy() {
return setterCrawlerProxy;
}
public CrawlerLoader<D, T> getCrawlerLoader() {
return crawlerLoader;
}
public D readData(CrawlerKey crawlerKey, T loadParams) throws Throwable {
D data = null;
if (getGetterCrawlerProxy().has(crawlerKey)) {
try {
data = getGetterCrawlerProxy().onGet(crawlerKey, getCrawlerLoader().getJavaType());
} catch (Throwable ignored) {
}
}
if (data == null) {
data = getCrawlerLoader().onLoad(crawlerKey, loadParams);
if (data != null) {
getSetterCrawlerProxy().onSet(crawlerKey, data, loadParams);
}
}
return data;
}
public abstract static class GetterCrawlerProxy<D> {
public abstract boolean has(CrawlerKey crawlerKey) throws Throwable;
public abstract D onGet(CrawlerKey crawlerKey, JavaType resultType) throws Throwable;
}
public abstract static class SetterCrawlerProxy<D, T> {
public abstract void onSet(CrawlerKey crawlerKey, D newValue, T params) throws Throwable;
}
}

View File

@@ -0,0 +1,65 @@
package com.iqudoo.framework.tape.modules.crawler;
public class CrawlerKey {
public static CrawlerKey builder(String type, String keyword) {
return new CrawlerKey().setCrawlerType(type)
.setCrawlerKeyword(keyword);
}
public static CrawlerKey builder(String type, String keyword, long expire) {
return new CrawlerKey().setCrawlerType(type)
.setCrawlerKeyword(keyword)
.setExpireMillis(expire);
}
private String crawlerType;
private String crawlerKeyword;
private boolean forceRequest = false;
private long expireMillis = 0L;
private CrawlerKey() {
}
public String getCrawlerType() {
return crawlerType;
}
public CrawlerKey setCrawlerType(String crawlerType) {
this.crawlerType = crawlerType;
return this;
}
public String getCrawlerKeyword() {
return crawlerKeyword;
}
public CrawlerKey setCrawlerKeyword(String crawlerKeyword) {
this.crawlerKeyword = crawlerKeyword;
return this;
}
public long getExpireMillis() {
return expireMillis;
}
public CrawlerKey setExpireMillis(long expireMillis) {
this.expireMillis = expireMillis;
return this;
}
public CrawlerKey setForceRequest(boolean forceRequest) {
this.forceRequest = forceRequest;
return this;
}
public boolean isForceRequest() {
return forceRequest;
}
@Override
public String toString() {
return this.crawlerType + "_" + this.crawlerKeyword;
}
}

View File

@@ -0,0 +1,22 @@
package com.iqudoo.framework.tape.modules.crawler;
import com.fasterxml.jackson.databind.JavaType;
import com.iqudoo.framework.tape.modules.utils.JsonUtil;
import com.iqudoo.framework.tape.modules.utils.ReflectUtil;
import java.lang.reflect.Type;
public abstract class CrawlerLoader<D, T> {
public JavaType getJavaType() {
try {
Type type = ReflectUtil.getType(getClass(), 0);
return JsonUtil.constructJavaType(type);
} catch (Throwable ignored) {
}
return null;
}
public abstract D onLoad(CrawlerKey crawlerKey, T params) throws Throwable;
}

View File

@@ -0,0 +1,109 @@
package com.iqudoo.framework.tape.modules.crud;
import com.iqudoo.framework.tape.modules.utils.ReflectUtil;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("SizeReplaceableByIsEmpty")
public class JoinUtil {
public static abstract class JoinLoader<T, P, D> {
public abstract List<D> selectJoinData(List<T> targetList, P joinParam) throws Throwable;
public abstract boolean checkJoin(T target, D data) throws Throwable;
public abstract void onJoined(T target, D data) throws Throwable;
public void onDefault(T target, P joinParam) throws Throwable {
}
}
public static abstract class JoinDefaultLoader<T, P> {
public void onDefault(T target, P joinParam) throws Throwable {
}
}
public static abstract class JoinListLoader<T, P, D> {
public abstract List<D> selectJoinData(List<T> targetList, P joinParam) throws Throwable;
public abstract boolean checkJoin(T target, D data) throws Throwable;
public abstract void onJoined(T target, List<D> data) throws Throwable;
}
public static <T, D, P> void joinOne(P joinParam, List<T> targetList, JoinLoader<T, P, D> loader) {
try {
if (targetList == null || targetList.size() == 0) {
return;
}
List<D> selectList = loader.selectJoinData(targetList, joinParam);
for (T target : targetList) {
boolean joinedFlag = false;
if (selectList != null && selectList.size() > 0) {
for (D data : selectList) {
try {
if (loader.checkJoin(target, data)) {
loader.onJoined(target, data);
joinedFlag = true;
}
} catch (Throwable ignored) {
}
}
}
if (!joinedFlag) {
loader.onDefault(target, joinParam);
}
}
} catch (Throwable ignored) {
}
}
public static <T, D, P> void joinDefault(P joinParam, List<T> targetList, JoinDefaultLoader<T, P> loader) {
try {
if (targetList == null || targetList.size() == 0) {
return;
}
for (T target : targetList) {
loader.onDefault(target, joinParam);
}
} catch (Throwable ignored) {
}
}
public static <T, D, P> void joinList(P joinParam, List<T> targetList, JoinListLoader<T, P, D> loader) {
try {
if (targetList == null || targetList.size() == 0) {
return;
}
List<D> selectList = loader.selectJoinData(targetList, joinParam);
if (selectList != null && selectList.size() > 0) {
for (T target : targetList) {
List<D> joinedList = new ArrayList<>();
for (D data : selectList) {
try {
if (loader.checkJoin(target, data)) {
joinedList.add(data);
}
} catch (Throwable ignored) {
}
}
loader.onJoined(target, joinedList);
}
}
} catch (Throwable ignored) {
}
}
}

View File

@@ -0,0 +1,177 @@
package com.iqudoo.framework.tape.modules.crud;
import com.iqudoo.framework.tape.base.docs.annotation.ApiDocFieldConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed", "DuplicatedCode"})
public class PageData<T> {
public interface FilterHandler<T> {
boolean filter(T t) throws Throwable;
}
public interface DistinctHandler<T> {
String distinctKey(T t);
}
public interface MapHandler<T, D> {
D map(T t) throws Throwable;
}
/**
* 页码
**/
@ApiDocFieldConfiguration(desc = "分页页码")
private int page;
/**
* 每页数量
**/
@ApiDocFieldConfiguration(desc = "每页数量")
private int size;
/**
* 结果数量
**/
@ApiDocFieldConfiguration(desc = "列表数量")
private long count;
/**
* 总数量
**/
@ApiDocFieldConfiguration(desc = "总计数量")
private long total;
/**
* 分页游标
**/
@ApiDocFieldConfiguration(desc = "分页游标")
private long cursor;
/**
* 结果列表
**/
@ApiDocFieldConfiguration(desc = "数据列表", parametricIndex = {0})
private List<T> list;
public PageData() {
}
public <M> PageData<M> map(MapHandler<T, M> handler) throws Throwable {
if (handler != null) {
PageData<M> mPagination = new PageData<>();
mPagination.list = new ArrayList<>();
for (T t : this.list) {
mPagination.list.add(handler.map(t));
}
mPagination.page = this.page;
mPagination.size = this.size;
mPagination.count = this.count;
mPagination.total = Math.max(this.total, mPagination.count);
mPagination.cursor = this.cursor;
return mPagination;
}
return null;
}
public PageData<T> filter(FilterHandler<T> handler) throws Throwable {
if (handler != null) {
PageData<T> mPagination = new PageData<>();
mPagination.list = new ArrayList<>();
for (T t : this.list) {
if (handler.filter(t)) {
mPagination.list.add(t);
}
}
mPagination.page = this.page;
mPagination.size = this.size;
mPagination.count = mPagination.list.size();
mPagination.total = Math.max(this.total, mPagination.count);
mPagination.cursor = this.cursor;
return mPagination;
}
return null;
}
public PageData<T> distinct(DistinctHandler<T> handler) throws Throwable {
if (handler != null) {
PageData<T> mPagination = new PageData<>();
mPagination.list = new ArrayList<>();
List<String> distinctKeyList = new ArrayList<>();
for (T t : this.list) {
String key = handler.distinctKey(t);
if (!distinctKeyList.contains(key)) {
distinctKeyList.add(key);
mPagination.list.add(t);
}
}
mPagination.page = this.page;
mPagination.size = this.size;
mPagination.count = mPagination.list.size();
mPagination.total = Math.max(this.total, mPagination.count);
mPagination.cursor = this.cursor;
return mPagination;
}
return null;
}
public <D, P> PageData<T> joinOne(P joinParam, JoinUtil.JoinLoader<T, P, D> loader) {
JoinUtil.joinOne(joinParam, getList(), loader);
return this;
}
public <D, P> PageData<T> joinList(P joinParam, JoinUtil.JoinListLoader<T, P, D> loader) {
JoinUtil.joinList(joinParam, getList(), loader);
return this;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public long getCursor() {
return cursor;
}
public void setCursor(long cursor) {
this.cursor = cursor;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}

View File

@@ -0,0 +1,58 @@
package com.iqudoo.framework.tape.modules.crud;
import java.util.List;
@SuppressWarnings({"DuplicatedCode", "unused"})
public class PageUtil {
public static <D> PageData<D> pagination(List<D> list, Integer totalCount, Integer pageNum, Integer pageSize) {
return createPagination(list,
totalCount == null ? 0L : totalCount,
pageNum == null ? 0 : pageNum,
pageSize == null ? 0 : pageSize,
0L
);
}
public static <D> PageData<D> pagination(List<D> list, Long totalCount, Integer pageNum, Integer pageSize) {
return createPagination(list,
totalCount == null ? 0L : totalCount,
pageNum == null ? 0 : pageNum,
pageSize == null ? 0 : pageSize,
0L
);
}
public static <D> PageData<D> pagination(List<D> list, Integer totalCount, Integer pageNum, Integer pageSize, Long pageCursor) {
return createPagination(list,
totalCount == null ? 0L : totalCount,
pageNum == null ? 0 : pageNum,
pageSize == null ? 0 : pageSize,
pageCursor == null ? 0L : pageCursor
);
}
public static <D> PageData<D> pagination(List<D> list, Long totalCount, Integer pageNum, Integer pageSize, Long pageCursor) {
return createPagination(list,
totalCount == null ? 0L : totalCount,
pageNum == null ? 0 : pageNum,
pageSize == null ? 0 : pageSize,
pageCursor == null ? 0L : pageCursor
);
}
private static <D> PageData<D> createPagination(List<D> list, long totalCount, int pageNum, int pageSize, long pageCursor) {
PageData<D> pageData = new PageData<>();
if (list != null) {
pageData.setList(list);
pageData.setPage(pageNum);
pageData.setSize(pageSize);
pageData.setCount(list.size());
pageData.setTotal(Math.max(totalCount, list.size()));
pageData.setCursor(pageCursor);
}
return pageData;
}
}

View File

@@ -0,0 +1,9 @@
package com.iqudoo.framework.tape.modules.excel;
public class BaseEasyExcelIgnoreError extends Throwable {
public BaseEasyExcelIgnoreError(String message) {
super(message);
}
}

View File

@@ -0,0 +1,125 @@
package com.iqudoo.framework.tape.modules.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.iqudoo.framework.tape.modules.utils.ThrowableUtil;
import lombok.Data;
import java.util.*;
public abstract class BaseEasyExcelReadAbstractListener<T, P> extends AnalysisEventListener<T> {
@Data
public static class ErrorInfo {
String message;
String errorMsg;
int errorCount;
}
public enum Result {
IGNORE(0, "忽略处理"),
SUCCESS(1, "导入成功");
private final int value;
private final String desc;
Result(int value, String desc) {
this.value = value;
this.desc = desc;
}
public int getValue() {
return value;
}
public String getDesc() {
return desc;
}
}
private final P param;
public BaseEasyExcelReadAbstractListener(P param) {
this.param = param;
}
private long startTime = 0;
private Integer totalCount = 0;
private Integer ignoreCount = 0;
private Integer successCount = 0;
private Integer failureCount = 0;
private final List<T> ignoreList = new ArrayList<>();
private final Map<String, ErrorInfo> errorMap = new HashMap<>();
private final BaseEasyExcelReadResponse response = new BaseEasyExcelReadResponse();
public BaseEasyExcelReadResponse getResponse() {
return response;
}
public abstract Result handleInvoke(T model, P param) throws Throwable;
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
if (startTime > 0) {
response.setUseTime(new Date().getTime() - startTime);
} else {
response.setUseTime(0);
}
response.setTotalCount(totalCount);
response.setSuccessCount(successCount);
response.setIgnoreCount(ignoreCount);
response.setFailureCount(failureCount);
response.setIgnoreList(ignoreList);
List<BaseEasyExcelReadResponse.ErrorInfo> errorInfos = new ArrayList<>();
for (Map.Entry<String, ErrorInfo> entry : errorMap.entrySet()) {
BaseEasyExcelReadResponse.ErrorInfo errorInfo
= new BaseEasyExcelReadResponse.ErrorInfo();
errorInfo.setMessage(entry.getValue().getMessage());
errorInfo.setErrorMsg(entry.getValue().getErrorMsg());
errorInfo.setErrorCount(entry.getValue().getErrorCount());
errorInfos.add(errorInfo);
}
response.setErrors(errorInfos);
}
@Override
public void invoke(T t, AnalysisContext analysisContext) {
if (startTime == 0) {
startTime = new Date().getTime();
}
try {
totalCount += 1;
Result result = handleInvoke(t, this.param);
if (result == Result.SUCCESS) {
successCount += 1;
} else {
ignoreCount += 1;
ignoreList.add(t);
}
} catch (Throwable throwable) {
if (throwable instanceof BaseEasyExcelIgnoreError) {
ignoreCount += 1;
ignoreList.add(t);
} else {
failureCount += 1;
}
String message = throwable.getMessage();
if (errorMap.containsKey(message)) {
ErrorInfo errorInfo = errorMap.get(message);
errorInfo.setErrorCount(errorInfo.getErrorCount() + 1);
errorMap.put(message, errorInfo);
} else {
ErrorInfo errorInfo = new ErrorInfo();
errorInfo.setMessage(message);
errorInfo.setErrorMsg(ThrowableUtil.getStackTraceToString(throwable));
errorInfo.setErrorCount(1);
errorMap.put(message, errorInfo);
}
}
}
}

View File

@@ -0,0 +1,27 @@
package com.iqudoo.framework.tape.modules.excel;
import lombok.Data;
import java.util.List;
@Data
public class BaseEasyExcelReadResponse {
private long useTime;
private Integer totalCount;
private Integer successCount;
private Integer failureCount;
private Integer ignoreCount;
private List<ErrorInfo> errors;
private List<?> ignoreList;
@Data
public static class ErrorInfo {
String message;
String errorMsg;
Integer errorCount;
}
}

View File

@@ -0,0 +1,23 @@
package com.iqudoo.framework.tape.modules.excel;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import java.util.List;
public class BaseExcelCellWriteHandler extends AbstractColumnWidthStyleStrategy {
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
Sheet sheet = writeSheetHolder.getSheet();
int columnWidth = sheet.getColumnWidth(cell.getColumnIndex());
if (columnWidth == 0) {
sheet.setColumnWidth(cell.getColumnIndex(), 5000);
}
}
}

View File

@@ -0,0 +1,76 @@
package com.iqudoo.framework.tape.modules.excel;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.Sheet;
public class BaseExcelHeaderFooterHandler implements SheetWriteHandler {
private final String headerCenter;
private final String headerLeft;
private final String headerRight;
private final String footerCenter;
private final String footerLeft;
private final String footerRight;
/**
* 构造函数,传入页眉页脚内容
*/
public BaseExcelHeaderFooterHandler(String headerCenter, String footerCenter) {
this.headerCenter = headerCenter;
this.headerLeft = "";
this.headerRight = "";
this.footerCenter = footerCenter;
this.footerLeft = "";
this.footerRight = "";
}
/**
* 构造函数,传入页眉页脚内容
*/
public BaseExcelHeaderFooterHandler(String headerCenter, String headerLeft, String headerRight,
String footerCenter, String footerLeft, String footerRight) {
this.headerCenter = headerCenter;
this.headerLeft = headerLeft;
this.headerRight = headerRight;
this.footerCenter = footerCenter;
this.footerLeft = footerLeft;
this.footerRight = footerRight;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
// 无需操作
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
Header header = sheet.getHeader();
if (headerLeft != null) {
header.setLeft(headerLeft);
}
if (headerCenter != null) {
header.setCenter(headerCenter);
}
if (headerRight != null) {
header.setRight(headerRight);
}
// 设置页脚
Footer footer = sheet.getFooter();
if (footerLeft != null) {
footer.setLeft(footerLeft);
}
if (footerCenter != null) {
footer.setCenter(footerCenter);
}
if (footerRight != null) {
footer.setRight(footerRight);
}
sheet.setFitToPage(true);
sheet.setHorizontallyCenter(true);
}
}

View File

@@ -0,0 +1,75 @@
package com.iqudoo.framework.tape.modules.excel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
@SuppressWarnings("unused")
public class EasyExcelUtil {
public static <T, P> BaseEasyExcelReadResponse readSheet(InputStream inputStream, Class<T> clazz, BaseEasyExcelReadAbstractListener<T, P> excelListener) throws IOException {
EasyExcel.read(inputStream, clazz, excelListener).sheet(0).doRead();
return excelListener.getResponse();
}
public static <T, P> BaseEasyExcelReadResponse readSheet(InputStream inputStream, String sheetName, Class<T> clazz, BaseEasyExcelReadAbstractListener<T, P> excelListener) throws IOException {
EasyExcel.read(inputStream, clazz, excelListener).sheet(sheetName).doRead();
return excelListener.getResponse();
}
public static <T, P> BaseEasyExcelReadResponse readAll(InputStream inputStream, Class<T> clazz, BaseEasyExcelReadAbstractListener<T, P> excelListener) throws IOException {
EasyExcel.read(inputStream, clazz, excelListener).doReadAll();
return excelListener.getResponse();
}
@SuppressWarnings("UnusedAssignment")
public static <T> void exportExcel(HttpServletResponse servletResponse, Class<T> clazz, List<T> dataList, String fileName) throws Throwable {
// 输出流
OutputStream outputStream = null;
try {
outputStream = servletResponse.getOutputStream();
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
headWriteCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
HorizontalCellStyleStrategy horizontalCellStyleStrategy
= new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
WriteSheet listSheet = EasyExcel.writerSheet("Sheet1")
.head(clazz)
.registerWriteHandler(new BaseExcelCellWriteHandler())
.registerWriteHandler(horizontalCellStyleStrategy)
.build();
excelWriter.write(dataList, listSheet);
servletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
servletResponse.setContentType("application/vnd.ms-excel");
servletResponse.setCharacterEncoding("utf-8");
excelWriter.finish();
outputStream.flush();
outputStream.close();
} finally {
if (outputStream != null) {
outputStream.close();
outputStream = null;
}
}
}
}

View File

@@ -0,0 +1,79 @@
package com.iqudoo.framework.tape.modules.fileManager;
@SuppressWarnings({"unused", "LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class FileConfig {
private String type;
private String ossEndpoint = "";
private String ossAccessKeyId = "";
private String ossAccessKeySecret = "";
private String ossBucketName = "";
private String localRootPath = "";
private String domainProtocol = "";
private String domainName = "";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getOssEndpoint() {
return ossEndpoint;
}
public void setOssEndpoint(String ossEndpoint) {
this.ossEndpoint = ossEndpoint;
}
public String getOssAccessKeyId() {
return ossAccessKeyId;
}
public void setOssAccessKeyId(String ossAccessKeyId) {
this.ossAccessKeyId = ossAccessKeyId;
}
public String getOssAccessKeySecret() {
return ossAccessKeySecret;
}
public void setOssAccessKeySecret(String ossAccessKeySecret) {
this.ossAccessKeySecret = ossAccessKeySecret;
}
public String getOssBucketName() {
return ossBucketName;
}
public void setOssBucketName(String ossBucketName) {
this.ossBucketName = ossBucketName;
}
public String getLocalRootPath() {
return localRootPath;
}
public void setLocalRootPath(String localRootPath) {
this.localRootPath = localRootPath;
}
public String getDomainProtocol() {
return domainProtocol;
}
public void setDomainProtocol(String domainProtocol) {
this.domainProtocol = domainProtocol;
}
public String getDomainName() {
return domainName;
}
public void setDomainName(String domainName) {
this.domainName = domainName;
}
}

View File

@@ -0,0 +1,42 @@
package com.iqudoo.framework.tape.modules.fileManager;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class FileInfo {
private String fileType;
private String fileName;
private long fileSize;
private long lastModified;
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public long getLastModified() {
return lastModified;
}
public void setLastModified(long lastModified) {
this.lastModified = lastModified;
}
}

View File

@@ -0,0 +1,66 @@
package com.iqudoo.framework.tape.modules.proto;
import java.io.Serializable;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class Proto<D> implements Serializable {
public static final int SUCCESS_CODE = 0;
public static final int FAILURE_CODE = -1;
private int code;
private long useTime;
private Throwable err;
private D data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public long getUseTime() {
return useTime;
}
public void setUseTime(long useTime) {
this.useTime = useTime;
}
public Throwable getErr() {
return err;
}
public void setErr(Throwable err) {
this.err = err;
}
public D getData() {
return data;
}
public void setData(D data) {
this.data = data;
}
public boolean isSuccess() {
return code == SUCCESS_CODE;
}
public static <D> Proto<D> resolve(D data) {
return resolve(data, 0L);
}
public static <D> Proto<D> resolve(D data, long useTime) {
Proto<D> proto = new Proto<>();
proto.setErr(null);
proto.setCode(SUCCESS_CODE);
proto.setData(data);
proto.setUseTime(useTime);
return proto;
}
}

View File

@@ -0,0 +1,80 @@
package com.iqudoo.framework.tape.modules.proto;
import com.iqudoo.framework.tape.Tape;
import com.iqudoo.framework.tape.modules.IModuleLock;
import com.iqudoo.framework.tape.modules.utils.ReflectUtil;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public abstract class ProtoLoader<T, P> {
/**
* 锁键缓存
*/
private final List<String> LOCK_KEYS = new ArrayList<>();
/**
* 验证并设置锁,会在执行结束后自动释放,不存在锁时,才会通过锁验证并设置新的锁
*
* @param key 锁键
* @throws Throwable 锁异常
*/
public void lock(String key) throws Throwable {
if (key == null || key.isEmpty()) {
return;
}
Tape.getService(IModuleLock.class)
.lock(key, 0);
this.LOCK_KEYS.add(key);
}
/**
* 验证并设置超时锁,超时锁不会自动释放,只有过了超时时间,才会通过锁验证并设置新的超时锁
*
* @param key 锁键
* @param timeoutMs 超时时间
* @throws Throwable 锁异常
*/
public void timeoutLock(String key, long timeoutMs) throws Throwable {
if (key == null || key.isEmpty()) {
return;
}
if (timeoutMs <= 0) {
return;
}
Tape.getService(IModuleLock.class)
.lock(key, timeoutMs);
}
/**
* 强制移除锁
*
* @param key 锁键
* @throws Throwable 锁异常
*/
public void removeLock(String key) throws Throwable {
if (key == null || key.isEmpty()) {
return;
}
// only can unlock self add lock
Tape.getService(IModuleLock.class)
.unlock(key);
this.LOCK_KEYS.remove(key);
}
public abstract T onLoad(P loadParam) throws Throwable;
public void handleResponseAfter(T t, P params) throws Throwable {
}
protected Type getType(int index) {
return ReflectUtil.getType(getClass(), index);
}
public List<String> getLockKeys() {
return LOCK_KEYS;
}
}

View File

@@ -0,0 +1,78 @@
package com.iqudoo.framework.tape.modules.proto;
import com.iqudoo.framework.tape.Tape;
import com.iqudoo.framework.tape.base.action.bean.ActionParam;
import com.iqudoo.framework.tape.base.action.bean.ActionState;
import com.iqudoo.framework.tape.modules.IModuleLock;
import org.slf4j.Logger;
import java.util.Date;
public class ProtoUtil {
private static final Logger LOGGER = Tape.getLogger(ProtoUtil.class);
public static <T, P> Proto<T> load(P params, ProtoLoader<T, P> loader) throws Throwable {
if (params instanceof ActionParam) {
return load(params, loader, ((ActionParam) params).isThrowThrowable());
}
return load(params, loader, false);
}
public static <T, P> Proto<T> load(P params, ProtoLoader<T, P> loader, boolean throwThrowable) throws Throwable {
String actionString = null;
if (params instanceof ActionParam actionParam) {
if (actionParam.getActionRequest() != null && actionParam.getActionRequest().getActionParameter() != null) {
actionString = actionParam.getActionRequest().getActionParameter().getAction();
}
}
if (actionString != null) {
actionString = "\n\t|-> action: " + actionString;
} else {
actionString = "";
}
Proto<T> proto = new Proto<>();
long startTime = new Date().getTime();
try {
T data = loader.onLoad(params);
loader.handleResponseAfter(data, params);
proto.setCode(Proto.SUCCESS_CODE);
proto.setData(data);
} catch (Throwable e) {
if (throwThrowable) {
throw e;
}
if (!(e instanceof ActionState)
&& !(e instanceof IllegalArgumentException)) {
LOGGER.error("[PROTO] load error" +
actionString +
"\n\t|-> param type: {}" +
"\n\t|-----------------------------------", params, e);
}
proto.setCode(Proto.FAILURE_CODE);
proto.setErr(e);
} finally {
// release lock
try {
IModuleLock moduleLock = Tape.getService(IModuleLock.class);
for (String key : loader.getLockKeys()) {
moduleLock.unlock(key);
}
loader.getLockKeys().clear();
} catch (Throwable ignored) {
}
}
long useTime = new Date().getTime() - startTime;
if (useTime > 2000) {
LOGGER.error("[PROTO] handle proto load use long time" +
actionString +
"\n\t|-> use time: " + useTime + "ms" +
"\n\t|-> return type: " + loader.getType(0) +
"\n\t|-> param type: " + loader.getType(1) +
"\n\t|-----------------------------------");
}
proto.setUseTime(useTime);
return proto;
}
}

View File

@@ -0,0 +1,31 @@
package com.iqudoo.framework.tape.modules.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class LongListToStringListSerializer extends JsonSerializer<List<Long>> {
@Override
public void serialize(List<Long> longs, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (null != jsonGenerator) {
// try to parse as JSONArray
List<String> stringList = longs.stream()
.map(new Function<Long, String>() {
@Override
public String apply(Long aLong) {
return aLong + "";
}
})
.collect(Collectors.toList());
jsonGenerator.writeObject(stringList);
}
}
}

View File

@@ -0,0 +1,29 @@
package com.iqudoo.framework.tape.modules.serializer;
import com.alibaba.fastjson.JSONArray;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class ToJsonArraySerializer extends JsonSerializer<String> {
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (null != jsonGenerator) {
// try to parse as JSONArray
JSONArray jsonArray = null;
try {
jsonArray = JSONArray.parseArray(s);
} catch (Throwable ignored) {
}
if (jsonArray != null) {
jsonGenerator.writeObject(jsonArray);
} else {
jsonGenerator.writeObject(new JSONArray());
}
}
}
}

View File

@@ -0,0 +1,30 @@
package com.iqudoo.framework.tape.modules.serializer;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class ToJsonObjSerializer extends JsonSerializer<String> {
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (null != jsonGenerator) {
// try to parse as JSONObject
JSONObject jsonObject = null;
try {
jsonObject = JSON.parseObject(s);
} catch (Throwable ignored) {
}
if (null != jsonObject) {
jsonGenerator.writeObject(jsonObject);
} else {
jsonGenerator.writeObject(new JSONObject());
}
}
}
}

View File

@@ -0,0 +1,37 @@
package com.iqudoo.framework.tape.modules.serializer;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class ToJsonSerializer extends JsonSerializer<String> {
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (null != jsonGenerator) {
// try to parse as JSONObject
JSONObject jsonObject = null;
try {
jsonObject = JSON.parseObject(s);
} catch (Throwable ignored) {
}
if (null != jsonObject) {
jsonGenerator.writeObject(jsonObject);
} else {
// try to parse as JSONArray
JSONArray jsonArray = null;
try {
jsonArray = JSONArray.parseArray(s);
} catch (Throwable ignored) {
}
jsonGenerator.writeObject(jsonArray);
}
}
}
}

View File

@@ -0,0 +1,33 @@
package com.iqudoo.framework.tape.modules.session;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class GetSessionParam {
private Long userGuid;
private String userGroup;
private String originPlatform;
public Long getUserGuid() {
return userGuid;
}
public void setUserGuid(Long userGuid) {
this.userGuid = userGuid;
}
public String getUserGroup() {
return userGroup;
}
public void setUserGroup(String userGroup) {
this.userGroup = userGroup;
}
public String getOriginPlatform() {
return originPlatform;
}
public void setOriginPlatform(String originPlatform) {
this.originPlatform = originPlatform;
}
}

View File

@@ -0,0 +1,61 @@
package com.iqudoo.framework.tape.modules.session;
@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"})
public class RefreshSessionParam {
private Long userGuid;
private String userGroup;
private String originPlatform;
private String originDomain;
private String originIp;
private boolean refresh;
public Long getUserGuid() {
return userGuid;
}
public void setUserGuid(Long userGuid) {
this.userGuid = userGuid;
}
public String getUserGroup() {
return userGroup;
}
public void setUserGroup(String userGroup) {
this.userGroup = userGroup;
}
public String getOriginPlatform() {
return originPlatform;
}
public void setOriginPlatform(String originPlatform) {
this.originPlatform = originPlatform;
}
public String getOriginDomain() {
return originDomain;
}
public void setOriginDomain(String originDomain) {
this.originDomain = originDomain;
}
public String getOriginIp() {
return originIp;
}
public void setOriginIp(String originIp) {
this.originIp = originIp;
}
public boolean isRefresh() {
return refresh;
}
public void setRefresh(boolean refresh) {
this.refresh = refresh;
}
}

View File

@@ -0,0 +1,940 @@
package com.iqudoo.framework.tape.modules.utils;
import java.util.*;
import java.util.regex.Pattern;
@SuppressWarnings({"DuplicatedCode", "unused"})
public class AddressUtil {
private static final List<String> PROVINCE_KEYWORDS = Arrays.asList("", "自治区", "特别行政区");
private static final List<String> MUNICIPALITIES = Arrays.asList("北京", "上海", "天津", "重庆");
private static final List<String> CITY_KEYWORDS = Arrays.asList("", "自治州", "地区", "");
private static final List<String> DISTRICT_KEYWORDS = Arrays.asList("", "", "", "自治县", "自治旗");
private static final List<String> SPECIAL_DISTRICT = Arrays.asList("新区", "开发区", "高新区", "工业园区", "示范区");
private static final Set<String> CHINA_PROVINCES = new HashSet<>(Arrays.asList(
"北京市", "上海市", "天津市", "重庆市",
"黑龙江省", "吉林省", "辽宁省", "河北省", "河南省", "山东省", "山西省",
"陕西省", "甘肃省", "青海省", "四川省", "贵州省", "云南省", "湖南省",
"湖北省", "江西省", "安徽省", "江苏省", "浙江省", "福建省", "广东省",
"海南省",
"内蒙古自治区", "广西壮族自治区", "西藏自治区", "宁夏回族自治区", "新疆维吾尔自治区",
"香港特别行政区", "澳门特别行政区"
));
// 完整的省份简称/全称列表(用于清理城市名中的省份前缀)
private static final List<String> PROVINCE_PREFIXES = Arrays.asList(
"北京市", "上海市", "天津市", "重庆市",
"黑龙江省", "吉林省", "辽宁省", "河北省", "河南省", "山东省", "山西省",
"陕西省", "甘肃省", "青海省", "四川省", "贵州省", "云南省", "湖南省",
"湖北省", "江西省", "安徽省", "江苏省", "浙江省", "福建省", "广东省",
"海南省", "台湾省",
"内蒙古自治区", "广西壮族自治区", "西藏自治区", "宁夏回族自治区", "新疆维吾尔自治区",
"香港特别行政区", "澳门特别行政区",
"黑龙江", "吉林", "辽宁", "河北", "河南", "山东", "山西",
"陕西", "甘肃", "青海", "四川", "贵州", "云南", "湖南",
"湖北", "江西", "安徽", "江苏", "浙江", "福建", "广东", "海南",
"新疆", "西藏", "内蒙古", "广西", "宁夏", "香港", "澳门"
);
private static final Pattern CLEAN_PATTERN = Pattern.compile("^[^\\u4e00-\\u9fa5a-zA-Z0-9]+");
private static final Pattern SPACE_PATTERN = Pattern.compile("\\s+");
public static AddressInfo parse(String fullAddress) {
if (fullAddress == null || fullAddress.trim().isEmpty()) return null;
String address = preprocessAddress(fullAddress);
String province = null;
String city = null;
String district = null;
String detail = address;
try {
// 1. 解析省
province = parseProvince(address);
if (province != null) {
detail = address.substring(province.length());
}
// 2. 解析市
if (isMunicipality(province)) {
city = province.replace("", "");
} else if (detail != null && !detail.isEmpty()) {
String originalCity = parseCity(detail);
if (originalCity != null) {
// 安全清理省份前缀
String cleanCity = cleanProvincePrefix(originalCity);
int prefixLen = getProvincePrefixLength(originalCity);
detail = detail.substring(originalCity.length());
city = cleanCity;
}
}
// 3. 解析区/县(修复版:只取区县名,不包含前面的城市名)
if (detail != null && !detail.isEmpty()) {
district = parseDistrictV2(detail);
if (district != null) {
detail = detail.substring(district.length());
}
}
// 4. 补全省份
if (province == null && city != null) {
province = inferProvinceByCity(city);
}
} catch (Exception e) {
return new AddressInfo(province, city, district, fullAddress);
}
detail = cleanupDetail(detail);
return new AddressInfo(province, city, district, detail);
}
/**
* 清理城市名中的省份前缀
*/
private static String cleanProvincePrefix(String city) {
if (city == null) return null;
for (String prefix : PROVINCE_PREFIXES) {
if (city.startsWith(prefix)) {
String result = city.substring(prefix.length());
if (result.endsWith("") && result.length() > 1) {
return result;
}
}
}
return city;
}
/**
* 获取省份前缀的长度
*/
private static int getProvincePrefixLength(String city) {
if (city == null) return 0;
for (String prefix : PROVINCE_PREFIXES) {
if (city.startsWith(prefix)) {
return prefix.length();
}
}
return 0;
}
private static String preprocessAddress(String address) {
if (address == null) return null;
String res = convertFullWidthToHalfWidth(address);
res = SPACE_PATTERN.matcher(res).replaceAll("");
res = CLEAN_PATTERN.matcher(res).replaceFirst("");
return res.trim();
}
private static String convertFullWidthToHalfWidth(String input) {
char[] c = input.toCharArray();
for (int i = 0; i < c.length; i++) {
if (c[i] == ' ') c[i] = ' ';
else if (c[i] >= '' && c[i] <= '') c[i] -= 65248;
else if (c[i] >= '' && c[i] <= '') c[i] -= 65248;
else if (c[i] >= '' && c[i] <= '') c[i] -= 65248;
}
return new String(c);
}
private static String parseProvince(String address) {
for (String p : CHINA_PROVINCES) if (address.startsWith(p)) return p;
for (String m : MUNICIPALITIES) if (address.startsWith(m)) return m + "";
for (String k : PROVINCE_KEYWORDS) {
int idx = address.indexOf(k);
if (idx > 0 && idx < 15) return address.substring(0, idx + k.length());
}
return null;
}
private static String parseCity(String address) {
int min = Integer.MAX_VALUE;
String match = null;
for (String k : CITY_KEYWORDS) {
int idx = address.indexOf(k);
if (idx > 0 && idx < min) {
min = idx;
match = address.substring(0, idx + k.length());
}
}
return (match != null && match.length() <= 20) ? match : null;
}
/**
* 解析区县(修复版)
* 只提取区县名称,不包含前面的内容
*/
private static String parseDistrictV2(String address) {
if (address == null || address.isEmpty()) return null;
// 特殊区县级关键词
for (String key : SPECIAL_DISTRICT) {
int idx = address.indexOf(key);
if (idx > 0 && idx <= 10) {
// 向前查找区名的起始位置
int start = idx;
for (int i = idx - 1; i >= 0; i--) {
char ch = address.charAt(i);
if (ch < 0x4E00 || ch > 0x9FA5 || idx - i > 5) {
break;
}
start = i;
}
String district = address.substring(start, idx + key.length());
// 验证区名不包含"市"字
if (district.length() >= 2 && district.length() <= 12) {
if (!district.contains("") || district.equals("市区")) {
return district;
}
}
}
}
// 标准区县级关键词
for (String key : DISTRICT_KEYWORDS) {
int idx = address.indexOf(key);
if (idx > 0 && idx <= 8) {
// 向前查找区名的起始位置
int start = idx;
for (int i = idx - 1; i >= 0; i--) {
char ch = address.charAt(i);
if (ch < 0x4E00 || ch > 0x9FA5 || idx - i > 4) {
break;
}
start = i;
}
String district = address.substring(start, idx + key.length());
// 验证区名合理性
if (district.length() >= 2 && district.length() <= 8) {
if (!district.contains("") || district.equals("市区")) {
return district;
}
}
}
}
return null;
}
private static String inferProvinceByCity(String city) {
if (city == null) return null;
Map<String, String> map = buildFullCityMap();
// 直接匹配
String res = map.get(city);
if (res != null) return res;
// 去掉"市"字匹配
if (city.endsWith("")) {
res = map.get(city.substring(0, city.length() - 1));
return res;
}
return null;
}
private static Map<String, String> buildFullCityMap() {
Map<String, String> map = new HashMap<>();
// 直辖市
map.put("北京", "北京市");
map.put("北京市", "北京市");
map.put("上海", "上海市");
map.put("上海市", "上海市");
map.put("天津", "天津市");
map.put("天津市", "天津市");
map.put("重庆", "重庆市");
map.put("重庆市", "重庆市");
// 江苏
map.put("南京", "江苏省");
map.put("南京市", "江苏省");
map.put("苏州", "江苏省");
map.put("苏州市", "江苏省");
map.put("昆山", "江苏省");
map.put("昆山市", "江苏省");
map.put("无锡", "江苏省");
map.put("无锡市", "江苏省");
map.put("常州", "江苏省");
map.put("常州市", "江苏省");
map.put("镇江", "江苏省");
map.put("镇江市", "江苏省");
map.put("南通", "江苏省");
map.put("南通市", "江苏省");
map.put("扬州", "江苏省");
map.put("扬州市", "江苏省");
map.put("盐城", "江苏省");
map.put("盐城市", "江苏省");
map.put("徐州", "江苏省");
map.put("徐州市", "江苏省");
map.put("淮安", "江苏省");
map.put("淮安市", "江苏省");
map.put("连云港", "江苏省");
map.put("连云港市", "江苏省");
map.put("泰州", "江苏省");
map.put("泰州市", "江苏省");
map.put("宿迁", "江苏省");
map.put("宿迁市", "江苏省");
// 浙江
map.put("杭州", "浙江省");
map.put("杭州市", "浙江省");
map.put("宁波", "浙江省");
map.put("宁波市", "浙江省");
map.put("温州", "浙江省");
map.put("温州市", "浙江省");
map.put("嘉兴", "浙江省");
map.put("嘉兴市", "浙江省");
map.put("湖州", "浙江省");
map.put("湖州市", "浙江省");
map.put("绍兴", "浙江省");
map.put("绍兴市", "浙江省");
map.put("金华", "浙江省");
map.put("金华市", "浙江省");
map.put("衢州", "浙江省");
map.put("衢州市", "浙江省");
map.put("舟山", "浙江省");
map.put("舟山市", "浙江省");
map.put("台州", "浙江省");
map.put("台州市", "浙江省");
map.put("丽水", "浙江省");
map.put("丽水市", "浙江省");
// 广东
map.put("广州", "广东省");
map.put("广州市", "广东省");
map.put("深圳", "广东省");
map.put("深圳市", "广东省");
map.put("珠海", "广东省");
map.put("珠海市", "广东省");
map.put("汕头", "广东省");
map.put("汕头市", "广东省");
map.put("佛山", "广东省");
map.put("佛山市", "广东省");
map.put("韶关", "广东省");
map.put("韶关市", "广东省");
map.put("湛江", "广东省");
map.put("湛江市", "广东省");
map.put("肇庆", "广东省");
map.put("肇庆市", "广东省");
map.put("江门", "广东省");
map.put("江门市", "广东省");
map.put("茂名", "广东省");
map.put("茂名市", "广东省");
map.put("惠州", "广东省");
map.put("惠州市", "广东省");
map.put("梅州", "广东省");
map.put("梅州市", "广东省");
map.put("汕尾", "广东省");
map.put("汕尾市", "广东省");
map.put("河源", "广东省");
map.put("河源市", "广东省");
map.put("阳江", "广东省");
map.put("阳江市", "广东省");
map.put("清远", "广东省");
map.put("清远市", "广东省");
map.put("东莞", "广东省");
map.put("东莞市", "广东省");
map.put("中山", "广东省");
map.put("中山市", "广东省");
map.put("潮州", "广东省");
map.put("潮州市", "广东省");
map.put("揭阳", "广东省");
map.put("揭阳市", "广东省");
map.put("云浮", "广东省");
map.put("云浮市", "广东省");
// 四川
map.put("成都", "四川省");
map.put("成都市", "四川省");
map.put("自贡", "四川省");
map.put("自贡市", "四川省");
map.put("攀枝花", "四川省");
map.put("攀枝花市", "四川省");
map.put("泸州", "四川省");
map.put("泸州市", "四川省");
map.put("德阳", "四川省");
map.put("德阳市", "四川省");
map.put("绵阳", "四川省");
map.put("绵阳市", "四川省");
map.put("广元", "四川省");
map.put("广元市", "四川省");
map.put("遂宁", "四川省");
map.put("遂宁市", "四川省");
map.put("内江", "四川省");
map.put("内江市", "四川省");
map.put("乐山", "四川省");
map.put("乐山市", "四川省");
map.put("南充", "四川省");
map.put("南充市", "四川省");
map.put("眉山", "四川省");
map.put("眉山市", "四川省");
map.put("宜宾", "四川省");
map.put("宜宾市", "四川省");
map.put("广安", "四川省");
map.put("广安市", "四川省");
map.put("达州", "四川省");
map.put("达州市", "四川省");
map.put("雅安", "四川省");
map.put("雅安市", "四川省");
map.put("巴中", "四川省");
map.put("巴中市", "四川省");
map.put("资阳", "四川省");
map.put("资阳市", "四川省");
// 湖北
map.put("武汉", "湖北省");
map.put("武汉市", "湖北省");
map.put("黄石", "湖北省");
map.put("黄石市", "湖北省");
map.put("十堰", "湖北省");
map.put("十堰市", "湖北省");
map.put("宜昌", "湖北省");
map.put("宜昌市", "湖北省");
map.put("襄阳", "湖北省");
map.put("襄阳市", "湖北省");
map.put("鄂州", "湖北省");
map.put("鄂州市", "湖北省");
map.put("荆门", "湖北省");
map.put("荆门市", "湖北省");
map.put("孝感", "湖北省");
map.put("孝感市", "湖北省");
map.put("荆州", "湖北省");
map.put("荆州市", "湖北省");
map.put("黄冈", "湖北省");
map.put("黄冈市", "湖北省");
map.put("咸宁", "湖北省");
map.put("咸宁市", "湖北省");
map.put("随州", "湖北省");
map.put("随州市", "湖北省");
// 湖南
map.put("长沙", "湖南省");
map.put("长沙市", "湖南省");
map.put("株洲", "湖南省");
map.put("株洲市", "湖南省");
map.put("湘潭", "湖南省");
map.put("湘潭市", "湖南省");
map.put("衡阳", "湖南省");
map.put("衡阳市", "湖南省");
map.put("邵阳", "湖南省");
map.put("邵阳市", "湖南省");
map.put("岳阳", "湖南省");
map.put("岳阳市", "湖南省");
map.put("常德", "湖南省");
map.put("常德市", "湖南省");
map.put("张家界", "湖南省");
map.put("张家界市", "湖南省");
map.put("益阳", "湖南省");
map.put("益阳市", "湖南省");
map.put("郴州", "湖南省");
map.put("郴州市", "湖南省");
map.put("永州", "湖南省");
map.put("永州市", "湖南省");
map.put("怀化", "湖南省");
map.put("怀化市", "湖南省");
map.put("娄底", "湖南省");
map.put("娄底市", "湖南省");
// 福建
map.put("福州", "福建省");
map.put("福州市", "福建省");
map.put("厦门", "福建省");
map.put("厦门市", "福建省");
map.put("莆田", "福建省");
map.put("莆田市", "福建省");
map.put("三明", "福建省");
map.put("三明市", "福建省");
map.put("泉州", "福建省");
map.put("泉州市", "福建省");
map.put("漳州", "福建省");
map.put("漳州市", "福建省");
map.put("南平", "福建省");
map.put("南平市", "福建省");
map.put("龙岩", "福建省");
map.put("龙岩市", "福建省");
map.put("宁德", "福建省");
map.put("宁德市", "福建省");
// 安徽
map.put("合肥", "安徽省");
map.put("合肥市", "安徽省");
map.put("芜湖", "安徽省");
map.put("芜湖市", "安徽省");
map.put("蚌埠", "安徽省");
map.put("蚌埠市", "安徽省");
map.put("淮南", "安徽省");
map.put("淮南市", "安徽省");
map.put("马鞍山", "安徽省");
map.put("马鞍山市", "安徽省");
map.put("淮北", "安徽省");
map.put("淮北市", "安徽省");
map.put("铜陵", "安徽省");
map.put("铜陵市", "安徽省");
map.put("安庆", "安徽省");
map.put("安庆市", "安徽省");
map.put("黄山", "安徽省");
map.put("黄山市", "安徽省");
map.put("滁州", "安徽省");
map.put("滁州市", "安徽省");
map.put("阜阳", "安徽省");
map.put("阜阳市", "安徽省");
map.put("宿州", "安徽省");
map.put("宿州市", "安徽省");
map.put("六安", "安徽省");
map.put("六安市", "安徽省");
map.put("亳州", "安徽省");
map.put("亳州市", "安徽省");
map.put("池州", "安徽省");
map.put("池州市", "安徽省");
map.put("宣城", "安徽省");
map.put("宣城市", "安徽省");
// 江西
map.put("南昌", "江西省");
map.put("南昌市", "江西省");
map.put("景德镇", "江西省");
map.put("景德镇市", "江西省");
map.put("萍乡", "江西省");
map.put("萍乡市", "江西省");
map.put("九江", "江西省");
map.put("九江市", "江西省");
map.put("新余", "江西省");
map.put("新余市", "江西省");
map.put("鹰潭", "江西省");
map.put("鹰潭市", "江西省");
map.put("赣州", "江西省");
map.put("赣州市", "江西省");
map.put("吉安", "江西省");
map.put("吉安市", "江西省");
map.put("宜春", "江西省");
map.put("宜春市", "江西省");
map.put("抚州", "江西省");
map.put("抚州市", "江西省");
map.put("上饶", "江西省");
map.put("上饶市", "江西省");
// 山东
map.put("济南", "山东省");
map.put("济南市", "山东省");
map.put("青岛", "山东省");
map.put("青岛市", "山东省");
map.put("淄博", "山东省");
map.put("淄博市", "山东省");
map.put("枣庄", "山东省");
map.put("枣庄市", "山东省");
map.put("东营", "山东省");
map.put("东营市", "山东省");
map.put("烟台", "山东省");
map.put("烟台市", "山东省");
map.put("潍坊", "山东省");
map.put("潍坊市", "山东省");
map.put("济宁", "山东省");
map.put("济宁市", "山东省");
map.put("泰安", "山东省");
map.put("泰安市", "山东省");
map.put("威海", "山东省");
map.put("威海市", "山东省");
map.put("日照", "山东省");
map.put("日照市", "山东省");
map.put("临沂", "山东省");
map.put("临沂市", "山东省");
map.put("德州", "山东省");
map.put("德州市", "山东省");
map.put("聊城", "山东省");
map.put("聊城市", "山东省");
map.put("滨州", "山东省");
map.put("滨州市", "山东省");
map.put("菏泽", "山东省");
map.put("菏泽市", "山东省");
// 西藏
map.put("拉萨", "西藏自治区");
map.put("拉萨市", "西藏自治区");
map.put("山南", "西藏自治区");
map.put("山南市", "西藏自治区");
map.put("日喀则", "西藏自治区");
map.put("日喀则市", "西藏自治区");
map.put("昌都", "西藏自治区");
map.put("昌都市", "西藏自治区");
map.put("林芝", "西藏自治区");
map.put("林芝市", "西藏自治区");
map.put("那曲", "西藏自治区");
map.put("那曲市", "西藏自治区");
// 新疆
map.put("乌鲁木齐", "新疆维吾尔自治区");
map.put("乌鲁木齐市", "新疆维吾尔自治区");
map.put("克拉玛依", "新疆维吾尔自治区");
map.put("克拉玛依市", "新疆维吾尔自治区");
map.put("吐鲁番", "新疆维吾尔自治区");
map.put("吐鲁番市", "新疆维吾尔自治区");
map.put("哈密", "新疆维吾尔自治区");
map.put("哈密市", "新疆维吾尔自治区");
// 广西
map.put("南宁", "广西壮族自治区");
map.put("南宁市", "广西壮族自治区");
map.put("柳州", "广西壮族自治区");
map.put("柳州市", "广西壮族自治区");
map.put("桂林", "广西壮族自治区");
map.put("桂林市", "广西壮族自治区");
map.put("梧州", "广西壮族自治区");
map.put("梧州市", "广西壮族自治区");
map.put("北海", "广西壮族自治区");
map.put("北海市", "广西壮族自治区");
map.put("防城港", "广西壮族自治区");
map.put("防城港市", "广西壮族自治区");
map.put("钦州", "广西壮族自治区");
map.put("钦州市", "广西壮族自治区");
map.put("贵港", "广西壮族自治区");
map.put("贵港市", "广西壮族自治区");
map.put("玉林", "广西壮族自治区");
map.put("玉林市", "广西壮族自治区");
map.put("百色", "广西壮族自治区");
map.put("百色市", "广西壮族自治区");
map.put("贺州", "广西壮族自治区");
map.put("贺州市", "广西壮族自治区");
map.put("河池", "广西壮族自治区");
map.put("河池市", "广西壮族自治区");
map.put("来宾", "广西壮族自治区");
map.put("来宾市", "广西壮族自治区");
map.put("崇左", "广西壮族自治区");
map.put("崇左市", "广西壮族自治区");
// 内蒙古
map.put("呼和浩特", "内蒙古自治区");
map.put("呼和浩特市", "内蒙古自治区");
map.put("包头", "内蒙古自治区");
map.put("包头市", "内蒙古自治区");
map.put("乌海", "内蒙古自治区");
map.put("乌海市", "内蒙古自治区");
map.put("赤峰", "内蒙古自治区");
map.put("赤峰市", "内蒙古自治区");
map.put("通辽", "内蒙古自治区");
map.put("通辽市", "内蒙古自治区");
map.put("鄂尔多斯", "内蒙古自治区");
map.put("鄂尔多斯市", "内蒙古自治区");
map.put("呼伦贝尔", "内蒙古自治区");
map.put("呼伦贝尔市", "内蒙古自治区");
map.put("巴彦淖尔", "内蒙古自治区");
map.put("巴彦淖尔市", "内蒙古自治区");
map.put("乌兰察布", "内蒙古自治区");
map.put("乌兰察布市", "内蒙古自治区");
// 宁夏
map.put("银川", "宁夏回族自治区");
map.put("银川市", "宁夏回族自治区");
map.put("石嘴山", "宁夏回族自治区");
map.put("石嘴山市", "宁夏回族自治区");
map.put("吴忠", "宁夏回族自治区");
map.put("吴忠市", "宁夏回族自治区");
map.put("固原", "宁夏回族自治区");
map.put("固原市", "宁夏回族自治区");
map.put("中卫", "宁夏回族自治区");
map.put("中卫市", "宁夏回族自治区");
// 海南
map.put("海口", "海南省");
map.put("海口市", "海南省");
map.put("三亚", "海南省");
map.put("三亚市", "海南省");
map.put("三沙", "海南省");
map.put("三沙市", "海南省");
map.put("儋州", "海南省");
map.put("儋州市", "海南省");
// 贵州
map.put("贵阳", "贵州省");
map.put("贵阳市", "贵州省");
map.put("六盘水", "贵州省");
map.put("六盘水市", "贵州省");
map.put("遵义", "贵州省");
map.put("遵义市", "贵州省");
map.put("安顺", "贵州省");
map.put("安顺市", "贵州省");
map.put("毕节", "贵州省");
map.put("毕节市", "贵州省");
map.put("铜仁", "贵州省");
map.put("铜仁市", "贵州省");
// 云南
map.put("昆明", "云南省");
map.put("昆明市", "云南省");
map.put("曲靖", "云南省");
map.put("曲靖市", "云南省");
map.put("玉溪", "云南省");
map.put("玉溪市", "云南省");
map.put("保山", "云南省");
map.put("保山市", "云南省");
map.put("昭通", "云南省");
map.put("昭通市", "云南省");
map.put("丽江", "云南省");
map.put("丽江市", "云南省");
map.put("普洱", "云南省");
map.put("普洱市", "云南省");
map.put("临沧", "云南省");
map.put("临沧市", "云南省");
// 甘肃
map.put("兰州", "甘肃省");
map.put("兰州市", "甘肃省");
map.put("嘉峪关", "甘肃省");
map.put("嘉峪关市", "甘肃省");
map.put("金昌", "甘肃省");
map.put("金昌市", "甘肃省");
map.put("白银", "甘肃省");
map.put("白银市", "甘肃省");
map.put("天水", "甘肃省");
map.put("天水市", "甘肃省");
map.put("武威", "甘肃省");
map.put("武威市", "甘肃省");
map.put("张掖", "甘肃省");
map.put("张掖市", "甘肃省");
map.put("平凉", "甘肃省");
map.put("平凉市", "甘肃省");
map.put("酒泉", "甘肃省");
map.put("酒泉市", "甘肃省");
map.put("庆阳", "甘肃省");
map.put("庆阳市", "甘肃省");
map.put("定西", "甘肃省");
map.put("定西市", "甘肃省");
map.put("陇南", "甘肃省");
map.put("陇南市", "甘肃省");
// 青海
map.put("西宁", "青海省");
map.put("西宁市", "青海省");
map.put("海东", "青海省");
map.put("海东市", "青海省");
// 陕西
map.put("西安", "陕西省");
map.put("西安市", "陕西省");
map.put("铜川", "陕西省");
map.put("铜川市", "陕西省");
map.put("宝鸡", "陕西省");
map.put("宝鸡市", "陕西省");
map.put("咸阳", "陕西省");
map.put("咸阳市", "陕西省");
map.put("渭南", "陕西省");
map.put("渭南市", "陕西省");
map.put("延安", "陕西省");
map.put("延安市", "陕西省");
map.put("汉中", "陕西省");
map.put("汉中市", "陕西省");
map.put("榆林", "陕西省");
map.put("榆林市", "陕西省");
map.put("安康", "陕西省");
map.put("安康市", "陕西省");
map.put("商洛", "陕西省");
map.put("商洛市", "陕西省");
// 辽宁
map.put("沈阳", "辽宁省");
map.put("沈阳市", "辽宁省");
map.put("大连", "辽宁省");
map.put("大连市", "辽宁省");
map.put("鞍山", "辽宁省");
map.put("鞍山市", "辽宁省");
map.put("抚顺", "辽宁省");
map.put("抚顺市", "辽宁省");
map.put("本溪", "辽宁省");
map.put("本溪市", "辽宁省");
map.put("丹东", "辽宁省");
map.put("丹东市", "辽宁省");
map.put("锦州", "辽宁省");
map.put("锦州市", "辽宁省");
map.put("营口", "辽宁省");
map.put("营口市", "辽宁省");
map.put("阜新", "辽宁省");
map.put("阜新市", "辽宁省");
map.put("辽阳", "辽宁省");
map.put("辽阳市", "辽宁省");
map.put("盘锦", "辽宁省");
map.put("盘锦市", "辽宁省");
map.put("铁岭", "辽宁省");
map.put("铁岭市", "辽宁省");
map.put("朝阳", "辽宁省");
map.put("朝阳市", "辽宁省");
map.put("葫芦岛", "辽宁省");
map.put("葫芦岛市", "辽宁省");
// 吉林
map.put("长春", "吉林省");
map.put("长春市", "吉林省");
map.put("吉林", "吉林省");
map.put("吉林市", "吉林省");
map.put("四平", "吉林省");
map.put("四平市", "吉林省");
map.put("辽源", "吉林省");
map.put("辽源市", "吉林省");
map.put("通化", "吉林省");
map.put("通化市", "吉林省");
map.put("白山", "吉林省");
map.put("白山市", "吉林省");
map.put("松原", "吉林省");
map.put("松原市", "吉林省");
map.put("白城", "吉林省");
map.put("白城市", "吉林省");
// 黑龙江
map.put("哈尔滨", "黑龙江省");
map.put("哈尔滨市", "黑龙江省");
map.put("齐齐哈尔", "黑龙江省");
map.put("齐齐哈尔市", "黑龙江省");
map.put("鸡西", "黑龙江省");
map.put("鸡西市", "黑龙江省");
map.put("鹤岗", "黑龙江省");
map.put("鹤岗市", "黑龙江省");
map.put("双鸭山", "黑龙江省");
map.put("双鸭山市", "黑龙江省");
map.put("大庆", "黑龙江省");
map.put("大庆市", "黑龙江省");
map.put("伊春", "黑龙江省");
map.put("伊春市", "黑龙江省");
map.put("佳木斯", "黑龙江省");
map.put("佳木斯市", "黑龙江省");
map.put("七台河", "黑龙江省");
map.put("七台河市", "黑龙江省");
map.put("牡丹江", "黑龙江省");
map.put("牡丹江市", "黑龙江省");
map.put("黑河", "黑龙江省");
map.put("黑河市", "黑龙江省");
map.put("绥化", "黑龙江省");
map.put("绥化市", "黑龙江省");
// 河北
map.put("石家庄", "河北省");
map.put("石家庄市", "河北省");
map.put("唐山", "河北省");
map.put("唐山市", "河北省");
map.put("秦皇岛", "河北省");
map.put("秦皇岛市", "河北省");
map.put("邯郸", "河北省");
map.put("邯郸市", "河北省");
map.put("邢台", "河北省");
map.put("邢台市", "河北省");
map.put("保定", "河北省");
map.put("保定市", "河北省");
map.put("张家口", "河北省");
map.put("张家口市", "河北省");
map.put("承德", "河北省");
map.put("承德市", "河北省");
map.put("沧州", "河北省");
map.put("沧州市", "河北省");
map.put("廊坊", "河北省");
map.put("廊坊市", "河北省");
map.put("衡水", "河北省");
map.put("衡水市", "河北省");
// 山西
map.put("太原", "山西省");
map.put("太原市", "山西省");
map.put("大同", "山西省");
map.put("大同市", "山西省");
map.put("阳泉", "山西省");
map.put("阳泉市", "山西省");
map.put("长治", "山西省");
map.put("长治市", "山西省");
map.put("晋城", "山西省");
map.put("晋城市", "山西省");
map.put("朔州", "山西省");
map.put("朔州市", "山西省");
map.put("晋中", "山西省");
map.put("晋中市", "山西省");
map.put("运城", "山西省");
map.put("运城市", "山西省");
map.put("忻州", "山西省");
map.put("忻州市", "山西省");
map.put("临汾", "山西省");
map.put("临汾市", "山西省");
map.put("吕梁", "山西省");
map.put("吕梁市", "山西省");
// 河南
map.put("郑州", "河南省");
map.put("郑州市", "河南省");
map.put("开封", "河南省");
map.put("开封市", "河南省");
map.put("洛阳", "河南省");
map.put("洛阳市", "河南省");
map.put("平顶山", "河南省");
map.put("平顶山市", "河南省");
map.put("安阳", "河南省");
map.put("安阳市", "河南省");
map.put("鹤壁", "河南省");
map.put("鹤壁市", "河南省");
map.put("新乡", "河南省");
map.put("新乡市", "河南省");
map.put("焦作", "河南省");
map.put("焦作市", "河南省");
map.put("濮阳", "河南省");
map.put("濮阳市", "河南省");
map.put("许昌", "河南省");
map.put("许昌市", "河南省");
map.put("漯河", "河南省");
map.put("漯河市", "河南省");
map.put("三门峡", "河南省");
map.put("三门峡市", "河南省");
map.put("南阳", "河南省");
map.put("南阳市", "河南省");
map.put("商丘", "河南省");
map.put("商丘市", "河南省");
map.put("信阳", "河南省");
map.put("信阳市", "河南省");
map.put("周口", "河南省");
map.put("周口市", "河南省");
map.put("驻马店", "河南省");
map.put("驻马店市", "河南省");
return map;
}
private static String cleanupDetail(String detail) {
if (detail == null) return null;
String res = CLEAN_PATTERN.matcher(detail).replaceFirst("").trim();
return res.isEmpty() ? null : res;
}
private static boolean isMunicipality(String province) {
if (province == null) return false;
for (String m : MUNICIPALITIES) if (province.equals(m + "")) return true;
return false;
}
// ===================== 结果类 =====================
@SuppressWarnings("LombokGetterMayBeUsed")
public static class AddressInfo {
private final String province;
private final String city;
private final String district;
private final String detail;
public AddressInfo(String province, String city, String district, String detail) {
this.province = province;
this.city = city;
this.district = district;
this.detail = detail;
}
public String getProvince() {
return province;
}
public String getCity() {
return city;
}
public String getDistrict() {
return district;
}
public String getDetail() {
return detail;
}
@Override
public String toString() {
return String.format("省:%s\n市%s\n区%s\n详情%s",
province == null ? "未识别" : province,
city == null ? "未识别" : city,
district == null ? "未识别" : district,
detail == null ? "" : detail);
}
}
}

View File

@@ -0,0 +1,357 @@
package com.iqudoo.framework.tape.modules.utils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
/**
* 数组工具类
*/
@SuppressWarnings({"unchecked", "unused", "DuplicatedCode"})
public class ArrayUtil {
public interface ForHandler<T> {
boolean filter(T t);
void handle(T t);
}
public interface FilterHandler<T> {
boolean filter(T t);
}
public interface DistinctHandler<T> {
String distinctKey(T t);
}
public interface MapHandler<T, R> {
R map(T t);
}
public static abstract class ToHandler<T, R> implements MapHandler<T, R>, FilterHandler<T>, DistinctHandler<R> {
@Override
public boolean filter(T t) {
return true;
}
@Override
public String distinctKey(R r) {
return null;
}
public boolean filterNewValue(R t) {
return true;
}
}
public static <T> boolean in(List<T> array, T target) {
for (T val : array) {
if (val != null && val.equals(target)) {
return true;
}
}
return false;
}
public static <T> boolean in(T[] array, T target) {
for (T val : array) {
if (val != null && val.equals(target)) {
return true;
}
}
return false;
}
public static <E, R> List<R> to(List<E> collection, ToHandler<E, R> handler) {
return filter(distinct(map(filter(collection, handler), handler), handler), new FilterHandler<R>() {
@Override
public boolean filter(R r) {
return handler.filterNewValue(r);
}
});
}
public static <E, R> List<R> map(List<E> collection, MapHandler<E, R> handler) {
List<R> list = new ArrayList<>();
if (handler == null) {
return list;
}
if (collection != null) {
for (E e : collection) {
R r = handler.map(e);
if (r != null) {
list.add(r);
}
}
}
return list;
}
public static <E> void foreach(List<E> collection, ForHandler<E> handler) {
if (handler == null) {
return;
}
if (collection != null) {
for (int i = collection.size() - 1; i >= 0; i--) {
E e = collection.get(i);
if (handler.filter(e)) {
handler.handle(e);
}
}
}
}
public static <E> List<E> distinct(List<E> collection, DistinctHandler<E> handler) {
if (handler == null) {
return collection;
}
List<E> list = new ArrayList<>();
if (collection != null) {
List<String> distinctKeyList = new ArrayList<>();
for (E e : collection) {
String key = handler.distinctKey(e);
if (key == null) {
list.add(e);
} else if (!distinctKeyList.contains(key)) {
distinctKeyList.add(key);
list.add(e);
}
}
}
return list;
}
public static <E> List<E> filter(List<E> collection, FilterHandler<E> handler) {
if (handler == null) {
return collection;
}
List<E> list = new ArrayList<>();
if (collection != null) {
for (E e : collection) {
if (handler.filter(e)) {
list.add(e);
}
}
}
return list;
}
public static <E> boolean hasIf(List<E> collection, FilterHandler<E> handler) {
if (handler == null) {
return false;
}
if (collection != null) {
for (int i = collection.size() - 1; i >= 0; i--) {
E e = collection.get(i);
if (handler.filter(e)) {
return true;
}
}
}
return false;
}
public static <E> int indexIf(List<E> collection, FilterHandler<E> handler) {
if (handler == null) {
return -1;
}
if (collection != null) {
for (int i = collection.size() - 1; i >= 0; i--) {
E e = collection.get(i);
if (handler.filter(e)) {
return i;
}
}
}
return -1;
}
public static <E> E findIf(List<E> collection, FilterHandler<E> handler) {
if (handler == null) {
return null;
}
if (collection != null) {
for (int i = collection.size() - 1; i >= 0; i--) {
E e = collection.get(i);
if (handler.filter(e)) {
return e;
}
}
}
return null;
}
public static <E> List<E> removeIf(List<E> collection, FilterHandler<E> handler) {
if (handler == null) {
return collection;
}
List<E> newCollection = new ArrayList<>(collection);
newCollection.removeIf(new Predicate<E>() {
@Override
public boolean test(E e) {
return handler.filter(e);
}
});
return collection;
}
public static List<String> split(String valStr, String... splitDelimiter) {
List<String> list = new ArrayList<>();
// 新增空值判断
if (valStr == null) {
return list;
}
if (splitDelimiter == null || splitDelimiter.length == 0) {
splitDelimiter = new String[]{","};
}
String[] nextSlitDelimiter = new String[splitDelimiter.length - 1];
System.arraycopy(splitDelimiter, 1, nextSlitDelimiter, 0, nextSlitDelimiter.length);
List<String> splitList = splitToString(valStr, splitDelimiter[0]);
if (nextSlitDelimiter.length > 0) {
for (String str : splitList) {
list.addAll(split(str, nextSlitDelimiter));
}
} else {
list.addAll(splitList);
}
return list;
}
public static <T> List<T> splitTo(String valStr, Class<T> clazz, String... splitDelimiter) {
List<String> stringList = split(valStr, splitDelimiter);
return ArrayUtil.to(stringList, new ToHandler<String, T>() {
@Override
public boolean filterNewValue(T t) {
return t != null;
}
@Override
public T map(String val) {
try {
if (clazz == String.class) {
return (T) val;
} else if (clazz == Integer.class) {
return (T) Integer.valueOf(val);
} else if (clazz == Long.class) {
return (T) Long.valueOf(val);
} else if (clazz == Double.class) {
return (T) Double.valueOf(val);
} else if (clazz == Float.class) {
return (T) Float.valueOf(val);
} else if (clazz == Boolean.class) {
return (T) Boolean.valueOf(val);
}
} catch (Throwable ignored) {
}
return null;
}
});
}
private static List<String> splitToString(String valStr, String splitDelimiter) {
if (valStr != null) {
String[] valArr = valStr.split(splitDelimiter);
List<String> list = new ArrayList<>();
for (String val : valArr) {
val = val.trim();
if (val.isEmpty()) {
continue;
}
list.add(val);
}
return list;
}
return null;
}
/**
* 计算多个List<T>的交集
* 交集定义:所有子列表中都共同包含的元素
*
* @param list list
* @param checkList checkList
* @return 交集
*/
public static <T> List<T> getListIntersection(List<T> list, List<T>... checkList) {
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
if (checkList == null || checkList.length == 0) {
return list;
}
Set<T> intersectionSet = new HashSet<>(checkList[0]);
for (int i = 1; i < checkList.length; i++) {
List<T> currentList = checkList[i];
if (currentList == null || currentList.isEmpty()) {
return new ArrayList<>();
}
intersectionSet.retainAll(currentList);
if (intersectionSet.isEmpty()) {
break;
}
}
return new ArrayList<>(intersectionSet);
}
/**
* 计算 List<List<T>> 中所有子集合的交集
* 交集定义:所有子列表中都共同包含的元素
*
* @param multiList 多维集合
* @return 交集
*/
public static <T> List<T> getListIntersection(List<List<T>> multiList) {
if (multiList == null || multiList.isEmpty()) {
return new ArrayList<>();
}
if (multiList.size() == 1) {
return new ArrayList<>(multiList.get(0));
}
Set<T> intersectionSet = new HashSet<>(multiList.get(0));
for (int i = 1; i < multiList.size(); i++) {
List<T> currentList = multiList.get(i);
if (currentList == null || currentList.isEmpty()) {
return new ArrayList<>();
}
intersectionSet.retainAll(currentList);
if (intersectionSet.isEmpty()) {
break;
}
}
return new ArrayList<>(intersectionSet);
}
/**
* 将List<T>拆分成多个子列表
*
* @param list 原始列表
* @param chunkSize 个子列表的最大容量
* @return 拆分后的列表集合
*/
public static <T> List<List<T>> getChunkList(List<T> list, int chunkSize) {
List<List<T>> result = new ArrayList<>();
if (list == null || list.isEmpty()) {
return result;
}
int totalSize = list.size();
int chunkCount = (totalSize + chunkSize - 1) / chunkSize;
for (int i = 0; i < chunkCount; i++) {
int start = i * chunkSize;
int end = Math.min(start + chunkSize, totalSize);
result.add(list.subList(start, end));
}
return result;
}
}

View File

@@ -0,0 +1,129 @@
package com.iqudoo.framework.tape.modules.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Base64工具类
*/
public final class Base64Util {
private static final char[] DEFAULT_LEGAL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.toCharArray();
public static String encode(byte[] data) {
return encode(data, DEFAULT_LEGAL_CHARS);
}
public static byte[] decode(String string) {
return decode(string, DEFAULT_LEGAL_CHARS);
}
public static String encode(byte[] data, char[] legalChars) {
int start = 0;
int len = data.length;
StringBuilder buf = new StringBuilder(data.length * 3 / 2);
int end = len - 3;
int i = start;
while (i <= end) {
int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 0x0ff) << 8)
| (((int) data[i + 2]) & 0x0ff);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append(legalChars[d & 63]);
i += 3;
}
if (i == start + len - 2) {
int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 255) << 8);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append("=");
} else if (i == start + len - 1) {
int d = (((int) data[i]) & 0x0ff) << 16;
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append("==");
}
return buf.toString();
}
@SuppressWarnings("UnusedAssignment")
public static byte[] decode(String string, char[] legalChars) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
decode(string, legalChars, bos);
} catch (IOException e) {
throw new RuntimeException();
}
byte[] decodedBytes = bos.toByteArray();
try {
bos.close();
bos = null;
} catch (IOException ignored) {
}
return decodedBytes;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// Private /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
private static void decode(String s, char[] legalChars, OutputStream os) throws IOException {
int i = 0;
int len = s.length();
while (true) {
while (i < len && s.charAt(i) <= ' ')
i++;
if (i == len)
break;
int tri = (decode(s.charAt(i), legalChars) << 18) + (decode(s.charAt(i + 1), legalChars) << 12) + (decode(s.charAt(i + 2), legalChars) << 6)
+ (decode(s.charAt(i + 3), legalChars));
os.write((tri >> 16) & 255);
if (s.charAt(i + 2) == '=')
break;
os.write((tri >> 8) & 255);
if (s.charAt(i + 3) == '=')
break;
os.write(tri & 255);
i += 4;
}
}
private static int decode(char c, char[] legalChars) {
if (c >= 'A' && c <= 'Z')
return ((int) c) - 65;
else if (c >= 'a' && c <= 'z')
return ((int) c) - 97 + 26;
else if (c >= '0' && c <= '9')
return ((int) c) - 48 + 26 + 26;
else {
if (c == legalChars[62]) {
return 62;
} else if (c == legalChars[63]) {
return 63;
} else if (c == '=') {
return 0;
} else {
throw new RuntimeException("unexpected code: " + c);
}
}
}
}

View File

@@ -0,0 +1,192 @@
package com.iqudoo.framework.tape.modules.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@SuppressWarnings("EnhancedSwitchMigration")
public class DateTimeUtil {
public static String getWeekOfDay(Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
switch (c.get(Calendar.DAY_OF_WEEK)) {
case Calendar.MONDAY:
return "1";
case Calendar.TUESDAY:
return "2";
case Calendar.WEDNESDAY:
return "3";
case Calendar.THURSDAY:
return "4";
case Calendar.FRIDAY:
return "5";
case Calendar.SATURDAY:
return "6";
case Calendar.SUNDAY:
return "7";
}
return "";
}
public static long getDateStartTime(String dateStr) {
return getDateStartTime(dateStr, "yyyy-MM-dd");
}
public static long getDateStartTime(String dateStr, String formatStr) {
Date date = parseDate(dateStr, formatStr);
if (date != null) {
return getStartOfDay(date);
}
return 0L;
}
public static long getDateEndTime(String dateStr) {
return getDateEndTime(dateStr, "yyyy-MM-dd");
}
public static long getDateEndTime(String dateStr, String formatStr) {
Date date = parseDate(dateStr, formatStr);
if (date != null) {
return getEndOfDay(date);
}
return 0L;
}
public static long getStartOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
}
public static long getEndOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
return calendar.getTimeInMillis() + 1;
}
/**
* 获取指定日期所属月份的开始时间戳当月1日 00:00:00
*
* @param date 目标日期
* @return 月份开始时间戳毫秒若date为null则返回0
*/
public static long getStartOfMonth(Date date) {
if (date == null) {
return 0L;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
}
/**
* 获取指定日期所属月份的结束时间戳(当月最后一日 23:59:59 999毫秒
*
* @param date 目标日期
* @return 月份结束时间戳毫秒若date为null则返回0
*/
public static long getEndOfMonth(Date date) {
if (date == null) {
return 0L;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.add(Calendar.MONTH, 1);
calendar.add(Calendar.MILLISECOND, -1);
return calendar.getTimeInMillis();
}
public static Date parseDate(String date) {
return parseDate(date, "yyyy-MM-dd HH:mm:ss");
}
public static Date parseDate(String date, String formatStr) {
Date date1 = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatStr);
try {
date1 = simpleDateFormat.parse(date);
} catch (Throwable ignored) {
}
return date1;
}
public static String formatDate(long timeMs) {
return formatDate(timeMs, "yyyy-MM-dd HH:mm:ss");
}
public static String formatDate(Date date) {
return formatDate(date, "yyyy-MM-dd HH:mm:ss");
}
public static String formatDate(long timeMs, String formatStr) {
return formatDate(new Date(timeMs), formatStr);
}
public static String formatDate(Date date, String formatStr) {
if (formatStr == null) {
formatStr = "yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatStr);
return simpleDateFormat.format(date);
}
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/**
* Format of http head.
*/
public static final String FORMAT_HTTP_DATA = "EEE, dd MMM y HH:mm:ss 'GMT'";
/**
* Commmon TimeZone for GMT.
*/
public static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
/**
* Parsing the TimeZone of time in milliseconds.
*
* @param gmtTime GRM Time, Format such as: {@value #FORMAT_HTTP_DATA}.
* @return The number of milliseconds from 1970.1.1.
* @throws ParseException if an error occurs during parsing.
*/
public static long parseGMTToMillis(String gmtTime) throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_HTTP_DATA, Locale.US);
formatter.setTimeZone(GMT_TIME_ZONE);
Date date = formatter.parse(gmtTime);
return date.getTime();
}
/**
* Parsing the TimeZone of time from milliseconds.
*
* @param milliseconds the number of milliseconds from 1970.1.1.
* @return GRM Time, Format such as: {@value #FORMAT_HTTP_DATA}.
*/
public static String formatMillisToGMT(long milliseconds) {
Date date = new Date(milliseconds);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(FORMAT_HTTP_DATA, Locale.US);
simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
return simpleDateFormat.format(date);
}
}

View File

@@ -0,0 +1,192 @@
package com.iqudoo.framework.tape.modules.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@SuppressWarnings({"DuplicatedCode", "EnhancedSwitchMigration", "unused"})
public class DateUtil {
public static String getWeekOfDay(Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
switch (c.get(Calendar.DAY_OF_WEEK)) {
case Calendar.MONDAY:
return "1";
case Calendar.TUESDAY:
return "2";
case Calendar.WEDNESDAY:
return "3";
case Calendar.THURSDAY:
return "4";
case Calendar.FRIDAY:
return "5";
case Calendar.SATURDAY:
return "6";
case Calendar.SUNDAY:
return "7";
}
return "";
}
public static long getDateStartTime(String dateStr) {
return getDateStartTime(dateStr, "yyyy-MM-dd");
}
public static long getDateStartTime(String dateStr, String formatStr) {
Date date = parseDate(dateStr, formatStr);
if (date != null) {
return getStartOfDay(date);
}
return 0L;
}
public static long getDateEndTime(String dateStr) {
return getDateEndTime(dateStr, "yyyy-MM-dd");
}
public static long getDateEndTime(String dateStr, String formatStr) {
Date date = parseDate(dateStr, formatStr);
if (date != null) {
return getEndOfDay(date);
}
return 0L;
}
public static long getStartOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
}
public static long getEndOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
return calendar.getTimeInMillis() + 1;
}
/**
* 获取指定日期所属月份的开始时间戳当月1日 00:00:00
*
* @param date 目标日期
* @return 月份开始时间戳毫秒若date为null则返回0
*/
public static long getStartOfMonth(Date date) {
if (date == null) {
return 0L;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
}
/**
* 获取指定日期所属月份的结束时间戳(当月最后一日 23:59:59 999毫秒
*
* @param date 目标日期
* @return 月份结束时间戳毫秒若date为null则返回0
*/
public static long getEndOfMonth(Date date) {
if (date == null) {
return 0L;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.add(Calendar.MONTH, 1);
calendar.add(Calendar.MILLISECOND, -1);
return calendar.getTimeInMillis();
}
public static Date parseDate(String date) {
return parseDate(date, "yyyy-MM-dd HH:mm:ss");
}
public static Date parseDate(String date, String formatStr) {
Date date1 = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatStr);
try {
date1 = simpleDateFormat.parse(date);
} catch (Throwable ignored) {
}
return date1;
}
public static String formatDate(long timeMs) {
return formatDate(timeMs, "yyyy-MM-dd HH:mm:ss");
}
public static String formatDate(Date date) {
return formatDate(date, "yyyy-MM-dd HH:mm:ss");
}
public static String formatDate(long timeMs, String formatStr) {
return formatDate(new Date(timeMs), formatStr);
}
public static String formatDate(Date date, String formatStr) {
if (formatStr == null) {
formatStr = "yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatStr);
return simpleDateFormat.format(date);
}
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/**
* Format of http head.
*/
public static final String FORMAT_HTTP_DATA = "EEE, dd MMM y HH:mm:ss 'GMT'";
/**
* Commmon TimeZone for GMT.
*/
public static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
/**
* Parsing the TimeZone of time in milliseconds.
*
* @param gmtTime GRM Time, Format such as: {@value #FORMAT_HTTP_DATA}.
* @return The number of milliseconds from 1970.1.1.
* @throws ParseException if an error occurs during parsing.
*/
public static long parseGMTToMillis(String gmtTime) throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_HTTP_DATA, Locale.US);
formatter.setTimeZone(GMT_TIME_ZONE);
Date date = formatter.parse(gmtTime);
return date.getTime();
}
/**
* Parsing the TimeZone of time from milliseconds.
*
* @param milliseconds the number of milliseconds from 1970.1.1.
* @return GRM Time, Format such as: {@value #FORMAT_HTTP_DATA}.
*/
public static String formatMillisToGMT(long milliseconds) {
Date date = new Date(milliseconds);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(FORMAT_HTTP_DATA, Locale.US);
simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
return simpleDateFormat.format(date);
}
}

View File

@@ -0,0 +1,96 @@
package com.iqudoo.framework.tape.modules.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.*;
/**
* 内存级防抖工具(最后一次调用后等待 waitTime 再执行)
* 适用于:重复点击、重复上报、频繁更新、频繁查询等场景
*/
@SuppressWarnings("unused")
public class DebounceUtil {
public interface DebounceRunner {
void onRun() throws Throwable;
}
private static final Logger LOGGER = LoggerFactory.getLogger(DebounceUtil.class);
// 防抖任务缓存 key -> future任务
private static final Map<String, Future<?>> DEBOUNCE_MAP = new ConcurrentHashMap<>();
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(
Runtime.getRuntime().availableProcessors(),
r -> {
Thread thread = new Thread(r, "debounce-thread");
thread.setDaemon(true);
return thread;
}
);
/**
* 防抖执行(最后一次调用后等待 waitTime 毫秒再执行)
*
* @param key 防抖唯一标识不同业务用不同key
* @param waitTime 防抖等待时间(毫秒)
* @param runnable 要执行的任务
*/
public static void debounce(String key, long waitTime, DebounceRunner runnable) {
if (key == null || key.isBlank() || waitTime <= 0 || runnable == null) {
return;
}
// 包装任务执行后自动清除map
Runnable wrapperTask = () -> {
try {
runnable.onRun();
} catch (Throwable e) {
LOGGER.error("Debounce task execute error, key:{}", key, e);
} finally {
DEBOUNCE_MAP.remove(key);
}
};
// 加锁保证原子性
synchronized (key.intern()) {
// 1. 取消旧任务
Future<?> oldFuture = DEBOUNCE_MAP.get(key);
if (oldFuture != null && !oldFuture.isDone()) {
oldFuture.cancel(false);
}
// 2. 提交新延迟任务
ScheduledFuture<?> newFuture = SCHEDULER.schedule(
wrapperTask,
waitTime,
TimeUnit.MILLISECONDS
);
DEBOUNCE_MAP.put(key, newFuture);
}
}
/**
* 立即移除某个key的防抖任务
*/
public static void remove(String key) {
if (key == null) return;
synchronized (key.intern()) {
Future<?> future = DEBOUNCE_MAP.remove(key);
if (future != null) {
future.cancel(false);
}
}
}
/**
* 清空所有防抖任务
*/
public static void clearAll() {
DEBOUNCE_MAP.forEach((k, f) -> f.cancel(false));
DEBOUNCE_MAP.clear();
}
}

View File

@@ -0,0 +1,33 @@
package com.iqudoo.framework.tape.modules.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class DecimalUtil {
public static String format(BigDecimal bigDecimal) {
if (bigDecimal == null) {
return "0";
}
DecimalFormat df = new DecimalFormat("#.##");
df.setRoundingMode(RoundingMode.HALF_UP);
return df.format(bigDecimal);
}
public static String formatAmount(BigDecimal bigDecimal) {
if (bigDecimal == null) {
return "0.00";
}
// 0.00 代表强制保留2位小数
DecimalFormat df = new DecimalFormat("0.00");
df.setRoundingMode(RoundingMode.HALF_UP);
return df.format(bigDecimal);
}
public static BigDecimal customSetScale(BigDecimal number, int newScale, RoundingMode roundingMode) {
BigDecimal factor = BigDecimal.ONE.movePointRight(newScale);
return number.divide(factor, roundingMode).multiply(factor);
}
}

View File

@@ -0,0 +1,168 @@
package com.iqudoo.framework.tape.modules.utils;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class DigestUtil {
private static final String MD5_ALGORITHM_NAME = "MD5";
private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Return a hexadecimal string representation of the MD5 digest of string.
*
* @param string string to calculate the digest over.
* @return a hexadecimal digest string.
*/
public static String md5DigestAsHex(String string) {
return md5DigestAsHex(string.getBytes());
}
/**
* Return a hexadecimal string representation of the MD5 digest of the given bytes.
*
* @param bytes the bytes to calculate the digest over.
* @return a hexadecimal digest string.
*/
public static String md5DigestAsHex(byte[] bytes) {
return digestAsHexString(MD5_ALGORITHM_NAME, bytes);
}
/**
* Return a hexadecimal string representation of the MD5 digest of the given stream.
*
* @param inputStream the InputStream to calculate the digest over.
* @return a hexadecimal digest string.
*/
public static String md5DigestAsHex(InputStream inputStream) throws IOException {
return digestAsHexString(MD5_ALGORITHM_NAME, inputStream);
}
/**
* Calculate the MD5 digest of string.
*
* @param string string to calculate the digest over.
* @return the digest.
*/
public static byte[] md5Digest(String string) {
return md5Digest(string.getBytes());
}
/**
* Calculate the MD5 digest of the given bytes.
*
* @param bytes the bytes to calculate the digest over.
* @return the digest.
*/
public static byte[] md5Digest(byte[] bytes) {
return digest(MD5_ALGORITHM_NAME, bytes);
}
/**
* Calculate the MD5 digest of the given stream.
*
* @param inputStream the InputStream to calculate the digest over.
* @return the digest.
*/
public static byte[] md5Digest(InputStream inputStream) throws IOException {
return digest(MD5_ALGORITHM_NAME, inputStream);
}
/**
* Append a hexadecimal string representation of the MD5 digest of the given
* bytes to the given {@link StringBuilder}.
*
* @param bytes the bytes to calculate the digest over.
* @param builder the string builder to append the digest to.
* @return the given string builder.
*/
public static StringBuilder appendMd5DigestAsHex(byte[] bytes, StringBuilder builder) {
return appendDigestAsHex(MD5_ALGORITHM_NAME, bytes, builder);
}
/**
* Append a hexadecimal string representation of the MD5 digest of the given
* getInputStream to the given {@link StringBuilder}.
*
* @param inputStream the getInputStream to calculate the digest over.
* @param builder the string builder to append the digest to.
* @return the given string builder.
*/
public static StringBuilder appendMd5DigestAsHex(InputStream inputStream, StringBuilder builder) throws IOException {
return appendDigestAsHex(MD5_ALGORITHM_NAME, inputStream, builder);
}
/**
* Create a new {@link MessageDigest} with the given algorithm.
* Necessary because {@code MessageDigest} is not thread-safe.
*/
private static MessageDigest getDigest(String algorithm) {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("Could not find MessageDigest with algorithm \"" + algorithm + "\"", ex);
}
}
private static byte[] digest(String algorithm, byte[] bytes) {
return getDigest(algorithm).digest(bytes);
}
private static byte[] digest(String algorithm, InputStream inputStream) throws IOException {
MessageDigest messageDigest = getDigest(algorithm);
final byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
messageDigest.update(buffer, 0, bytesRead);
}
return messageDigest.digest();
}
private static String digestAsHexString(String algorithm, byte[] bytes) {
char[] hexDigest = digestAsHexChars(algorithm, bytes);
return new String(hexDigest);
}
private static String digestAsHexString(String algorithm, InputStream inputStream) throws IOException {
char[] hexDigest = digestAsHexChars(algorithm, inputStream);
return new String(hexDigest);
}
private static StringBuilder appendDigestAsHex(String algorithm, byte[] bytes, StringBuilder builder) {
char[] hexDigest = digestAsHexChars(algorithm, bytes);
return builder.append(hexDigest);
}
private static StringBuilder appendDigestAsHex(String algorithm, InputStream inputStream, StringBuilder builder)
throws IOException {
char[] hexDigest = digestAsHexChars(algorithm, inputStream);
return builder.append(hexDigest);
}
private static char[] digestAsHexChars(String algorithm, byte[] bytes) {
byte[] digest = digest(algorithm, bytes);
return encodeHex(digest);
}
private static char[] digestAsHexChars(String algorithm, InputStream inputStream) throws IOException {
byte[] digest = digest(algorithm, inputStream);
return encodeHex(digest);
}
private static char[] encodeHex(byte[] bytes) {
char chars[] = new char[32];
for (int i = 0; i < chars.length; i = i + 2) {
byte b = bytes[i / 2];
chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
chars[i + 1] = HEX_CHARS[b & 0xf];
}
return chars;
}
}

View File

@@ -0,0 +1,124 @@
package com.iqudoo.framework.tape.modules.utils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class FileUtil {
@SuppressWarnings("LombokGetterMayBeUsed")
public static class FileData {
byte[] fileBytes;
String fileName;
FileData(byte[] fileBytes, String fileName) {
this.fileBytes = fileBytes;
this.fileName = fileName;
}
public byte[] getFileBytes() {
return fileBytes;
}
public String getFileName() {
return fileName;
}
}
@SuppressWarnings("resource")
public static FileData getFileData(String fileUrl) throws Throwable {
if (fileUrl.startsWith("//")) {
fileUrl = "http:" + fileUrl;
}
InputStream input = createInputStreamByUrl(fileUrl);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int numBytesRead;
while ((numBytesRead = input.read(buf)) != -1) {
output.write(buf, 0, numBytesRead);
}
String fileName = extractFileName(fileUrl);
return new FileData(output.toByteArray(), fileName);
}
private static InputStream createInputStreamByUrl(String url) throws Throwable {
URL uri = new URL(url);
// 建立http连接
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
// 设置允许输出
conn.setDoOutput(true);
// 设置允许输入
conn.setDoInput(true);
// 设置不用缓存
conn.setUseCaches(false);
// 设置传递方式
conn.setRequestMethod("GET");
// 设置维持长连接
conn.setRequestProperty("Connection", "Keep-Alive");
// 设置文件字符集:
conn.setRequestProperty("Charset", "UTF-8");
// 开始连接请求
conn.connect();
if (conn.getResponseCode() == 200) {
return conn.getInputStream();
} else {
throw new Throwable("get request failure: " + "\nurl: " + url
+ "\nstatusCode: " + conn.getResponseCode());
}
}
private static String extractFileName(String imageUrl) {
String[] parts = imageUrl.split("/");
String fileName = parts[parts.length - 1];
// 去除文件名中的查询参数
fileName = fileName.split("\\?")[0];
return fileName;
}
public static void saveFile(String filePath, byte[] fileData) throws Throwable {
File file = new File(filePath);
File parentDir = file.getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}
if (file.exists()) {
file.delete();
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(fileData, 0, fileData.length);
fos.flush();
}
}
/**
* 获取文件的后缀名
*
* @param fileName 文件名名称
* @return 文件后缀名
*/
public static String getFileSuffix(String fileName) {
int indexOf = fileName.lastIndexOf(".");
String suffix = "";
if (indexOf >= 0) {
suffix = fileName.substring(indexOf);
}
return suffix.toLowerCase();
}
public static String getFileName(String videoUrl) {
if (videoUrl != null) {
int index = videoUrl.lastIndexOf("/");
if (index >= 0) {
return videoUrl.substring(index + 1);
}
return videoUrl;
}
return null;
}
}

View File

@@ -0,0 +1,613 @@
package com.iqudoo.framework.tape.modules.utils;
import okhttp3.*;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.util.io.resource.PathResource;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* 基于OkHttp实现的HTTP请求工具类
* 替代原HttpURLConnection实现提升请求稳定性和可维护性
*/
@SuppressWarnings({"UastIncorrectHttpHeaderInspection", "unused", "resource"})
public class HttpUtil {
// 私有构造器:禁止实例化
private HttpUtil() {
}
public static String getSortedParams(Map<String, String> params) {
if (params != null) {
Map<String, String> sortedParams = new TreeMap<>(params);
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
builder.append(entry.getKey()).append("=")
.append(entry.getValue()).append("&");
}
if (!builder.isEmpty()) {
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
return null;
}
private static final ConnectionPool connectionPool = new ConnectionPool(5,
5, TimeUnit.MINUTES);
public interface BodyHandler<D> {
D toBody(String body) throws Throwable;
}
private static OkHttpClient getClient(long connectTimeout, long readWriteTimeout) {
return new OkHttpClient.Builder()
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.readTimeout(readWriteTimeout, TimeUnit.MILLISECONDS)
.writeTimeout(readWriteTimeout, TimeUnit.MILLISECONDS)
.connectionPool(connectionPool)
.retryOnConnectionFailure(true)
.followRedirects(false)
.build();
}
/**
* SSH 隧道配置(通过动态 SOCKS5 转发发起网络请求)
* - 适用于 HTTP/HTTPSHTTPS 不会因改为 localhost 而导致证书/SNI 失败)
* - 认证方式支持:密码 / 公钥 / 密码+公钥
*/
@SuppressWarnings({"LombokGetterMayBeUsed", "UnusedReturnValue"})
public static class SshTunnelConfig {
private String host;
private Integer port = 22;
private String username;
private AuthMethod authMethod = AuthMethod.PASSWORD;
private String password;
/**
* 私钥内容PEM/OpenSSH 文本)
*/
private String publicKey;
/**
* SSH 连接超时(毫秒)
*/
private Integer connectTimeoutMs = 10_000;
public enum AuthMethod {
PASSWORD,
PUBLIC_KEY,
PASSWORD_AND_PUBLIC_KEY
}
public String getHost() {
return host;
}
public SshTunnelConfig setHost(String host) {
this.host = host;
return this;
}
public Integer getPort() {
return port;
}
public SshTunnelConfig setPort(Integer port) {
this.port = port;
return this;
}
public String getUsername() {
return username;
}
public SshTunnelConfig setUsername(String username) {
this.username = username;
return this;
}
public AuthMethod getAuthMethod() {
return authMethod;
}
public SshTunnelConfig setAuthMethod(AuthMethod authMethod) {
this.authMethod = authMethod;
return this;
}
public String getPassword() {
return password;
}
public SshTunnelConfig setPassword(String password) {
this.password = password;
return this;
}
public String getPublicKey() {
return publicKey;
}
public SshTunnelConfig setPublicKey(String publicKey) {
this.publicKey = publicKey;
return this;
}
public Integer getConnectTimeoutMs() {
return connectTimeoutMs;
}
public SshTunnelConfig setConnectTimeoutMs(Integer connectTimeoutMs) {
this.connectTimeoutMs = connectTimeoutMs;
return this;
}
void validate() throws HttpException {
if (!StringUtils.hasText(host)) {
throw new HttpException("SSH隧道配置错误host不能为空");
}
if (port == null || port <= 0) {
throw new HttpException("SSH隧道配置错误port不合法");
}
if (!StringUtils.hasText(username)) {
throw new HttpException("SSH隧道配置错误username不能为空");
}
if (authMethod == null) {
throw new HttpException("SSH隧道配置错误authMethod不能为空");
}
if (authMethod == AuthMethod.PASSWORD || authMethod == AuthMethod.PASSWORD_AND_PUBLIC_KEY) {
if (!StringUtils.hasText(password)) {
throw new HttpException("SSH隧道配置错误password不能为空");
}
}
if (authMethod == AuthMethod.PUBLIC_KEY || authMethod == AuthMethod.PASSWORD_AND_PUBLIC_KEY) {
if (!StringUtils.hasText(publicKey)) {
throw new HttpException("SSH隧道配置错误publicKey不能为空");
}
}
}
}
private static final ConcurrentHashMap<String, TunnelRef> TUNNEL_POOL = new ConcurrentHashMap<>();
private static final ReentrantLock TUNNEL_CREATE_LOCK = new ReentrantLock();
private static class TunnelRef {
final AtomicInteger refCount = new AtomicInteger(0);
volatile SshClient client;
volatile ClientSession session;
volatile int socksPort;
volatile boolean started;
}
private static class TunnelLease implements AutoCloseable {
private final String key;
private final Proxy proxy;
private boolean closed;
TunnelLease(String key, Proxy proxy) {
this.key = key;
this.proxy = proxy;
}
Proxy getProxy() {
return proxy;
}
@Override
public void close() {
if (closed) {
return;
}
closed = true;
TunnelRef ref = TUNNEL_POOL.get(key);
if (ref == null) {
return;
}
int left = ref.refCount.decrementAndGet();
if (left > 0) {
return;
}
// 关闭会话并移除
TUNNEL_CREATE_LOCK.lock();
try {
TunnelRef now = TUNNEL_POOL.get(key);
if (now != null && now.refCount.get() <= 0) {
try {
if (now.session != null) {
now.session.close();
}
} catch (Throwable ignored) {
}
try {
if (now.client != null) {
now.client.stop();
}
} catch (Throwable ignored) {
}
TUNNEL_POOL.remove(key);
}
} finally {
TUNNEL_CREATE_LOCK.unlock();
}
}
}
private static TunnelLease acquireSocksTunnel(SshTunnelConfig cfg) throws HttpException {
cfg.validate();
String key = buildTunnelKey(cfg);
TunnelRef ref = TUNNEL_POOL.computeIfAbsent(key, k -> new TunnelRef());
ref.refCount.incrementAndGet();
try {
if (!ref.started || ref.session == null || !ref.session.isOpen()) {
startTunnelIfNeeded(ref, cfg);
}
Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", ref.socksPort));
return new TunnelLease(key, proxy);
} catch (Throwable e) {
// 启动失败,回滚引用计数并尽量清理
try {
int left = ref.refCount.decrementAndGet();
if (left <= 0) {
TUNNEL_POOL.remove(key);
}
} catch (Throwable ignored) {
}
if (e instanceof HttpException) {
throw (HttpException) e;
}
throw new HttpException("建立SSH隧道失败" + e.getMessage(), e);
}
}
private static void startTunnelIfNeeded(TunnelRef ref, SshTunnelConfig cfg) throws HttpException {
TUNNEL_CREATE_LOCK.lock();
try {
if (ref.started && ref.session != null && ref.session.isOpen()) {
return;
}
// 清理旧会话
try {
if (ref.session != null) {
ref.session.close();
}
} catch (Throwable ignored) {
}
try {
if (ref.client != null && ref.client.isOpen()) {
ref.client.stop();
}
} catch (Throwable ignored) {
}
SshClient client = SshClient.setUpDefaultClient();
client.start();
ClientSession session = null;
try {
session = client.connect(cfg.username, cfg.host, cfg.port)
.verify((cfg.connectTimeoutMs == null ? 10_000 : cfg.connectTimeoutMs))
.getSession();
// public key
if (cfg.authMethod == SshTunnelConfig.AuthMethod.PUBLIC_KEY
|| cfg.authMethod == SshTunnelConfig.AuthMethod.PASSWORD_AND_PUBLIC_KEY) {
Path keyFile = writeTempKeyFile(cfg.publicKey);
FilePasswordProvider provider = FilePasswordProvider.EMPTY;
if (StringUtils.hasText(cfg.password)) {
provider = (sessionContext, resourceKey, retryIndex) -> cfg.password;
}
try (var in = Files.newInputStream(keyFile)) {
Iterable<java.security.KeyPair> keyPairs = SecurityUtils.loadKeyPairIdentities(
session,
new PathResource(keyFile),
in,
provider
);
for (java.security.KeyPair kp : keyPairs) {
session.addPublicKeyIdentity(kp);
}
}
}
// password
if (cfg.authMethod == SshTunnelConfig.AuthMethod.PASSWORD
|| cfg.authMethod == SshTunnelConfig.AuthMethod.PASSWORD_AND_PUBLIC_KEY) {
session.addPasswordIdentity(cfg.password);
}
session.auth().verify((cfg.connectTimeoutMs == null ? 10_000 : cfg.connectTimeoutMs));
// 动态转发SOCKS5
SshdSocketAddress local = new SshdSocketAddress("127.0.0.1", 0);
SshdSocketAddress bound = session.startDynamicPortForwarding(local);
ref.client = client;
ref.session = session;
ref.socksPort = bound.getPort();
ref.started = true;
} catch (Throwable e) {
try {
if (session != null) {
session.close();
}
} catch (Throwable ignored) {
}
try {
client.stop();
} catch (Throwable ignored) {
}
throw new HttpException("建立SSH隧道失败" + e.getMessage(), e);
}
} finally {
TUNNEL_CREATE_LOCK.unlock();
}
}
private static int allocateFreePort() throws HttpException {
try (ServerSocket ss = new ServerSocket(0)) {
ss.setReuseAddress(true);
return ss.getLocalPort();
} catch (IOException e) {
throw new HttpException("分配本地端口失败:" + e.getMessage(), e);
}
}
private static Path writeTempKeyFile(String keyText) throws HttpException {
try {
Path tmp = Files.createTempFile("ssh-key-", ".key");
Files.writeString(tmp, keyText, StandardCharsets.UTF_8);
tmp.toFile().deleteOnExit();
return tmp;
} catch (IOException e) {
throw new HttpException("写入临时私钥文件失败:" + e.getMessage(), e);
}
}
private static String buildTunnelKey(SshTunnelConfig cfg) {
// 避免把私钥全文做 key这里只做基本区分足够用于复用/隔离
int keyHash = cfg.publicKey == null ? 0 : cfg.publicKey.hashCode();
return cfg.host + ":" + cfg.port + ":" + cfg.username + ":" + cfg.authMethod + ":" + keyHash;
}
/**
* POST请求并转化为指定响应值
*
* @param url 请求地址
* @param jsonValue JSON请求体
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @param bodyHandler 请求结果处理器
* @param <D> 响应结果类型
* @return 响应结果示例
* @throws HttpException 异常
*/
public static <D> D postRequest(String url, String jsonValue, Map<String, String> headers, long connectTimeout, long readWriteTimeout, BodyHandler<D> bodyHandler) throws HttpException {
return postRequest(url, jsonValue, headers, connectTimeout, readWriteTimeout, bodyHandler, null);
}
/**
* POST请求并转化为指定响应值
*
* @param url 请求地址
* @param jsonValue JSON请求体
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @param bodyHandler 请求结果处理器
* @param sshTunnelConfig SSH隧道配置可为空
* @param <D> 响应结果类型
* @return 响应结果示例
* @throws HttpException 异常
*/
public static <D> D postRequest(String url, String jsonValue, Map<String, String> headers, long connectTimeout, long readWriteTimeout, BodyHandler<D> bodyHandler, SshTunnelConfig sshTunnelConfig) throws HttpException {
String bodyString = postString(url, jsonValue, headers, connectTimeout, readWriteTimeout, sshTunnelConfig);
try {
return bodyHandler.toBody(bodyString);
} catch (Throwable e) {
throw new HttpException("解析请求响应结果失败", e);
}
}
/**
* GET请求并转化为指定响应值
*
* @param url 请求地址
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @param bodyHandler 请求结果处理器
* @param <D> 响应结果类型
* @return 响应结果示例
* @throws HttpException 异常
*/
public static <D> D getRequest(String url, Map<String, String> headers, long connectTimeout, long readWriteTimeout, BodyHandler<D> bodyHandler) throws HttpException {
return getRequest(url, headers, connectTimeout, readWriteTimeout, bodyHandler, null);
}
/**
* GET请求并转化为指定响应值
*
* @param url 请求地址
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @param bodyHandler 请求结果处理器
* @param sshTunnelConfig SSH隧道配置可为空
* @param <D> 响应结果类型
* @return 响应结果示例
* @throws HttpException 异常
*/
public static <D> D getRequest(String url, Map<String, String> headers, long connectTimeout, long readWriteTimeout, BodyHandler<D> bodyHandler, SshTunnelConfig sshTunnelConfig) throws HttpException {
String bodyString = getString(url, headers, connectTimeout, readWriteTimeout, sshTunnelConfig);
try {
return bodyHandler.toBody(bodyString);
} catch (Throwable e) {
throw new HttpException("解析请求响应结果失败", e);
}
}
/**
* POST请求JSON体
*
* @param url 请求地址
* @param jsonValue JSON请求体
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @return 响应体字符串
* @throws RuntimeException 请求失败时抛出
*/
public static String postString(String url, String jsonValue, Map<String, String> headers, long connectTimeout, long readWriteTimeout) throws HttpException {
return postString(url, jsonValue, headers, connectTimeout, readWriteTimeout, null);
}
/**
* POST请求JSON体
*
* @param url 请求地址
* @param jsonValue JSON请求体
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @param sshTunnelConfig SSH隧道配置可为空
* @return 响应体字符串
* @throws RuntimeException 请求失败时抛出
*/
public static String postString(String url, String jsonValue, Map<String, String> headers, long connectTimeout, long readWriteTimeout, SshTunnelConfig sshTunnelConfig) throws HttpException {
if (!StringUtils.hasText(url)) {
throw new HttpException("POST请求失败请求地址不能为空");
}
RequestBody requestBody = RequestBody.create(
StringUtils.hasText(jsonValue) ? jsonValue : "",
MediaType.parse("application/json; charset=utf-8")
);
Request request = buildRequest(url, "POST", requestBody, headers);
return executeRequest(request, connectTimeout, readWriteTimeout, sshTunnelConfig);
}
/**
* GET请求
*
* @param url 请求地址
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @return 响应体字符串
* @throws RuntimeException 请求失败时抛出
*/
public static String getString(String url, Map<String, String> headers, long connectTimeout, long readWriteTimeout) throws HttpException {
return getString(url, headers, connectTimeout, readWriteTimeout, null);
}
/**
* GET请求
*
* @param url 请求地址
* @param headers 自定义请求头
* @param connectTimeout 连接超时时间
* @param readWriteTimeout 读写超时时间
* @param sshTunnelConfig SSH隧道配置可为空
* @return 响应体字符串
* @throws RuntimeException 请求失败时抛出
*/
public static String getString(String url, Map<String, String> headers, long connectTimeout, long readWriteTimeout, SshTunnelConfig sshTunnelConfig) throws HttpException {
if (!StringUtils.hasText(url)) {
throw new RuntimeException("GET请求失败请求地址不能为空");
}
Request request = buildRequest(url, "GET", null, headers);
return executeRequest(request, connectTimeout, readWriteTimeout, sshTunnelConfig);
}
/**
* 构建请求对象统一处理Method、Header、Cookie、请求体
*/
private static Request buildRequest(String url, String method, RequestBody requestBody,
Map<String, String> headers) {
Request.Builder requestBuilder = new Request.Builder().url(url);
switch (method.toUpperCase()) {
case "POST":
requestBuilder.post(requestBody);
break;
case "GET":
requestBuilder.get();
break;
default:
throw new RuntimeException("不支持的请求方法:" + method);
}
requestBuilder.header("Connection", "Keep-Alive")
.header("Charset", "UTF-8");
if (headers != null && !headers.isEmpty()) {
headers.forEach(requestBuilder::header);
}
return requestBuilder.build();
}
/**
* 执行请求并处理响应/异常(核心执行逻辑)
*/
private static String executeRequest(Request request, long connectTimeout, long readWriteTimeout) throws HttpException {
return executeRequest(request, connectTimeout, readWriteTimeout, null);
}
private static String executeRequest(Request request, long connectTimeout, long readWriteTimeout, SshTunnelConfig sshTunnelConfig) throws HttpException {
TunnelLease lease = null;
try {
OkHttpClient.Builder builder = getClient(connectTimeout, readWriteTimeout).newBuilder();
if (sshTunnelConfig != null) {
lease = acquireSocksTunnel(sshTunnelConfig);
builder.proxy(lease.getProxy());
}
OkHttpClient httpClient = builder.build();
Response response = httpClient.newCall(request).execute();
if (!response.isSuccessful()) {
throw new HttpException(String.format("请求失败,状态码:%d请求地址%s",
response.code(), request.url()));
}
ResponseBody responseBody = response.body();
return responseBody == null ? "" : responseBody.string();
} catch (IOException e) {
throw new HttpException(String.format("请求IO异常地址%s原因%s", request.url(), e.getMessage()), e);
} catch (Exception e) {
throw new HttpException(String.format("请求未知异常,地址:%s原因%s", request.url(), e.getMessage()), e);
} finally {
if (lease != null) {
try {
lease.close();
} catch (Throwable ignored) {
}
}
}
}
public static class HttpException extends Throwable {
public HttpException(String message) {
super(message);
}
public HttpException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@@ -0,0 +1,152 @@
package com.iqudoo.framework.tape.modules.utils;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqudoo.framework.tape.base.action.bean.ActionState;
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicReference;
public class JsonUtil {
public static class JsonThrowable extends ActionState {
public JsonThrowable(String message) {
super(488, message);
}
}
private static final AtomicReference<ObjectMapper> MAPPER = new AtomicReference<>(null);
/**
* getMapper
*
* @return ObjectMapper
*/
private static ObjectMapper getMapper() {
if (MAPPER.get() == null) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MAPPER.set(objectMapper);
}
return MAPPER.get();
}
/**
* 合并JSON
*
* @return JSONObject
*/
public static JSONObject mergedJson(JSONObject target, JSONObject... jsonValues) {
// 合并json1和json2
if (jsonValues != null && jsonValues.length > 0) {
JSONObject mergedJson = new JSONObject();
for (String key : target.keySet()) {
mergedJson.put(key, target.get(key));
}
for (JSONObject srcJson : jsonValues) {
if (srcJson != null) {
for (String key : srcJson.keySet()) {
mergedJson.put(key, srcJson.get(key));
}
}
}
return mergedJson;
}
return target;
}
/**
* 将字符串转化为JSONObject
*
* @param data 字符串
* @return JSONObject
*/
public static JSONObject toJSONObject(String data) {
JSONObject jsonObject = null;
try {
jsonObject = JSONObject.parseObject(data);
} catch (Throwable ignored) {
}
if (jsonObject == null) {
jsonObject = new JSONObject();
}
return jsonObject;
}
/**
* 将对象转化为json字符串
*
* @param data 对象
* @return json字符串
*/
public static String toJSONString(Object data) throws Throwable {
return getMapper().writeValueAsString(data);
}
/**
* 将json转化为单个对象
*
* @param jsonData json字符串
* @param javaType 类的类型
* @return 对象
*/
public static <T> T toBeanObject(String jsonData, JavaType javaType) throws Throwable {
try {
return getMapper().readValue(jsonData, javaType);
} catch (Throwable throwable) {
throw new JsonThrowable(jsonData + " can not cast to " + javaType.getTypeName());
}
}
/**
* 将json转化为单个对象
*
* @param jsonData json字符串
* @param beanType 类的类型
* @return 对象
*/
public static <T> T toBeanObject(String jsonData, Type beanType) throws Throwable {
JavaType javaType = constructJavaType(beanType);
try {
return getMapper().readValue(jsonData, javaType);
} catch (Throwable throwable) {
throw new JsonThrowable(jsonData + " can not cast to " + javaType.getTypeName());
}
}
/**
* JSON字符串转化为泛型对象
*
* @param jsonData json字符串
* @param rawType 类型的类
* @param parametricTypes 泛型的类
* @param <T> 键的泛型
* @return 泛型对象
*/
public static <T> T toBeanObjectParametric(String jsonData, Class<T> rawType, Class<?>... parametricTypes) throws Throwable {
try {
JavaType javaType = getMapper().getTypeFactory()
.constructParametricType(rawType, parametricTypes);
return getMapper().readValue(jsonData, javaType);
} catch (Throwable throwable) {
throw new JsonThrowable(jsonData + " can not cast to " + rawType.getTypeName());
}
}
/**
* 类型转化为JavaType
*
* @param type 类型
* @return JavaType
*/
public static JavaType constructJavaType(Type type) {
return getMapper().getTypeFactory().constructType(type);
}
}

View File

@@ -0,0 +1,156 @@
package com.iqudoo.framework.tape.modules.utils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
@SuppressWarnings("unused")
public class LogUtil {
private static final String LOG_DIR = "." + File.separator + "runtime" + File.separator + "logs";
private static final Map<String, String> tempLogMap = new HashMap<>();
// 配置文件数量阈值(超过此数量触发清理)
private static final int MAX_LOG_FILES = 200;
// 清理时保留的文件数量删除最旧的保留最新的80%
private static final int KEEP_FILES_COUNT = 160;
public static String readLog(String id) {
// 构建日志文件路径
File logFile = new File(LOG_DIR, id + ".log");
// 检查文件是否存在
if (!logFile.exists()) {
return "";
}
// 读取文件内容
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream(logFile),
StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (!content.isEmpty()) {
content.append("\n");
}
content.append(line);
}
} catch (IOException e) {
return "";
}
return content.toString();
}
/**
* 防抖打印日志30ms 合并输出减少IO
*
* @param id 日志唯一标识生成文件名id.log
* @param logContent 日志内容
*/
public static void printLog(String id, String logContent) {
// 加锁保证线程安全
synchronized (tempLogMap) {
String log = tempLogMap.getOrDefault(id, "");
// 拼接新日志(换行分隔)
log = log + (log.isEmpty() ? "" : "\n") + logContent;
tempLogMap.put(id, log);
}
// 30ms 防抖执行写入
DebounceUtil.debounce("log-util-" + id, 30, new DebounceUtil.DebounceRunner() {
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
public void onRun() throws Throwable {
String appendLog;
// 原子性取出并清空缓存
synchronized (tempLogMap) {
appendLog = tempLogMap.remove(id);
}
// 无内容直接返回
if (appendLog == null || appendLog.isEmpty()) {
return;
}
// 1. 创建日志目录(不存在则创建)
File dir = new File(LOG_DIR);
if (!dir.exists()) {
dir.mkdirs();
}
// 2. 清理旧日志文件(如果文件数量超过阈值)
cleanOldLogFilesIfNeeded(dir);
// 3. 日志文件
File logFile = new File(dir, id + ".log");
// 4. 追加写入文件UTF-8自动创建文件
try (FileOutputStream fos = new FileOutputStream(logFile, true);
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
PrintWriter pw = new PrintWriter(osw)) {
pw.println(appendLog);
pw.flush();
}
}
});
}
/**
* 清理旧的日志文件(当文件数量超过阈值时)
*
* @param logDir 日志目录
*/
private static void cleanOldLogFilesIfNeeded(File logDir) {
File[] logFiles = logDir.listFiles((dir, name) -> name.endsWith(".log"));
if (logFiles == null || logFiles.length <= MAX_LOG_FILES) {
return;
}
// 异步清理,避免阻塞日志写入
new Thread(() -> {
synchronized (LogUtil.class) {
try {
// 二次检查(防止并发清理)
File[] currentFiles = logDir.listFiles((dir, name) -> name.endsWith(".log"));
if (currentFiles == null || currentFiles.length <= MAX_LOG_FILES) {
return;
}
// 按最后修改时间排序(最早的在前)
List<File> sortedFiles = Arrays.stream(currentFiles)
.sorted(Comparator.comparingLong(File::lastModified))
.toList();
// 计算需要删除的文件数量
int filesToDelete = sortedFiles.size() - KEEP_FILES_COUNT;
if (filesToDelete <= 0) {
return;
}
// 删除最旧的 N 个文件
for (int i = 0; i < filesToDelete; i++) {
File fileToDelete = sortedFiles.get(i);
try {
Files.deleteIfExists(fileToDelete.toPath());
} catch (IOException e) {
// 删除失败时记录错误但不影响主流程
System.err.println("Failed to delete old log file: " + fileToDelete.getName());
}
}
} catch (Exception e) {
// 清理失败不影响日志写入
System.err.println("Error during log cleanup: " + e.getMessage());
}
}
}, "LogCleanup-Thread").start();
}
/**
* 手动触发日志清理(可在应用启动时调用)
*/
public static void manualCleanup() {
File dir = new File(LOG_DIR);
if (dir.exists()) {
cleanOldLogFilesIfNeeded(dir);
}
}
}

View File

@@ -0,0 +1,12 @@
package com.iqudoo.framework.tape.modules.utils;
public class ObjectUtil {
public static <T, D> D objectCopy(T sourceObject, Class<D> targetClass) throws Throwable {
// 1. 将对象转换为JSON字符串
String json = JsonUtil.toJSONString(sourceObject);
// 2. 将JSON字符串转换为对象
return JsonUtil.toBeanObject(json, targetClass);
}
}

View File

@@ -0,0 +1,78 @@
package com.iqudoo.framework.tape.modules.utils;
import java.util.regex.Pattern;
public class PatternUtil {
public static class PatternInfo {
Pattern regex;
String errorMsg;
boolean isPositive;
public PatternInfo(String regex, String errorMsg) {
this.regex = Pattern.compile(regex);
this.errorMsg = errorMsg;
this.isPositive = true;
}
public PatternInfo(String regex, String errorMsg, boolean isPositive) {
this.regex = Pattern.compile(regex);
this.errorMsg = errorMsg;
this.isPositive = isPositive;
}
public PatternInfo(Pattern regex, String errorMsg) {
this.regex = regex;
this.errorMsg = errorMsg;
this.isPositive = true;
}
public PatternInfo(Pattern regex, String errorMsg, boolean isPositive) {
this.regex = regex;
this.errorMsg = errorMsg;
this.isPositive = isPositive;
}
public Pattern getRegex() {
return regex;
}
public void setRegex(Pattern regex) {
this.regex = regex;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public boolean isPositive() {
return isPositive;
}
public void setPositive(boolean positive) {
isPositive = positive;
}
}
public static void validateString(String value, PatternInfo... patternInfos) {
for (PatternInfo patternInfo : patternInfos) {
if (patternInfo.getRegex() == null) {
continue;
}
if (value == null) {
throw new IllegalArgumentException(patternInfo.getErrorMsg());
}
boolean isMatch = patternInfo.getRegex().matcher(value).matches();
if ((!isMatch && patternInfo.isPositive())
|| (isMatch && !patternInfo.isPositive())) {
throw new IllegalArgumentException(patternInfo.getErrorMsg());
}
}
}
}

View File

@@ -0,0 +1,170 @@
package com.iqudoo.framework.tape.modules.utils;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.software.os.NetworkParams;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import java.util.List;
/**
* 服务器性能信息获取工具类(修正版本)
*/
public class PerfUtils {
// 格式化数字,保留两位小数
private static final DecimalFormat DF = new DecimalFormat("#.00");
// 系统信息入口
private static final SystemInfo SI = new SystemInfo();
// 硬件抽象层
private static final HardwareAbstractionLayer HAL = SI.getHardware();
// 操作系统信息
private static final OperatingSystem OS = SI.getOperatingSystem();
/**
* 获取JVM内存使用信息
*
* @return 内存信息字符串
*/
public static String getJvmMemoryInfo() {
try {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); // 堆内存
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); // 非堆内存
return "=== JVM 内存信息 ===\n" +
"堆内存总量: " + formatBytes(heapUsage.getInit()) + "\n" +
"堆内存已使用: " + formatBytes(heapUsage.getUsed()) + "\n" +
"堆内存最大值: " + formatBytes(heapUsage.getMax()) + "\n" +
"堆内存使用率: " + DF.format((double) heapUsage.getUsed() / heapUsage.getMax() * 100) + "%\n" +
"非堆内存已使用: " + formatBytes(nonHeapUsage.getUsed()) + "\n"
+ "\n";
} catch (Throwable throwable) {
return "";
}
}
/**
* 获取系统内存信息(物理内存)
*
* @return 内存信息字符串
*/
public static String getSystemMemoryInfo() {
try {
oshi.hardware.GlobalMemory memory = HAL.getMemory();
long total = memory.getTotal();
long available = memory.getAvailable();
long used = total - available;
return "=== 物理内存信息 ===\n" +
"物理内存总量: " + formatBytes(total) + "\n" +
"物理内存已使用: " + formatBytes(used) + "\n" +
"物理可用内存: " + formatBytes(available) + "\n" +
"物理内存使用率: " + DF.format((double) used / total * 100) + "%\n"
+ "\n";
} catch (Throwable throwable) {
return "";
}
}
/**
* 获取CPU使用信息修正方法参数问题
*
* @return CPU信息字符串
*/
public static String getCpuInfo() {
try {
CentralProcessor processor = HAL.getProcessor();
// 获取CPU核心数、型号等基础信息
StringBuilder sb = new StringBuilder();
sb.append("=== CPU 信息 ===\n");
sb.append("CPU型号: ").append(processor.getProcessorIdentifier().getName()).append("\n");
sb.append("物理核心数: ").append(processor.getPhysicalProcessorCount()).append("\n");
sb.append("逻辑核心数: ").append(processor.getLogicalProcessorCount()).append("\n");
// 1. 获取总体CPU使用率单参数版本无格式问题
long[] prevTicks = processor.getSystemCpuLoadTicks();
Util.sleep(1000); // 休眠1秒确保数据有效
double cpuTotalLoad = processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100;
sb.append("CPU使用率: ").append(DF.format(cpuTotalLoad)).append("%\n");
// 2. 获取各核心使用率改用getProcessorCpuLoad()方法,避免二维数组校验问题)
double[] coreLoads = processor.getProcessorCpuLoad(1000); // 直接获取1秒内各核心使用率
for (int i = 0; i < coreLoads.length; i++) {
sb.append("CPU核心").append(i + 1).append(": ")
.append(DF.format(coreLoads[i] * 100))
.append("%\n");
}
sb.append("\n");
return sb.toString();
} catch (Throwable throwable) {
return "";
}
}
/**
* 获取网络信息IP、网卡、收发字节数等
*
* @return 网络信息字符串
*/
public static String getNetworkInfo() {
try {
// 网络参数IP、网关、DNS等
NetworkParams networkParams = OS.getNetworkParams();
// 获取所有网卡信息,避免下标越界
List<NetworkIF> networkIFs = HAL.getNetworkIFs();
StringBuilder sb = new StringBuilder();
sb.append("=== 网络信息 ===\n");
sb.append("主机名: ").append(networkParams.getHostName()).append("\n");
sb.append("DNS服务器: ").append(String.join(",", networkParams.getDnsServers())).append("\n");
// 遍历网卡,展示每个网卡的收发数据
for (int i = 0; i < networkIFs.size(); i++) {
NetworkIF netIf = networkIFs.get(i);
sb.append("网卡").append(i).append("(").append(netIf.getName()).append("): ")
.append("接收: ").append(formatBytes(netIf.getBytesRecv()))
.append(" | 发送: ").append(formatBytes(netIf.getBytesSent())).append("\n");
}
sb.append("\n");
return sb.toString();
} catch (Throwable throwable) {
return "";
}
}
/**
* 统一入口:获取所有服务器性能信息
*
* @return 所有性能信息汇总
*/
public static String getAllServerPerformanceInfo() {
return getJvmMemoryInfo() + "\n" +
getSystemMemoryInfo() + "\n" +
getCpuInfo() + "\n" +
getNetworkInfo();
}
/**
* 字节数格式化转换为KB/MB/GB/TB
*
* @param bytes 原始字节数
* @return 格式化后的字符串
*/
private static String formatBytes(long bytes) {
if (bytes < 0) return "0 B"; // 处理负数情况
if (bytes < 1024) {
return bytes + " B";
} else if (bytes < 1024 * 1024) {
return DF.format((double) bytes / 1024) + " KB";
} else if (bytes < 1024 * 1024 * 1024) {
return DF.format((double) bytes / (1024 * 1024)) + " MB";
} else if (bytes < 1024 * 1024 * 1024 * 1024L) {
return DF.format((double) bytes / (1024 * 1024 * 1024)) + " GB";
} else {
return DF.format((double) bytes / (1024 * 1024 * 1024 * 1024L)) + " TB";
}
}
}

View File

@@ -0,0 +1,34 @@
package com.iqudoo.framework.tape.modules.utils;
import java.util.List;
import java.util.Random;
public class RandomUtil {
public static int getRandomInt(int maxSize) {
Random random = new Random();
return random.nextInt(maxSize);
}
public static String getRandomString(int length) {
return getRandomString(length, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
}
public static String getRandomString(int length, String str) {
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
int number = random.nextInt(str.length());
sb.append(str.charAt(number));
}
return sb.toString();
}
public static <T> T getRandom(List<T> list) {
if (list == null || list.isEmpty()) {
return null;
}
return list.get(getRandomInt(list.size()));
}
}

Some files were not shown because too many files have changed in this diff Show More