init commit
This commit is contained in:
0
.eslintignore
Normal file
0
.eslintignore
Normal file
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal 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
|
||||||
151
ddl.sql
Normal file
151
ddl.sql
Normal 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
388
pom.xml
Normal 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>
|
||||||
BIN
src/lib/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar
Normal file
BIN
src/lib/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
172
src/main/java/com/iqudoo/framework/tape/Tape.java
Normal file
172
src/main/java/com/iqudoo/framework/tape/Tape.java
Normal 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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 "";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.iqudoo.framework.tape.base.action.handler;
|
||||||
|
|
||||||
|
public abstract class DefaultActionHandler extends SimpleActionHandler<Object> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<?>> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 "";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 "";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
68
src/main/java/com/iqudoo/framework/tape/modules/cache/Cache.java
vendored
Normal file
68
src/main/java/com/iqudoo/framework/tape/modules/cache/Cache.java
vendored
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
src/main/java/com/iqudoo/framework/tape/modules/cache/CacheKey.java
vendored
Normal file
52
src/main/java/com/iqudoo/framework/tape/modules/cache/CacheKey.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
22
src/main/java/com/iqudoo/framework/tape/modules/cache/CacheLoader.java
vendored
Normal file
22
src/main/java/com/iqudoo/framework/tape/modules/cache/CacheLoader.java
vendored
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.iqudoo.framework.tape.modules.excel;
|
||||||
|
|
||||||
|
public class BaseEasyExcelIgnoreError extends Throwable {
|
||||||
|
|
||||||
|
public BaseEasyExcelIgnoreError(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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] >= 'A' && c[i] <= 'Z') c[i] -= 65248;
|
||||||
|
else if (c[i] >= 'a' && c[i] <= 'z') c[i] -= 65248;
|
||||||
|
else if (c[i] >= '0' && c[i] <= '9') 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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/HTTPS(HTTPS 不会因改为 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
Reference in New Issue
Block a user