路泰机电科技体检——数据平台后端
zhaowenxuan
2024-12-17 965a994c0fe4c59a72638a6a4f1bb28dfd66f655
20241217
5个文件已修改
36个文件已添加
1个文件已删除
5686 ■■■■■ 已修改文件
.gitignore 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/config/DruidConfig.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/config/RedisConfig.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/constant/CacheConstants.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/constant/Constants.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/constant/UserConstants.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/controller/xian/MeiJiController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/datasource/DynamicDataSource.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/datasource/DynamicDataSourceContextHolder.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/BaseEntity.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/SysConfig.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/TjAdvice.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/TjAskWorkLog.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/TjConsumables.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/TjCustomer.java 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/TjProject.java 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/domain/TjStandard.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/dto/xian/meiji/CheXiaoMzFyDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/dto/xian/meiji/CreateMenZhenFyDto.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/dto/xian/meiji/FeiYongIdDto.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/dto/xian/meiji/FeiYongMxDto.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/enums/DataSourceType.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/exception/ServiceException.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/mapper/SysConfigMapper.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/service/ISysConfigService.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/service/impl/SysConfigServiceImpl.java 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/service/xian/MeiJiService.java 471 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/FastJson2JsonRedisSerializer.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/HttpClientUtils.java 574 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/RedisCache.java 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/SpringUtils.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/StringUtils.java 542 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/text/CharsetKit.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/text/Convert.java 849 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/example/utils/text/StrFormatter.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-linux.yaml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-win.yaml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yaml 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/logback.xml 121 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/SysConfigMapper.xml 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -31,3 +31,6 @@
### VS Code ###
.vscode/
/log.path_IS_UNDEFINED/
/logs/
/src/main/resources/log.log
pom.xml
@@ -13,7 +13,7 @@
    <artifactId>ltkj_peis_sjpt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ltkj_peis_sjpt</name>
     <packaging>war</packaging>
     <packaging>jar</packaging>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
@@ -68,6 +68,36 @@
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.39</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
    </dependencies>
    <build>
src/main/java/com/example/config/DruidConfig.java
@@ -4,6 +4,9 @@
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.example.datasource.DynamicDataSource;
import com.example.enums.DataSourceType;
import com.example.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -106,6 +109,132 @@
        return druidProperties.dataSource(dataSource);
    }
    @Bean
//    @ConfigurationProperties("spring.datasource.druid.slavehis")
//    @ConditionalOnProperty(prefix = "spring.datasource.druid.slavehis", name = "enabled", havingValue = "true")
    public DataSource slaveHisDataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        Properties props = new Properties();
        try {
            // 从文件中读取配置信息
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(CONFIG_PATH);
            } catch (FileNotFoundException e) {
                log.info("数据库连接文件找不到!");
            }
            props.load(fis);
            fis.close();
            // 获取属性值并赋值
            Properties properties = new Properties();
            // 这里是测试写法,具体的value可以通过请求参数传递过来
            properties.setProperty("druid.enabled",props.getProperty("hisenabled"));
            properties.setProperty("druid.driverClassName","com.microsoft.sqlserver.jdbc.SQLServerDriver");
            properties.setProperty("druid.url","jdbc:sqlserver://"+props.getProperty("hisip")+":"+props.getProperty("hisprot")+";DatabaseName="+props.getProperty("hisname")+
                    ";&characterEncoding=utf8");
            properties.setProperty("druid.username",props.getProperty("hisusername"));
            properties.setProperty("druid.password",props.getProperty("hispassword"));
            dataSource.restart(properties);
            log.info("his数据库连接成功!!!");
        } catch (Exception e) {
            log.info("数据库连接失败  请联系管理员!");
            e.printStackTrace();
        }
        return druidProperties.dataSource(dataSource);
    }
    @Bean
//    @ConfigurationProperties("spring.datasource.druid.slavelis")
//    @ConditionalOnProperty(prefix = "spring.datasource.druid.slavelis", name = "enabled", havingValue = "true")
    public DataSource slaveDataLisSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        Properties props = new Properties();
        try {
            // 从文件中读取配置信息
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(CONFIG_PATH);
            } catch (FileNotFoundException e) {
                log.info("数据库连接文件找不到!");
            }
            props.load(fis);
            fis.close();
            // 获取属性值并赋值
            Properties properties = new Properties();
            // 这里是测试写法,具体的value可以通过请求参数传递过来
            properties.setProperty("druid.enabled",props.getProperty("lisenabled"));
            properties.setProperty("druid.url","jdbc:mysql://"+props.getProperty("lisip")+":"+props.getProperty("lisprot")+"/"+props.getProperty("lisname")+"" +
                    "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8");
            properties.setProperty("druid.username",props.getProperty("lisusername"));
            properties.setProperty("druid.password",props.getProperty("lispassword"));
            dataSource.restart(properties);
            log.info("数据库连接成功!!!");
        } catch (Exception e) {
            log.info("数据库连接失败  请联系管理员!");
            e.printStackTrace();
        }
        return druidProperties.dataSource(dataSource);
    }
    @Bean
//    @ConfigurationProperties("spring.datasource.druid.slavepacs")
//    @ConditionalOnProperty(prefix = "spring.datasource.druid.slavepacs", name = "enabled", havingValue = "true")
    public DataSource slaveDataPacsSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        Properties props = new Properties();
        try {
            // 从文件中读取配置信息
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(CONFIG_PATH);
            } catch (FileNotFoundException e) {
                log.info("数据库连接文件找不到");
            }
            props.load(fis);
            fis.close();
            // 获取属性值并赋值
            Properties properties = new Properties();
            // 这里是测试写法,具体的value可以通过请求参数传递过来
            properties.setProperty("druid.enabled",props.getProperty("pacsenabled"));
            properties.setProperty("druid.driverClassName","oracle.jdbc.OracleDriver");
            properties.setProperty("druid.url","jdbc:oracle:thin:@//"+props.getProperty("pacsip")+"/"+props.getProperty("pacsname"));
            properties.setProperty("druid.username",props.getProperty("pacsusername"));
            properties.setProperty("druid.password",props.getProperty("pacspassword"));
            dataSource.restart(properties);
            log.info("数据库连接成功!!!");
        } catch (Exception e) {
            log.info("数据库连接失败  请联系管理员!");
            e.printStackTrace();
        }
        return druidProperties.dataSource(dataSource);
    }
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE_HIS.name(), "slaveHisDataSource");
        setDataSource(targetDataSources, DataSourceType.SLAVE_LIS.name(), "slaveDataLisSource");
        setDataSource(targetDataSources, DataSourceType.SLAVE_PACS.name(), "slaveDataPacsSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }
    /**
     * 设置数据源
     *
     * @param targetDataSources 备选数据源集合
     * @param sourceName        数据源名称
     * @param beanName          bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
        try {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        } catch (Exception e) {
        }
    }
    /**
     * 去除监控页面底部的广告
     */
src/main/java/com/example/config/RedisConfig.java
New file
@@ -0,0 +1,175 @@
package com.example.config;
import com.example.utils.FastJson2JsonRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.io.*;
import java.util.Properties;
/**
 * redis配置
 *
 * @author ltkj
 */
@Configuration
@EnableCaching
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {
    @Value ("${config.path}")
    private String url;
    @Value ("${config.dir}")
    private  String path;
    @Bean
    @SuppressWarnings(value = {"unchecked", "rawtypes"})
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置JedisPoolConfig的相关参数,例如最大连接数、最大空闲时间等
//        config.setMinIdle(0);
//        config.setMaxIdle(8);
//        config.setMaxTotal(8);
//        config.setMaxWaitMillis(-1);
//        config.setTestOnBorrow(true);
//        config.setTestOnReturn(true);
        return config;
    }
    @Bean
    public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        // 从文件中读取配置信息
        try {
            FileInputStream fis = null;
            Properties props = new Properties();
            try {
                fis = new FileInputStream(url);
            } catch (FileNotFoundException e) {
                log.info("配置文件找不到 系统正在创建!");
                File f = new File(path);
                if(!f.exists()){
                    f.mkdirs();
                }
                File file = new File(url);
                try {
                    FileWriter fileWriter = new FileWriter(file);
                    fileWriter.write("ip = 你的主数据库连接ip地址\n");
                    fileWriter.write("prot = 你的主数据库连接端口\n");
                    fileWriter.write("name = 你的主数据库连接名称\n");
                    fileWriter.write("username = 你的主数据库连接用户名\n");
                    fileWriter.write("password = 你的主数据库连接密码\n");
                    fileWriter.write("hisenabled = 是否开启 his 从库数据库连接 (从数据源开关/默认关闭 fales)\n");
                    fileWriter.write("\n");
                    fileWriter.write("hisip = 你的 his 数据库连接ip地址\n");
                    fileWriter.write("hisprot = 你的 his 数据库连接端口\n");
                    fileWriter.write("hisname = 你的 his 数据库连接名称\n");
                    fileWriter.write("hisusername = 你的 his 数据库连接用户名\n");
                    fileWriter.write("hispassword = 你的 his 数据库连接密码\n");
                    fileWriter.write("\n");
                    fileWriter.write("pacsenabled = 是否开启pacs从库数据库连接 (从数据源开关/默认关闭 fales)\n");
                    fileWriter.write("pacsip = 你的pacs数据库连接ip地址\n");
                    fileWriter.write("pacsprot = 你的pacs数据库连接端口\n");
                    fileWriter.write("pacsname = 你的pacs数据库连接名称\n");
                    fileWriter.write("pacsusername = 你的pacs数据库连接用户名\n");
                    fileWriter.write("pacspassword = 你的pacs数据库连接密码\n");
                    fileWriter.write("\n");
                    fileWriter.write("lisenabled = 是否开启 lis 从库数据库连接 (从数据源开关/默认关闭 fales)\n");
                    fileWriter.write("lisip = 你的 lis 数据库连接ip地址\n");
                    fileWriter.write("lisprot = 你的 lis 数据库连接端口\n");
                    fileWriter.write("lisname = 你的 lis 数据库连接名称\n");
                    fileWriter.write("lisusername = 你的 lis 数据库连接用户名\n");
                    fileWriter.write("lispassword = 你的 lis 数据库连接密码\n");
                    fileWriter.write("redisIp = 你的redisIp地址\n");
                    fileWriter.write("redisProt = 你的redis端口\n");
                    fileWriter.write("redisIpDatabase = 你的redis链接库\n");
                    fileWriter.write("redisPassword = 你的redis密码\n");
                    fileWriter.write("\n");
                    fileWriter.close();
                    log.info("配置文件创建成功!");
                } catch (IOException ioException) {
                    log.info("配置文件创建失败  请联系管理员手动创建!");
                    ioException.printStackTrace();
                }
                e.printStackTrace();
            }
            props.load(fis);
            fis.close();
            // 获取属性值并赋值
            factory.setPoolConfig(jedisPoolConfig);
            // 设置Redis服务器的地址和端口号
            factory.setHostName(props.getProperty("redisIp"));
            factory.setPort(Integer.parseInt(props.getProperty("redisProt")));
            // 如果需要密码验证,设置密码
            factory.setPassword(props.getProperty("redisPassword"));
            // 设置其他参数,如数据库索引等
            factory.setDatabase(Integer.parseInt(props.getProperty("redisIpDatabase")));
            // 最后,初始化连接
            factory.afterPropertiesSet();
            log.info("redis连接成功!!!");
        } catch (IOException e) {
            log.info("redis连接失败  请联系管理员!");
            e.printStackTrace();
        }
        return factory;
    }
    @Bean
    public DefaultRedisScript<Long> limitScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(limitScriptText());
        redisScript.setResultType(Long.class);
        return redisScript;
    }
    /**
     * 限流脚本
     */
    private String limitScriptText() {
        return "local key = KEYS[1]\n" +
                "local count = tonumber(ARGV[1])\n" +
                "local time = tonumber(ARGV[2])\n" +
                "local current = redis.call('get', key);\n" +
                "if current and tonumber(current) > count then\n" +
                "    return tonumber(current);\n" +
                "end\n" +
                "current = redis.call('incr', key)\n" +
                "if tonumber(current) == 1 then\n" +
                "    redis.call('expire', key, time)\n" +
                "end\n" +
                "return tonumber(current);";
    }
}
src/main/java/com/example/constant/CacheConstants.java
New file
@@ -0,0 +1,43 @@
package com.example.constant;
/**
 * 缓存的key 常量
 *
 * @author ltkj
 */
public class CacheConstants {
    /**
     * 登录用户 redis key
     */
    public static final String LOGIN_TOKEN_KEY = "tj_login_tokens:";
    /**
     * 验证码 redis key
     */
    public static final String CAPTCHA_CODE_KEY = "TJ_captcha_codes:";
    /**
     * 参数管理 cache key
     */
    public static final String SYS_CONFIG_KEY = "sys_config:";
    /**
     * 字典管理 cache key
     */
    public static final String SYS_DICT_KEY = "sys_dict:";
    /**
     * 防重提交 redis key
     */
    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
    /**
     * 限流 redis key
     */
    public static final String RATE_LIMIT_KEY = "rate_limit:";
    /**
     * 登录账户密码错误次数 redis key
     */
    public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}
src/main/java/com/example/constant/Constants.java
New file
@@ -0,0 +1,147 @@
package com.example.constant;
import io.jsonwebtoken.Claims;
/**
 * 通用常量信息
 *
 * @author ltkj
 */
public class Constants {
    /**
     * UTF-8 字符集
     */
    public static final String UTF8 = "UTF-8";
    /**
     * GBK 字符集
     */
    public static final String GBK = "GBK";
    /**
     * www主域
     */
    public static final String WWW = "www.";
    /**
     * http请求
     */
    public static final String HTTP = "http://";
    /**
     * https请求
     */
    public static final String HTTPS = "https://";
    /**
     * 通用成功标识
     */
    public static final String SUCCESS = "0";
    /**
     * 通用失败标识
     */
    public static final String FAIL = "1";
    /**
     * 登录成功
     */
    public static final String LOGIN_SUCCESS = "Success";
    /**
     * 注销
     */
    public static final String LOGOUT = "Logout";
    /**
     * 注册
     */
    public static final String REGISTER = "Register";
    /**
     * 登录失败
     */
    public static final String LOGIN_FAIL = "Error";
    /**
     * 验证码有效期(分钟)
     */
    public static final Integer CAPTCHA_EXPIRATION = 2;
    /**
     * 令牌
     */
    public static final String TOKEN = "token";
    /**
     * 令牌前缀
     */
    public static final String TOKEN_PREFIX = "Bearer ";
    /**
     * 令牌前缀
     */
    public static final String LOGIN_USER_KEY = "tj_login_user_key";
    /**
     * 用户ID
     */
    public static final String JWT_USERID = "userid";
    /**
     * 用户名称
     */
    public static final String JWT_USERNAME = Claims.SUBJECT;
    /**
     * 用户头像
     */
    public static final String JWT_AVATAR = "avatar";
    /**
     * 创建时间
     */
    public static final String JWT_CREATED = "created";
    /**
     * 用户权限
     */
    public static final String JWT_AUTHORITIES = "authorities";
    /**
     * 资源映射路径 前缀
     */
    public static final String RESOURCE_PREFIX = "/profile";
    /**
     * RMI 远程方法调用
     */
    public static final String LOOKUP_RMI = "rmi:";
    /**
     * LDAP 远程方法调用
     */
    public static final String LOOKUP_LDAP = "ldap:";
    /**
     * LDAPS 远程方法调用
     */
    public static final String LOOKUP_LDAPS = "ldaps:";
    /**
     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
     */
    public static final String[] JOB_WHITELIST_STR = {"com.ltkj"};
    /**
     * 定时任务违规的字符
     */
    public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
            "org.springframework", "org.apache", "com.ltkj.common.mallOrderUtils.file"};
    /**
     * 登录用户编号 redis key
     */
    public static final String LOGIN_USERID_KEY = "tj_login_userid:";
}
src/main/java/com/example/constant/UserConstants.java
New file
@@ -0,0 +1,111 @@
package com.example.constant;
/**
 * 用户常量信息
 *
 * @author ltkj
 */
public class UserConstants {
    /**
     * 平台内系统用户的唯一标志
     */
    public static final String SYS_USER = "SYS_USER";
    /**
     * 正常状态
     */
    public static final String NORMAL = "0";
    /**
     * 异常状态
     */
    public static final String EXCEPTION = "1";
    /**
     * 用户封禁状态
     */
    public static final String USER_DISABLE = "1";
    /**
     * 角色封禁状态
     */
    public static final String ROLE_DISABLE = "1";
    /**
     * 部门正常状态
     */
    public static final String DEPT_NORMAL = "0";
    /**
     * 部门停用状态
     */
    public static final String DEPT_DISABLE = "1";
    /**
     * 字典正常状态
     */
    public static final String DICT_NORMAL = "0";
    /**
     * 是否为系统默认(是)
     */
    public static final String YES = "Y";
    /**
     * 是否菜单外链(是)
     */
    public static final String YES_FRAME = "0";
    /**
     * 是否菜单外链(否)
     */
    public static final String NO_FRAME = "1";
    /**
     * 菜单类型(目录)
     */
    public static final String TYPE_DIR = "M";
    /**
     * 菜单类型(菜单)
     */
    public static final String TYPE_MENU = "C";
    /**
     * 菜单类型(按钮)
     */
    public static final String TYPE_BUTTON = "F";
    /**
     * Layout组件标识
     */
    public final static String LAYOUT = "Layout";
    /**
     * ParentView组件标识
     */
    public final static String PARENT_VIEW = "ParentView";
    /**
     * InnerLink组件标识
     */
    public final static String INNER_LINK = "InnerLink";
    /**
     * 校验返回结果码
     */
    public final static String UNIQUE = "0";
    public final static String NOT_UNIQUE = "1";
    /**
     * 用户名长度限制
     */
    public static final int USERNAME_MIN_LENGTH = 2;
    public static final int USERNAME_MAX_LENGTH = 20;
    /**
     * 密码长度限制
     */
    public static final int PASSWORD_MIN_LENGTH = 5;
    public static final int PASSWORD_MAX_LENGTH = 20;
}
src/main/java/com/example/controller/xian/MeiJiController.java
New file
@@ -0,0 +1,11 @@
package com.example.controller.xian;
/**
 * 西安煤机医院
 * @Company: 西安路泰科技有限公司
 * @Author: zhaowenxuan
 * @Date: 2024/12/17 17:16
 */
public class MeiJiController {
}
src/main/java/com/example/datasource/DynamicDataSource.java
New file
@@ -0,0 +1,24 @@
package com.example.datasource;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * 动态数据源
 *
 * @author ltkj
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
src/main/java/com/example/datasource/DynamicDataSourceContextHolder.java
New file
@@ -0,0 +1,41 @@
package com.example.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 数据源切换处理
 *
 * @author ltkj
 */
public class DynamicDataSourceContextHolder {
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType) {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }
    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }
    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}
src/main/java/com/example/domain/BaseEntity.java
New file
@@ -0,0 +1,128 @@
package com.example.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
/**
 * Entity基类
 *
 * @author ltkj
 */
@Data
public class BaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 创建者
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * 更新者
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * 更新时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /**
     * 备注
     */
    @TableField(exist = false)
    private String remark;
    /**
     * 请求参数
     */
    @TableField(exist = false)
    private Map<String, Object> params;
    /**
     * 0删除1未删除
     */
    @TableLogic
    private Integer deleted;
    @TableField(fill = FieldFill.INSERT,exist = false)
    private String createId;
    @TableField(fill = FieldFill.INSERT_UPDATE,exist = false)
    private String updateId;
    public String getCreateBy() {
        return createBy;
    }
    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public String getUpdateBy() {
        return updateBy;
    }
    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public Map<String, Object> getParams() {
        if (params == null) {
            params = new HashMap<>();
        }
        return params;
    }
    public void setParams(Map<String, Object> params) {
        this.params = params;
    }
}
src/main/java/com/example/domain/SysConfig.java
New file
@@ -0,0 +1,103 @@
package com.example.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
 * 参数配置表 sys_config
 *
 * @author ltkj
 */
public class SysConfig extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * 参数主键
     */
    private Long configId;
    /**
     * 参数名称
     */
    private String configName;
    /**
     * 参数键名
     */
    private String configKey;
    /**
     * 参数键值
     */
    private String configValue;
    /**
     * 系统内置(Y是 N否)
     */
    private String configType;
    public Long getConfigId() {
        return configId;
    }
    public void setConfigId(Long configId) {
        this.configId = configId;
    }
    @NotBlank(message = "参数名称不能为空")
    @Size(min = 0, max = 100, message = "参数名称不能超过100个字符")
    public String getConfigName() {
        return configName;
    }
    public void setConfigName(String configName) {
        this.configName = configName;
    }
    @NotBlank(message = "参数键名长度不能为空")
    @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符")
    public String getConfigKey() {
        return configKey;
    }
    public void setConfigKey(String configKey) {
        this.configKey = configKey;
    }
    @NotBlank(message = "参数键值不能为空")
    @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符")
    public String getConfigValue() {
        return configValue;
    }
    public void setConfigValue(String configValue) {
        this.configValue = configValue;
    }
    public String getConfigType() {
        return configType;
    }
    public void setConfigType(String configType) {
        this.configType = configType;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("configId", getConfigId())
                .append("configName", getConfigName())
                .append("configKey", getConfigKey())
                .append("configValue", getConfigValue())
                .append("configType", getConfigType())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("remark", getRemark())
                .toString();
    }
}
src/main/java/com/example/domain/TjAdvice.java
New file
@@ -0,0 +1,83 @@
package com.example.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
 * advice对象 tj_advice
 *
 * @author ltkj
 * @date 2022-11-24
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TjAdvice extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * 主键
     */
    @TableId(type=IdType.AUTO)
    private Long id;
    /**
     * 项目id
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long proId;
    /**
     * 标题
     */
    private String title;
    /**
     * 建议
     */
    private String advice;
    private String deptId;
    private String kjbq;
    private String adSex;
    private String isZj;
    @TableField(exist = false)
    private String proName;
    @TableField(exist = false)
    private List<String> kjbqz;
    @TableField(exist = false)
    private String deptName;
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("id", getId())
                .append("proId", getProId())
                .append("title", getTitle())
                .append("advice", getAdvice())
                .append("createTime", getCreateTime())
                .append("createBy", getCreateBy())
                .append("updateTime", getUpdateTime())
                .append("updateBy", getUpdateBy())
                .append("deleted", getDeleted())
                .append("deptName", getDeptName())
                .toString();
    }
}
src/main/java/com/example/domain/TjAskWorkLog.java
New file
@@ -0,0 +1,94 @@
package com.example.domain;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 问诊职业史记录对象 tj_ask_work_log
 *
 * @author ltkj_赵佳豪&李格
 * @date 2023-12-06
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TjAskWorkLog extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @TableId(type= IdType.AUTO)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /**
     * 体检号
     */
    private String tjNumber;
    /**
     * 客户
     */
    private Long cusId;
    /**
     * 客户名
     */
    private String cusName;
    /**
     * 问诊id
     */
    private Long askId;
    /**
     * 开始时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date beginTime;
    /**
     * 结束时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date endTime;
    /**
     * 工作单位
     */
    private String workCompany;
    /**
     * 部门
     */
    private String workDept;
    /**
     * 工种
     */
    private String workType;
    /**
     * 防护措施
     */
    private String fangHu;
    /**
     * 有害因素
     */
    @TableField(exist = false)
    private List<String> harmTypeLogs;
}
src/main/java/com/example/domain/TjConsumables.java
New file
@@ -0,0 +1,64 @@
package com.example.domain;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
 * 体检耗材对象 tj_consumables
 *
 * @author ltkj_赵佳豪&李格
 * @date 2022-12-29
 */
@Data
public class TjConsumables extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @TableId
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /**
     * 材料名
     */
    private String makings;
    /**
     * 单价
     */
    private BigDecimal price;
    /**
     * 规格
     */
    private String specifications;
    /**
     * 是否参与计算
     */
    private String isCalculation;
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("id", getId())
                .append("makings", getMakings())
                .append("price", getPrice())
                .append("specifications", getSpecifications())
                .append("isCalculation", getIsCalculation())
                .append("deleted", getDeleted())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .toString();
    }
}
src/main/java/com/example/domain/TjCustomer.java
New file
@@ -0,0 +1,290 @@
package com.example.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
 * 客户信息对象 tj_customer
 *
 * @author ltkj
 * @date 2022-11-17
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TjCustomer extends BaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    @TableId
    @JsonSerialize(using = ToStringSerializer.class)
    private Long cusId;
    /**
     * 客户姓名
     */
    private String cusName;
    private String pym;
    private String cardId;
    private String pationId;
    /**
     * 客户性别
     */
    private Long cusSex;
    /**
     * 出生日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date cusBrithday;
    /**
     * 现居住地址
     */
    private String cusAddr;
    /**
     * 户口所在地址
     */
    private String addr;
    /**
     * 联系电话
     */
    private String cusPhone;
    /**
     * 账号密码
     */
    private String cusPassword;
    /**
     * 邮政编码
     */
    private String cusPostcode;
    /**
     * 邮箱
     */
    private String cusEmail;
    /**
     * 索引卡号
     */
    private String indexCard;
    /**
     * 民族
     */
    private String cusNational;
    /**
     * 婚姻状况
     */
    private String cusMarryStatus;
    /**
     * 身份证号
     */
    private String cusIdcard;
    /**
     * 介绍人
     */
    private String cusIntroduce;
    /**
     * 体检次数
     */
    private Long cusNumber;
    /**
     * 是否VIP
     */
    private String cusIsvip;
    /**
     * 角色
     */
    private Long role = 2L;
    /**
     * 体检项目集合
     */
    @TableField(exist = false)
    private List<TjProject> tjProjects;
    /**
     * 体检号
     */
    @TableField(exist = false)
    private String tjNumber;
    /**
     * 体检订单号
     */
    @TableField(exist = false)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long orderId;
    /**
     * 体检状态
     */
    @TableField(exist = false)
    private Long tjStatus;
    /**
     * 体检类别  团队还是个人
     */
    @TableField(exist = false)
    private String tjType;
    /**
     * 预约所选套餐、所选项目
     */
    @TableField(exist = false)
    private Long pacId;
    /**
     * 体检时间
     */
    @TableField(exist = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date tjTime;
    /**
     * 所在单位名称
     */
    @TableField(exist = false)
    private String tjCompName;
    /**
     * 所在单位id
     */
    @TableField(exist = false)
    private String compId;
    @TableField(exist = false)
    private List<Long> proIds;  //项目以逗号隔开
    /**
     * 体检编号
     */
    @TableField(exist = false)
    private String teamNo;
    @TableField(exist = false)
    private String reservationId;
    @TableField(exist = false)
    private String notCheckeds;
    /**
     * 体检完成时间
     */
    @TableField(exist = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date finishTime;
    @TableField(exist = false)
    private String discount;
    @TableField(exist = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date applicationTime;
    @TableField(exist = false)
    private String groupingId;
    @TableField(exist = false)
    private String confirmStatus;
    private String idType;
    private String age;
    private String ageUnit;
    private String career;
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private String connect;
    private String wechat;
    @TableField(exist = false)
    private Integer isHz;
    @TableField(exist = false)
    private Long isPositive;
    @TableField(exist = false)
    private String tjCategory;
    private String dwPhone;
    private String wenHua;
    @TableField(exist = false)
    private List<TjAskWorkLog> workLogs;
    @TableField(exist = false)
    private String isBlack;
    private Long dictCompId;
    private String compName;
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("cusId", getCusId())
                .append("cusName", getCusName())
                .append("cusSex", getCusSex())
                .append("cusBrithday", getCusBrithday())
                .append("cusAddr", getCusAddr())
                .append("cusPhone", getCusPhone())
                .append("cusPassword", getCusPassword())
                .append("cusPostcode", getCusPostcode())
                .append("cusEmail", getCusEmail())
                .append("indexCard", getIndexCard())
                .append("cusNational", getCusNational())
                .append("cusMarryStatus", getCusMarryStatus())
                .append("cusIdcard", getCusIdcard())
                .append("cusIntroduce", getCusIntroduce())
                .append("cusNumber", getCusNumber())
                .append("cusIsvip", getCusIsvip())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("deleted", getDeleted())
                .append("dwPhone", getDwPhone())
                .append("compId", getDictCompId())
                .append("compName", getCompName())
                .toString();
    }
}
src/main/java/com/example/domain/TjProject.java
New file
@@ -0,0 +1,234 @@
package com.example.domain;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
 * 体检项目对象 tj_project
 *
 * @author ltkj
 * @date 2022-11-17
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TjProject extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * 主键
     */
    @TableId
    @JsonSerialize(using = ToStringSerializer.class)
    private Long proId;
    /**
     * 父项目id
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long proParentId;
    /**
     * 项目名称
     */
    private String proName;
    /**
     * 项目英文名
     */
    private String proEngName;
    /**
     * 项目价格
     */
    private BigDecimal proPrice;
    /**
     * 项目原价
     */
    @TableField(exist = false)
    private BigDecimal proOrdPrice;
    /**
     * 备注
     */
    private String proRemark;
    /**
     * 检查类别
     */
    private String proCheckType;
    /**
     * 临床意义
     */
    private String proMeaning;
    /**
     * 检查方式
     */
    private String proCheckMethod;
    /**
     * 是否父级 0为父1为子
     */
    private Integer proStandard;
    /**
     * 项目类型
     */
    private String proType;
    /**
     * 项目状态
     */
    private Integer proStatus;
    /**
     * 套餐是否含有该项目
     */
    @TableField(exist = false)
    private Integer ischeck;
    /**
     * 科室id
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long deptId;
    /**
     * 计量单位
     */
    private String proMetering;
    private String proDefault;
    private String proScope;
    @TableField(exist = false)
    private String flag;
    @TableField(exist = false)
    private List<TjProject> tjProjectList;
    @TableField(exist = false)
    private List<TjStandard> tjStandardList;
    /**
     * 父项目中的子项目名称字符串
     */
    @TableField(exist = false)
    private String allSonProName;
    /**
     * 该项目的标准
     */
    @TableField(exist = false)
    private TjStandard standard;
    /**
     * 该项目的建议集合
     */
    @TableField(exist = false)
    private List<TjAdvice> adviceList;
    /**
     * 该项目的耗材集合
     */
    @TableField(exist = false)
    private List<TjConsumables> consumablesList;
    /**
     * 该项目在套餐内现价
     */
    @TableField(exist = false)
    private BigDecimal priceNow;
    /**
     * 该项目所在部门名称
     */
    @TableField(exist = false)
    private String deptName;
    /**
     * 收费项目id
     */
    @TableField(exist = false)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long sfxmId;
    private String resultType;
    private String isSampling;
    private String specimenType;
    private String checkBw;
    private String proSex;
    private String isEat;
    private String needReport;
    private String lisXmbm;
    private String lisXmmc;
    private String hisXmbm;
    private String hisXmmc;
    private String sfzhfy;
    private BigDecimal hisdj;
    private Integer sl;
    /**
     * 子项目
     */
    @TableField(exist = false)
    private List<TjProject> children = new ArrayList<TjProject>();
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("proId", getProId())
                .append("proParentId", getProParentId())
                .append("proName", getProName())
                .append("proEngName", getProEngName())
                .append("proPrice", getProPrice())
                .append("proRemark", getProRemark())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("deleted", getDeleted())
                .append("proCheckType", getProCheckType())
                .append("proMeaning", getProMeaning())
                .append("proCheckMethod", getProCheckMethod())
                .append("proStandard", getProStandard())
                .append("proType", getProType())
                .append("ischeck", getIscheck())
                .toString();
    }
}
src/main/java/com/example/domain/TjStandard.java
New file
@@ -0,0 +1,74 @@
package com.example.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
 * standard对象 tj_standard
 *
 * @author ltkj
 * @date 2022-11-24
 */
@Data
public class TjStandard extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    @TableId
    private String stanId;
    /**
     * 项目id
     */
    private Long proId;
    @TableField(exist = false)
    private String proName;
    /**
     * 体检人性别
     */
    @TableField(updateStrategy= FieldStrategy.IGNORED)
    private Integer tjSex;
    /**
     * 体检人类型
     */
    private Integer tjType;
    /**
     * 标准值
     */
    private String tjStandardGtValue;
    /**
     * 标准值
     */
    private String tjStandardLtValue;
    /**
     * 单位
     */
    private String company;
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("stanId", getStanId())
                .append("proId", getProId())
                .append("tjSex", getTjSex())
                .append("tjType", getTjType())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("deleted", getDeleted())
                .toString();
    }
}
src/main/java/com/example/dto/xian/meiji/CheXiaoMzFyDto.java
New file
@@ -0,0 +1,24 @@
package com.example.dto.xian.meiji;
import lombok.Data;
import java.util.List;
/**
 * ClassName: CheXiaoMzFyDto <br/>
 * Description: <br/>
 * date: 2024/12/10 20:12<br/>
 *
 * @author zjh<br />
 */
@Data
public class CheXiaoMzFyDto {
    //就诊卡号
    private String jiuZhenKh;
    //病人Id(在HIS中的唯一码)
    private String bingRenId;
    //费用id
    private List<FeiYongIdDto>feiYongIdList;
}
src/main/java/com/example/dto/xian/meiji/CreateMenZhenFyDto.java
New file
@@ -0,0 +1,38 @@
package com.example.dto.xian.meiji;
import lombok.Data;
import java.util.List;
/**
 * ClassName: createMenZhenFyDto <br/>
 * Description: <br/>
 * date: 2024/12/10 11:57<br/>
 *
 * @author zjh<br />
 */
@Data
public class CreateMenZhenFyDto {
    //病人Id(在HIS中的唯一码)
    private String bingRenId;
    //就诊卡号
    private String jiuZhenKh;
    //操作员(收费人员/体检/自助机)
    private String caoZuoYuan;
    //院区Id
    private String yuanQuId;
    //应用Id(自助机传010101,体检 870101)
    private String yingYongId;
    //开单科室
    private String kaiDanKs;
    //登记流水号
    private String dengJiLsh;
    //收退标志(1:生成待收费, 2生成待退费)
    private String shouTuiBz;
    //费用明细
    private List<FeiYongMxDto> feiYongMxList;
}
src/main/java/com/example/dto/xian/meiji/FeiYongIdDto.java
New file
@@ -0,0 +1,16 @@
package com.example.dto.xian.meiji;
import lombok.Data;
/**
 * ClassName: FeiYongIdDto <br/>
 * Description: <br/>
 * date: 2024/12/10 20:16<br/>
 *
 * @author zjh<br />
 */
@Data
public class FeiYongIdDto {
    //费用Id
    private String feiYongId;
}
src/main/java/com/example/dto/xian/meiji/FeiYongMxDto.java
New file
@@ -0,0 +1,34 @@
package com.example.dto.xian.meiji;
import lombok.Data;
import java.math.BigDecimal;
/**
 * ClassName: feiYongMxDto <br/>
 * Description: <br/>
 * date: 2024/12/10 11:57<br/>
 *
 * @author zjh<br />
 */
@Data
public class FeiYongMxDto {
    //费用明细Id(退费必传)
    private String feiYongMxId;
    //费用Id(退费必传)
    private String feiYongId;
    //收费项目Id(HIS的)
    private String shouFeiXmId;
    //收费项目名称
    private String shouFeiXmMc;
    //数量(退费为负)
    private Integer shuLiang;
    //单价
    private BigDecimal danJia;
    //结算金额(退费为负)
    private BigDecimal jieSuanJe;
    //执行科室
    private String zhiXingKs;
    //执行科室名称
    private String zhiXingKsMc;
}
src/main/java/com/example/enums/DataSourceType.java
New file
@@ -0,0 +1,24 @@
package com.example.enums;
/**
 * 数据源
 *
 * @author ruoyi
 */
public enum DataSourceType {
    /**
     * 主库
     */
    MASTER,
    /**
     * 从库
     */
    SLAVE_PACS,
    SLAVE_HIS,
    SLAVE_LIS,
}
src/main/java/com/example/exception/ServiceException.java
New file
@@ -0,0 +1,61 @@
package com.example.exception;
/**
 * 业务异常
 */
public final class ServiceException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    /**
     * 错误码
     */
    private Integer code;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 错误明细,内部调试错误
     */
    private String detailMessage;
    /**
     * 空构造方法,避免反序列化问题
     */
    public ServiceException() {
    }
    public ServiceException(String message) {
        this.message = message;
    }
    public ServiceException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }
    public String getDetailMessage() {
        return detailMessage;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public Integer getCode() {
        return code;
    }
    public ServiceException setMessage(String message) {
        this.message = message;
        return this;
    }
    public ServiceException setDetailMessage(String detailMessage) {
        this.detailMessage = detailMessage;
        return this;
    }
}
src/main/java/com/example/mapper/SysConfigMapper.java
New file
@@ -0,0 +1,76 @@
package com.example.mapper;
import java.util.List;
import com.example.domain.SysConfig;
import org.apache.ibatis.annotations.Select;
/**
 * 参数配置 数据层
 *
 * @author ltkj
 */
public interface SysConfigMapper {
    /**
     * 查询参数配置信息
     *
     * @param config 参数配置信息
     * @return 参数配置信息
     */
    public SysConfig selectConfig(SysConfig config);
    /**
     * 查询参数配置列表
     *
     * @param config 参数配置信息
     * @return 参数配置集合
     */
    public List<SysConfig> selectConfigList(SysConfig config);
    /**
     * 根据键名查询参数配置信息
     *
     * @param configKey 参数键名
     * @return 参数配置信息
     */
    public SysConfig checkConfigKeyUnique(String configKey);
    /**
     * 新增参数配置
     *
     * @param config 参数配置信息
     * @return 结果
     */
    public int insertConfig(SysConfig config);
    /**
     * 修改参数配置
     *
     * @param config 参数配置信息
     * @return 结果
     */
    public int updateConfig(SysConfig config);
    /**
     * 删除参数配置
     *
     * @param configId 参数ID
     * @return 结果
     */
    public int deleteConfigById(Long configId);
    /**
     * 批量删除参数信息
     *
     * @param configIds 需要删除的参数ID
     * @return 结果
     */
    public int deleteConfigByIds(Long[] configIds);
    void tbhisproprice();
}
src/main/java/com/example/service/ISysConfigService.java
New file
@@ -0,0 +1,92 @@
package com.example.service;
import java.util.List;
import com.example.domain.SysConfig;
/**
 * 参数配置 服务层
 *
 * @author ltkj
 */
public interface ISysConfigService {
    /**
     * 查询参数配置信息
     *
     * @param configId 参数配置ID
     * @return 参数配置信息
     */
    public SysConfig selectConfigById(Long configId);
    /**
     * 根据键名查询参数配置信息
     *
     * @param configKey 参数键名
     * @return 参数键值
     */
    public String selectConfigByKey(String configKey);
    /**
     * 获取验证码开关
     *
     * @return true开启,false关闭
     */
    public boolean selectCaptchaEnabled();
    /**
     * 查询参数配置列表
     *
     * @param config 参数配置信息
     * @return 参数配置集合
     */
    public List<SysConfig> selectConfigList(SysConfig config);
    /**
     * 新增参数配置
     *
     * @param config 参数配置信息
     * @return 结果
     */
    public int insertConfig(SysConfig config);
    /**
     * 修改参数配置
     *
     * @param config 参数配置信息
     * @return 结果
     */
    public int updateConfig(SysConfig config);
    /**
     * 批量删除参数信息
     *
     * @param configIds 需要删除的参数ID
     */
    public void deleteConfigByIds(Long[] configIds);
    /**
     * 加载参数缓存数据
     */
    public void loadingConfigCache();
    /**
     * 清空参数缓存数据
     */
    public void clearConfigCache();
    /**
     * 重置参数缓存数据
     */
    public void resetConfigCache();
    /**
     * 校验参数键名是否唯一
     *
     * @param config 参数信息
     * @return 结果
     */
    public String checkConfigKeyUnique(SysConfig config);
    void tbhisproprice();
}
src/main/java/com/example/service/impl/SysConfigServiceImpl.java
New file
@@ -0,0 +1,209 @@
package com.example.service.impl;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import com.example.constant.CacheConstants;
import com.example.constant.UserConstants;
import com.example.domain.SysConfig;
import com.example.exception.ServiceException;
import com.example.mapper.SysConfigMapper;
import com.example.service.ISysConfigService;
import com.example.utils.RedisCache;
import com.example.utils.StringUtils;
import com.example.utils.text.Convert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 参数配置 服务层实现
 *
 * @author ltkj
 */
@Service
public class SysConfigServiceImpl implements ISysConfigService {
    @Autowired
    private SysConfigMapper configMapper;
    @Autowired
    private RedisCache redisCache;
    /**
     * 项目启动时,初始化参数到缓存
     */
    @PostConstruct
    public void init() {
        loadingConfigCache();
    }
    /**
     * 查询参数配置信息
     *
     * @param configId 参数配置ID
     * @return 参数配置信息
     */
    @Override
    public SysConfig selectConfigById(Long configId) {
        SysConfig config = new SysConfig();
        config.setConfigId(configId);
        return configMapper.selectConfig(config);
    }
    /**
     * 根据键名查询参数配置信息
     *
     * @param configKey 参数key
     * @return 参数键值
     */
    @Override
    public String selectConfigByKey(String configKey) {
        String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
        if (StringUtils.isNotEmpty(configValue)) {
            return configValue;
        }
        SysConfig config = new SysConfig();
        config.setConfigKey(configKey);
        SysConfig retConfig = configMapper.selectConfig(config);
        if (StringUtils.isNotNull(retConfig)) {
            redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue());
            return retConfig.getConfigValue();
        }
        return StringUtils.EMPTY;
    }
    /**
     * 获取验证码开关
     *
     * @return true开启,false关闭
     */
    @Override
    public boolean selectCaptchaEnabled() {
        String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled");
        if (StringUtils.isEmpty(captchaEnabled)) {
            return true;
        }
        return Convert.toBool(captchaEnabled);
    }
    /**
     * 查询参数配置列表
     *
     * @param config 参数配置信息
     * @return 参数配置集合
     */
    @Override
    public List<SysConfig> selectConfigList(SysConfig config) {
        return configMapper.selectConfigList(config);
    }
    /**
     * 新增参数配置
     *
     * @param config 参数配置信息
     * @return 结果
     */
    @Override
    public int insertConfig(SysConfig config) {
        int row = configMapper.insertConfig(config);
        if (row > 0) {
            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
        }
        return row;
    }
    /**
     * 修改参数配置
     *
     * @param config 参数配置信息
     * @return 结果
     */
    @Override
    public int updateConfig(SysConfig config) {
        int row = configMapper.updateConfig(config);
        if (row > 0) {
            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
        }
        return row;
    }
    /**
     * 批量删除参数信息
     *
     * @param configIds 需要删除的参数ID
     */
    @Override
    public void deleteConfigByIds(Long[] configIds) {
        for (Long configId : configIds) {
            if(configId==113){
                continue;
            }
            SysConfig config = selectConfigById(configId);
            if (StringUtils.equals(UserConstants.YES, config.getConfigType())) {
                throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
            }
            configMapper.deleteConfigById(configId);
            redisCache.deleteObject(getCacheKey(config.getConfigKey()));
        }
    }
    /**
     * 加载参数缓存数据
     */
    @Override
    public void loadingConfigCache() {
        List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig());
        for (SysConfig config : configsList) {
            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
        }
    }
    /**
     * 清空参数缓存数据
     */
    @Override
    public void clearConfigCache() {
        Collection<String> keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*");
        redisCache.deleteObject(keys);
    }
    /**
     * 重置参数缓存数据
     */
    @Override
    public void resetConfigCache() {
        clearConfigCache();
        loadingConfigCache();
    }
    /**
     * 校验参数键名是否唯一
     *
     * @param config 参数配置信息
     * @return 结果
     */
    @Override
    public String checkConfigKeyUnique(SysConfig config) {
        Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId();
        SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey());
        if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }
    @Override
    public void tbhisproprice() {
        configMapper.tbhisproprice();
    }
    /**
     * 设置cache key
     *
     * @param configKey 参数键
     * @return 缓存键key
     */
    private String getCacheKey(String configKey) {
        return CacheConstants.SYS_CONFIG_KEY + configKey;
    }
}
src/main/java/com/example/service/xian/MeiJiService.java
New file
@@ -0,0 +1,471 @@
package com.example.service.xian;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.domain.TjCustomer;
import com.example.dto.xian.meiji.CheXiaoMzFyDto;
import com.example.dto.xian.meiji.CreateMenZhenFyDto;
import com.example.service.ISysConfigService;
import com.example.utils.HttpClientUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import java.io.FileInputStream;
import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
 * @Company: 西安路泰科技有限公司
 * @Author: zhaowenxuan
 * @Date: 2024/12/17 17:17
 */
public class MeiJiService {
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private RedisTemplate<String ,Object> redisTemplate;
    private static  String HIS_URL = "http://oapi.pbkwyy.com/OAPI";
    private static String CONFIG_PATH;
    private static  String GRANT_TYPE = "client_credentials";
    private static  String CLIENT_ID = "XFZZQEfXTZ7exhhi";
    private static  String CLIENT_SECRET = "05a192176c21edfcc9cf5fa26fc5a9e0c5b131ad";
//    private static  String SCOP = "";
//    http://oapi.pbkwyy.com/OAPI/oauth/token
//    grant_type:client_credentials
//    client_id:XFZZQEfXTZ7exhhi
//    client_secret:05a192176c21edfcc9cf5fa26fc5a9e0c5b131ad
    @Value("${config.properties}")
    public void setConfigPath(String configPath) {
        CONFIG_PATH = configPath;
        try {
            FileInputStream inputStream = new FileInputStream(CONFIG_PATH);
            Properties props = new Properties();
            props.load(inputStream);
            String url = props.getProperty("df_his_api_url");
            String port = props.getProperty("df_his_api_port");
            GRANT_TYPE = props.getProperty("grant_type");
            CLIENT_ID= props.getProperty("client_id");
            CLIENT_SECRET = props.getProperty("client_secret");
//            SCOP = props.getProperty("scope");
            HIS_URL=url+":"+port+"/OAPI/";
        } catch (IOException throwables) {
            throwables.printStackTrace();
        }
    }
    //获取token
    private JSONObject getToken () {
        Map<String, Object> map = new HashMap<>();
        map.put("grant_type",GRANT_TYPE);
        map.put("client_id",CLIENT_ID);
        map.put("client_secret",CLIENT_SECRET);
//        map.put("scope",SCOP);
        String post = sendPost (HIS_URL+"/oauth/token", map);
        if (StrUtil.isBlank(post)) return null;
        JSONObject parseObj = JSONUtil.parseObj(post);
//        Integer expiresIn = parseObj.getInt("expires_in");
//        if (expiresIn != null){
//            parseObj.putOpt("time",Instant.now().getEpochSecond());
//            redisTemplate.opsForHash().putAll("token:his:df",parseObj);
//            redisTemplate.expire("token:his:df",expiresIn - 10, TimeUnit.SECONDS);
//        }
        return parseObj;
    }
    //建档
    public JSON jianDang (TjCustomer customer) {
        String czy = configService.selectConfigByKey("dfhisczybm");
        Map<String, Object> map = new HashMap<> ();
        map.put ("jiuZhenKh", customer.getPationId ());
        map.put ("kaiLeiXing","4");
        map.put ("xingMing", customer.getCusName ());
        Long cusSex = customer.getCusSex();
        if(cusSex==0L){
            map.put ("xingBie","男");
        }else if(cusSex==1L){
            map.put ("xingBie","女");
        }else {
            map.put ("xingBie","未知");
        }
        map.put ("shenFenZh", customer.getCusIdcard ());
        map.put ("danWeiBh","");
        map.put ("chuShengRq", DateUtil.format(customer.getCusBrithday(),"yyyy-MM-dd"));
        map.put ("lianXiDz", customer.getCusAddr());
        map.put ("lianXiDh", customer.getCusPhone ());
        map.put ("feiYongLb","");
        map.put ("feiYongXz","");
        map.put ("jiLuLy","3");
        map.put ("caoZuoYuan",czy);
        map.put ("chongZhiJe","");
        map.put ("yiBaoKh","");
        map.put ("geRenBh","");
        map.put ("yiBaoBrXx","");
        map.put ("gongZuoDw",customer.getCompName());
        map.put ("canBaoXzMc","");
        map.put ("muLuBlLb","");
        map.put ("kunNanJzDj","");
        map.put ("yiLiaoLb","");
        map.put ("minZuDm","");
        map.put ("minZuMc","");
        //职业编码
        String post = sendPost (HIS_URL+"/menZhenJz/jianDang", map);
        return JSONUtil.parseObj(post);
    }
    //验证身份证是否建过档
    public JSON getBingRenXxByShengFenZheng (TjCustomer customer) {
        String czy = configService.selectConfigByKey("dfhisczybm");
        Map<String, Object> map = new HashMap<> ();
        map.put ("caoZuoYuan",czy);
        map.put ("shenFenZh", customer.getCusIdcard ());
        map.put ("danWeiBh","");
        //职业编码
        String post = sendPost (HIS_URL+"/menZhenJz/getBingRenXxByShengFenZheng", map);
        return JSONUtil.parseObj(post);
    }
    //病人基本信息变更
    public JSON saveBingRenXx (TjCustomer customer) {
        String czy = configService.selectConfigByKey("dfhisczybm");
        Map<String, Object> map = new HashMap<> ();
        map.put ("jiuZhenKh", customer.getPationId ());
        map.put ("kaiLeiXing","4");
        map.put ("xingMing", customer.getCusName ());
        Long cusSex = customer.getCusSex();
        if(cusSex==0L){
            map.put ("xingBie","男");
        }else if(cusSex==1L){
            map.put ("xingBie","女");
        }else {
            map.put ("xingBie","未知");
        }
        map.put ("shenFenZh", customer.getCusIdcard ());
        map.put ("danWeiBh","");
        map.put ("chuShengRq", DateUtil.format(customer.getCusBrithday(),"yyyy-MM-dd"));
        map.put ("lianXiDz", customer.getCusAddr());
        map.put ("lianXiDh", customer.getCusPhone ());
        map.put ("feiYongLb","");
        map.put ("feiYongXz","");
        map.put ("jiLuLy","3");
        map.put ("caoZuoYuan",czy);
        //职业编码
        String post = sendPost (HIS_URL+"/menZhenJz/saveBingRenXx", map);
        return JSONUtil.parseObj(post);
    }
    //待收费费用查询
    public JSON getListDaiShouFei (String bingRenId,String jiuZhenKh,String yuanQuId) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("bingRenId",bingRenId);
        map.put ("jiuZhenKh",jiuZhenKh);
        map.put ("yuanQuId",yuanQuId);
        //职业编码
        String post = sendPost (HIS_URL+"/shouFei/getListDaiShouFei", map);
        return JSONUtil.parseObj(post);
    }
    //生成待收费/待退费 费用
    public JSON createMenZhenFy (CreateMenZhenFyDto dto) {
        String czy = configService.selectConfigByKey("dfhisczybm");
        Map<String, Object> map = new HashMap<> ();
        map.put ("bingRenId",dto.getBingRenId());
        map.put ("jiuZhenKh",dto.getJiuZhenKh());
        map.put ("caoZuoYuan",czy);
        map.put ("yuanQuId",dto.getYuanQuId());
        map.put ("yingYongId",dto.getYingYongId());
        map.put ("kaiDanKs",dto.getKaiDanKs());
        map.put ("dengJiLsh",dto.getDengJiLsh());
        map.put ("shouTuiBz",dto.getShouTuiBz());
        map.put ("feiYongMxList",dto.getFeiYongMxList());
        //职业编码
        String post = sendPost (HIS_URL+"/shouFei/createMenZhenFy", map);
        return JSONUtil.parseObj(post);
    }
    //门诊未收费费用撤销
    public JSON cheXiaoMzFy (CheXiaoMzFyDto dto) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("bingRenId",dto.getBingRenId());
        map.put ("jiuZhenKh",dto.getJiuZhenKh());
        map.put ("feiYongIdList",dto.getFeiYongIdList());
        //职业编码
        String post = sendPost (HIS_URL+"/shouFei/cheXiaoMzFy", map);
        return JSONUtil.parseObj(post);
    }
    //收费/退费完成通知第三方
    public JSON pushZhiFuMsg (String feiYongId,int yeWuLx ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("feiYongId",feiYongId);
        map.put ("yeWuLx",yeWuLx);
        //职业编码
        String post = sendPost (HIS_URL+"/shouFei/pushZhiFuMsg", map);
        return JSONUtil.parseObj(post);
    }
    //科室信息查询
    public JSON getKeShi (String yuanQuId,String keShiMc,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("yuanQuId",yuanQuId);
        map.put ("keShiMc",keShiMc);
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getKeShi", map);
        return JSONUtil.parseObj(post);
    }
    //医生信息查询
    public JSON getListYiShengZd (String yuanQuId,String keShiMc,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("yuanQuId",yuanQuId);
        map.put ("keShiMc",keShiMc);
        map.put ("bianGengSj","");
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getListYiShengZd", map);
        return JSONUtil.parseObj(post);
    }
    //获取收费项目分页
    public JSON getShouFeiXm (String queryString,String bianGengSj,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("queryString",queryString);
        map.put ("bianGengSj",bianGengSj);
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getShouFeiXm", map);
        return JSONUtil.parseObj(post);
    }
    /**
     *科室信息推送
     * @param xingZhiSx 组织属性 第一位1 表示 挂号
     * 第二位 1表示临床
     * 第三位 1表示检查
     * 第四位 1 表示手术
     * 第五位 1 表示治疗
     * 第六位 1 表示护理
     * @param queryString 模糊匹配输⼊码1、科室名称
     * @param zuoFeiBz 作废标志:0 正常;1 作废
     * @param yuanQuId 院区id
     * @param keShiIds 科室ID集合
     * @param ifPlus 是否查询plus属性
     * @return
     */
    public JSON getKeShiByConditions (int xingZhiSx, String queryString, int zuoFeiBz, String yuanQuId, List<String> keShiIds, int ifPlus) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("xingZhiSx",xingZhiSx);
        map.put ("queryString",queryString);
        map.put ("zuoFeiBz",zuoFeiBz);
        map.put ("yuanQuId",yuanQuId);
        map.put ("keShiIds",keShiIds);
        map.put ("ifPlus",ifPlus);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getKeShiByConditions", map);
        return JSONUtil.parseObj(post);
    }
    /**
     * 取样本字典
     * @param queryString 样本类型名称(样本名称/样本类型id)
     * @param pageIndex
     * @param pageSize 每⻚条数(最⼤不能超过100)
     * @return
     */
    public JSON getYangBen (String queryString,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("queryString",queryString);
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getYangBen", map);
        return JSONUtil.parseObj(post);
    }
    /**
     *  病区信息推送
     * @param yuanQuId 院区Id
     * @param keShiId 科室Id
     * @param pageIndex 当前⻚
     * @param pageSize 每⻚条数(最⼤不能超过100)
     * @return
     */
    public JSON getListBingQuZd (String yuanQuId,String keShiId,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("yuanQuId",yuanQuId);
        map.put ("keShiId",keShiId);
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getListBingQuZd", map);
        return JSONUtil.parseObj(post);
    }
    /**
     * 职⼯信息
     * @param bianGengSj 变更时间
     * @param pageIndex 当前⻚
     * @param pageSize 每⻚条数(最⼤不能超过100)
     * @return
     */
    public JSON getZhiGongPage (String bianGengSj,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("bianGengSj",bianGengSj);
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getZhiGongPage", map);
        return JSONUtil.parseObj(post);
    }
    /**
     * 检查项目
     * @param queryString
     * @param bianGengSj
     * @param pageIndex
     * @param pageSize
     * @return
     */
    public JSON getJianChaXm (String queryString,String bianGengSj,int pageIndex,int pageSize ) {
        Map<String, Object> map = new HashMap<> ();
        map.put ("queryString",queryString);
        map.put ("bianGengSj",bianGengSj);
        map.put ("pageIndex",pageIndex);
        map.put ("pageSize",pageSize);
        //职业编码
        String post = sendPost (HIS_URL+"/zhuShuJu/getJianChaXm", map);
        return JSONUtil.parseObj(post);
    }
    /**
     * 检验项⽬推送
     * @param queryCode 查询码(项⽬编码/拼⾳码
     * @param page 当前⻚
     * @param size 每⻚条数(最⼤不能超过100
     * @return
     */
    public JSON getJianYanXm(String queryCode,Integer page,Integer size){
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("queryCode",queryCode);
        hashMap.put("page",page);
        hashMap.put("size",size);
        String post = sendPost (HIS_URL+"/zhuShuJu/getJianYanXm", hashMap);
        return JSONUtil.parse(post);
    }
    /**
     * 检验项⽬收费推送
     * @param shouFeiXmId 收费项⽬id
     * @param jiaGeTx 价格体系
     * @return
     */
    public JSON getShouFeiXmJg(String shouFeiXmId,String jiaGeTx){
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("shouFeiXmId",shouFeiXmId);
        hashMap.put("jiaGeTx",jiaGeTx);
        String post = sendPost (HIS_URL+"/zhuShuJu/getShouFeiXmJg", hashMap);
        return JSONUtil.parse(post);
    }
    /**
     * 检验容器
     * @param queryString 容器名称(容器名称/输⼊码1)
     * @param pageIndex 当前⻚
     * @param pageSize 每⻚条数(最⼤不能超过100)
     * @return
     */
    public JSON getRongQi(String queryString,Integer pageIndex,Integer pageSize){
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("queryString",queryString);
        hashMap.put("pageIndex",pageIndex);
        hashMap.put("pageSize",pageSize);
        String post = sendPost (HIS_URL+"/zhuShuJu/getRongQi", hashMap);
        return JSONUtil.parse(post);
    }
    /**
     *  检验样本
     * @param queryString 样本类型名称(样本名称/样本类型id)
     * @param pageIndex  当前⻚
     * @param pageSize 每⻚条数(最⼤不能超过100)
     * @return
     */
    public JSON getYangBen(String queryString,Integer pageIndex,Integer pageSize){
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("queryString",queryString);
        hashMap.put("pageIndex",pageIndex);
        hashMap.put("pageSize",pageSize);
        String post = sendPost (HIS_URL+"/zhuShuJu/getYangBen", hashMap);
        return JSONUtil.parse(post);
    }
    private String sendPost(String url,Map<String, Object> hashMap){
        Map<Object, Object> entries = redisTemplate.opsForHash().entries("token:his:df");
        if (entries != null && !entries.isEmpty()) {
            String timeStr = entries.get("time").toString();
            String expiresInStr = entries.get("expires_in").toString();
            long time = Long.parseLong(timeStr);
            long expiresIn = Long.parseLong(expiresInStr);
            Instant tokenExpirationTime = Instant.ofEpochSecond(time).plusSeconds(expiresIn);
            Instant now = Instant.now();
            if (now.isAfter(tokenExpirationTime)){
                return refreshToken(url,hashMap);
            }else {
                String accessToken = entries.get("access_token").toString();
                String tokenType = entries.get("token_type").toString();
                String string = HttpClientUtils.sendPostToken(url, hashMap, tokenType + " " + accessToken);
                return StrUtil.isNotBlank(string) ? string : JSONUtil.createObj().toString();
            }
        }else {
            return refreshToken(url, hashMap);
        }
    }
    private String refreshToken(String url, Map<String, Object> hashMap) {
        JSONObject parseObj = getToken();
        if (parseObj != null) {
            Integer expiresIn = parseObj.getInt("expires_in");
            if (expiresIn != null) {
                parseObj.putOpt("time", Instant.now().getEpochSecond());
                redisTemplate.opsForHash().putAll("token:his:df", parseObj);
                redisTemplate.expire("token:his:df", expiresIn - 10, TimeUnit.SECONDS);
                String accessToken = parseObj.getStr("access_token");
                String tokenType = parseObj.getStr("token_type");
                String string = HttpClientUtils.sendPostToken(url, hashMap, tokenType + " " + accessToken);
                return StrUtil.isNotBlank(string) ? string : JSONUtil.createObj().toString();
            }
        }
        return JSONUtil.createObj().toString();
    }
}
src/main/java/com/example/utils/FastJson2JsonRedisSerializer.java
New file
@@ -0,0 +1,43 @@
package com.example.utils;
import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
/**
 * Redis使用FastJson序列化
 *
 * @author ltkj
 */
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private Class<T> clazz;
    public FastJson2JsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);
        return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);
    }
}
src/main/java/com/example/utils/HttpClientUtils.java
New file
@@ -0,0 +1,574 @@
package com.example.utils;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.util.PublicSuffixMatcher;
import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
 * httpClient 工具类
 *
 * @create 2019-02-10 下午 2:49
 */
@Component
@Slf4j
public class HttpClientUtils {
    /**
     * 默认参数设置
     * setConnectTimeout:设置连接超时时间,单位毫秒。
     * setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。
     * setSocketTimeout:请求获取数据的超时时间,单位毫秒。访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 暂时定义15分钟
     */
    private RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(600000).setConnectTimeout(600000).setConnectionRequestTimeout(600000).build();
    /**
     * 静态内部类---作用:单例产生类的实例
     *
     * @author Administrator
     */
    private static class LazyHolder {
        private static final HttpClientUtils INSTANCE = new HttpClientUtils();
    }
    HttpClientUtils() {
    }
    public static HttpClientUtils getInstance() {
        return LazyHolder.INSTANCE;
    }
    /**
     * 发送 post请求
     *
     * @param httpUrl 地址
     */
    public String sendHttpPost(String httpUrl) {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        return sendHttpPost(httpPost);
    }
    /**
     * 发送 post请求
     *
     * @param httpUrl 地址
     * @param params  参数(格式:key1=value1&key2=value2)
     */
    public String sendHttpPost(String httpUrl, String params) {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        try {
            //设置参数
            StringEntity stringEntity = new StringEntity(params, "UTF-8");
            stringEntity.setContentType("application/x-www-form-urlencoded");
            httpPost.setEntity(stringEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sendHttpPost(httpPost);
    }
    public static String sendPost(String httpUrl, Map<String, Object> maps) {
        try {
            // 1. 创建 URL 对象
            URL url = new URL(httpUrl);
            // 2. 创建 HttpURLConnection 对象
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 3. 设置请求方法为 POST
            connection.setRequestMethod("POST");
            // 4. 设置 Content-Type 头部字段
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            // 6. 向服务器发送数据
            String requestBody = JSONUtil.toJsonStr(maps);
            log.info(httpUrl+"入参:   "+requestBody);
//            String requestBody1 = maps.toString();
            byte[] postData = requestBody.getBytes("UTF-8");
            connection.setDoOutput(true);
            try (OutputStream outputStream = connection.getOutputStream()) {
                outputStream.write(postData);
            }
            // 8. 获取响应数据
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                StringBuilder response = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                log.info("=====================================================");
                log.info(httpUrl+"出参");
                log.info(response.toString());
                connection.disconnect();
                return response.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String sendPostToken(String httpUrl, Map<String, Object> maps,String authorization) {
        try {
            // 1. 创建 URL 对象
            URL url = new URL(httpUrl);
            // 2. 创建 HttpURLConnection 对象
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 3. 设置请求方法为 POST
            connection.setRequestMethod("POST");
            // 4. 设置 Content-Type 头部字段
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            connection.setRequestProperty("Authorization", authorization);
            // 6. 向服务器发送数据
            String requestBody = JSONUtil.toJsonStr(maps);
            log.info(httpUrl+"入参:   "+requestBody);
//            String requestBody1 = maps.toString();
            byte[] postData = requestBody.getBytes("UTF-8");
            connection.setDoOutput(true);
            try (OutputStream outputStream = connection.getOutputStream()) {
                outputStream.write(postData);
            }
            // 8. 获取响应数据
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                StringBuilder response = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                log.info("=====================================================");
                log.info(httpUrl+"出参");
                log.info(response.toString());
                connection.disconnect();
                return response.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String sendPostTokenFormUrlencoded(String httpUrl, Map<String, Object> maps, String authorization) {
        HttpURLConnection connection = null;
        OutputStreamWriter writer = null;
        BufferedReader reader = null;
        StringBuilder response = new StringBuilder();
        try {
            // 创建 URL 对象
            URL url = new URL(httpUrl);
            // 打开连接
            connection = (HttpURLConnection) url.openConnection();
            // 设置请求方法为 POST
            connection.setRequestMethod("POST");
            // 设置请求头 Content-Type 为 application/x-www-form-urlencoded
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // 如果需要 Authorization Header
            if (authorization != null && !authorization.isEmpty()) {
                connection.setRequestProperty("Authorization", authorization);
            }
            // 设置允许输出
            connection.setDoOutput(true);
            // 构建表单数据
            StringBuilder postData = new StringBuilder();
            for (Map.Entry<String, Object> entry : maps.entrySet()) {
                if (postData.length() > 0) {
                    postData.append("&");
                }
                postData.append(URLEncoder.encode(entry.getKey(), "UTF-8"))
                        .append("=")
                        .append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
            }
            // 获取输出流并写入表单数据
            writer = new OutputStreamWriter(connection.getOutputStream());
            writer.write(postData.toString());
            writer.flush();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            // 关闭流
            try {
                if (writer != null) writer.close();
                if (reader != null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
        return response.toString();
    }
    public static String sendPostTokenFormData(String httpUrl, Map<String, Object> maps, String authorization) {
        HttpURLConnection connection = null;
        DataOutputStream outStream = null;
        BufferedReader reader = null;
        StringBuilder response = new StringBuilder();
        // 边界字符串
        String boundary = "----WebKitFormBoundary" + UUID.randomUUID().toString().replaceAll("-", "");
        String CRLF = "\r\n"; // 换行符
        try {
            // 创建 URL 对象
            URL url = new URL(httpUrl);
            // 打开连接
            connection = (HttpURLConnection) url.openConnection();
            // 设置请求方法为 POST
            connection.setRequestMethod("POST");
            // 设置请求头 Content-Type 为 multipart/form-data
            connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
            // 如果需要 Authorization Header
            if (authorization != null && !authorization.isEmpty()) {
                connection.setRequestProperty("Authorization", authorization);
            }
            // 设置允许输出
            connection.setDoOutput(true);
            // 获取输出流
            outStream = new DataOutputStream(connection.getOutputStream());
            // 遍历传入的表单数据,并按照 multipart/form-data 格式写入
            for (Map.Entry<String, Object> entry : maps.entrySet()) {
                outStream.writeBytes("--" + boundary + CRLF);
                outStream.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + CRLF);
                outStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + CRLF);
                outStream.writeBytes(CRLF);  // 这里是分隔符,空一行
                outStream.writeBytes(entry.getValue().toString() + CRLF);
            }
            // 结束 multipart/form-data 发送数据部分
            outStream.writeBytes("--" + boundary + "--" + CRLF);
            // 刷新输出流
            outStream.flush();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            // 关闭流
            try {
                if (outStream != null) outStream.close();
                if (reader != null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
        return response.toString();
    }
    /**
     * 发送 post请求
     *
     * @param httpUrl 地址
     * @param maps    参数
     */
    public String sendHttpPost(String httpUrl, Map<String, String> maps) {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        // 创建参数队列
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        for (String key : maps.keySet()) {
            nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sendHttpPost(httpPost);
    }
    /**
     * 发送Post请求
     *
     * @param httpPost
     * @return
     */
    private String sendHttpPost(HttpPost httpPost) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        String responseContent = null;
        try {
            // 创建默认的httpClient实例
            httpClient = HttpClients.createDefault();
            httpPost.setConfig(requestConfig);
            // 执行请求
            long execStart = System.currentTimeMillis();
            response = httpClient.execute(httpPost);
            long execEnd = System.currentTimeMillis();
            System.out.println("=================执行post请求耗时:" + (execEnd - execStart) + "ms");
            long getStart = System.currentTimeMillis();
            entity = response.getEntity();
            responseContent = EntityUtils.toString(entity, "UTF-8");
            long getEnd = System.currentTimeMillis();
            System.out.println("=================获取响应结果耗时:" + (getEnd - getStart) + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭连接,释放资源
                if (response != null) {
                    response.close();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseContent;
    }
    /**
     * 发送 get请求
     *
     * @param httpUrl
     */
    public String sendHttpGet(String httpUrl) {
        HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求
        return sendHttpGet(httpGet);
    }
    /**
     * 发送 get请求Https
     *
     * @param httpUrl
     */
    public String sendHttpsGet(String httpUrl) {
        HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求
        return sendHttpsGet(httpGet);
    }
    /**
     * 发送Get请求
     *
     * @param httpGet
     * @return
     */
    private String sendHttpGet(HttpGet httpGet) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        String responseContent = null;
        try {
            // 创建默认的httpClient实例.
            httpClient = HttpClients.createDefault();
            httpGet.setConfig(requestConfig);
            // 执行请求
            response = httpClient.execute(httpGet);
            entity = response.getEntity();
            responseContent = EntityUtils.toString(entity, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭连接,释放资源
                if (response != null) {
                    response.close();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseContent;
    }
    /**
     * 发送Get请求Https
     *
     * @param httpGet
     * @return
     */
    private String sendHttpsGet(HttpGet httpGet) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        String responseContent = null;
        try {
            // 创建默认的httpClient实例.
            PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(new URL(httpGet.getURI().toString()));
            DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
            httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).build();
            httpGet.setConfig(requestConfig);
            // 执行请求
            response = httpClient.execute(httpGet);
            entity = response.getEntity();
            responseContent = EntityUtils.toString(entity, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭连接,释放资源
                if (response != null) {
                    response.close();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseContent;
    }
    /**
     * 发送xml数据
     *
     * @param url
     * @param xmlData
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static HttpResponse sendXMLDataByPost(String url, String xmlData)
            throws ClientProtocolException, IOException {
        HttpClient httpClient = HttpClients.createDefault();
        HttpPost httppost = new HttpPost(url);
        StringEntity entity = new StringEntity(xmlData);
        httppost.setEntity(entity);
        httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");
        HttpResponse response = httpClient.execute(httppost);
        return response;
    }
    /**
     * 获得响应HTTP实体内容
     *
     * @param response
     * @return
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    public static String getHttpEntityContent(HttpResponse response) throws IOException, UnsupportedEncodingException {
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream is = entity.getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String line = br.readLine();
            StringBuilder sb = new StringBuilder();
            while (line != null) {
                sb.append(line + "\n");
                line = br.readLine();
            }
            return sb.toString();
        }
        return "";
    }
    public static String sendPost(String httpUrl, Map<String, Object> maps,String token) {
        try {
            // 1. 创建 URL 对象
            URL url = new URL(httpUrl);
            // 2. 创建 HttpURLConnection 对象
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 3. 设置请求方法为 POST
            connection.setRequestMethod("POST");
            // 4. 设置 Content-Type 头部字段
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            //设置请求token
            connection.setRequestProperty("Authorization",token);
            // 6. 向服务器发送数据
            String requestBody = JSONUtil.toJsonStr(maps);
            log.info(httpUrl+"入参:   "+requestBody);
//            String requestBody1 = maps.toString();
            byte[] postData = requestBody.getBytes("UTF-8");
            connection.setDoOutput(true);
            try (OutputStream outputStream = connection.getOutputStream()) {
                outputStream.write(postData);
            }
            // 8. 获取响应数据
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                StringBuilder response = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                log.info("=====================================================");
                log.info(httpUrl+"出参");
                log.info(response.toString());
                connection.disconnect();
                return response.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
src/main/java/com/example/utils/RedisCache.java
New file
@@ -0,0 +1,266 @@
package com.example.utils;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
 * spring redis 工具类
 *
 * @author ltkj
 **/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisCache {
    @Autowired
    public RedisTemplate redisTemplate;
    public RedisTemplate setDataBase(int num) {
        LettuceConnectionFactory connectionFactory = (LettuceConnectionFactory) redisTemplate.getConnectionFactory();
        if (connectionFactory != null && num != connectionFactory.getDatabase()) {
            connectionFactory.setDatabase(num);
            this.redisTemplate.setConnectionFactory(connectionFactory);
            connectionFactory.resetConnection();
            connectionFactory.afterPropertiesSet();
        }
        return redisTemplate;
    }
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public long getExpire(final String key) {
        return redisTemplate.getExpire(key);
    }
    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    public Boolean hasHKey(String key1,String key2) {
        return redisTemplate.opsForHash().hasKey(key1,key2);
    }
    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }
    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public boolean deleteObject(final Collection collection) {
        return redisTemplate.delete(collection) > 0;
    }
    /**
     * 缓存List数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * 缓存Set
     *
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add(it.next());
        }
        return setOperation;
    }
    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 往Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final Object value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    public <T> void setHashKeyExpireTime(final String key, final Long time, final TimeUnit  unit) {
        redisTemplate.expire(key,time,unit);
    }
    /**
     * 获取Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * 获取多个Hash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 删除Hash中的某条数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    public boolean deleteCacheMapValue(final String key, final String hKey) {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }
    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }
}
src/main/java/com/example/utils/SpringUtils.java
New file
@@ -0,0 +1,140 @@
package com.example.utils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
 * spring工具类 方便在非spring管理环境中获取bean
 *
 * @author ruoyi
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
    /**
     * Spring应用上下文环境
     */
    private static ConfigurableListableBeanFactory beanFactory;
    private static ApplicationContext applicationContext;
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }
    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }
    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws BeansException
     */
    public static <T> T getBean(Class<T> clz) throws BeansException {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }
    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return beanFactory.containsBean(name);
    }
    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.isSingleton(name);
    }
    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getType(name);
    }
    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getAliases(name);
    }
    /**
     * 获取aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker) {
        return (T) AopContext.currentProxy();
    }
    /**
     * 获取当前的环境配置,无配置返回null
     *
     * @return 当前的环境配置
     */
    public static String[] getActiveProfiles() {
        return applicationContext.getEnvironment().getActiveProfiles();
    }
    /**
     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
     *
     * @return 当前的环境配置
     */
    public static String getActiveProfile() {
        final String[] activeProfiles = getActiveProfiles();
        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    }
    /**
     * 获取配置文件中的值
     *
     * @param key 配置文件的key
     * @return 当前的配置文件的值
     */
    public static String getRequiredProperty(String key) {
        return applicationContext.getEnvironment().getRequiredProperty(key);
    }
}
src/main/java/com/example/utils/StringUtils.java
New file
@@ -0,0 +1,542 @@
package com.example.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.hutool.core.text.StrFormatter;
import com.example.constant.Constants;
import org.springframework.util.AntPathMatcher;
/**
 * 字符串工具类
 *
 * @author ltkj
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    /**
     * 空字符串
     */
    private static final String NULLSTR = "";
    /**
     * 下划线
     */
    private static final char SEPARATOR = '_';
    /**
     * 获取参数不为空值
     *
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue) {
        return value != null ? value : defaultValue;
    }
    /**
     * * 判断一个Collection是否为空, 包含List,Set,Queue
     *
     * @param coll 要判断的Collection
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll) {
        return isNull(coll) || coll.isEmpty();
    }
    /**
     * * 判断一个Collection是否非空,包含List,Set,Queue
     *
     * @param coll 要判断的Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll) {
        return !isEmpty(coll);
    }
    /**
     * * 判断一个对象数组是否为空
     *
     * @param objects 要判断的对象数组
     *                * @return true:为空 false:非空
     */
    public static boolean isEmpty(Object[] objects) {
        return isNull(objects) || (objects.length == 0);
    }
    /**
     * * 判断一个对象数组是否非空
     *
     * @param objects 要判断的对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects) {
        return !isEmpty(objects);
    }
    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return isNull(map) || map.isEmpty();
    }
    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }
    /**
     * * 判断一个字符串是否为空串
     *
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str) {
        return isNull(str) || NULLSTR.equals(str.trim());
    }
    /**
     * * 判断一个字符串是否为非空串
     *
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }
    /**
     * * 判断一个对象是否为空
     *
     * @param object Object
     * @return true:为空 false:非空
     */
    public static boolean isNull(Object object) {
        return object == null;
    }
    /**
     * * 判断一个对象是否非空
     *
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object) {
        return !isNull(object);
    }
    /**
     * * 判断一个对象是否是数组类型(Java基本型别的数组)
     *
     * @param object 对象
     * @return true:是数组 false:不是数组
     */
    public static boolean isArray(Object object) {
        return isNotNull(object) && object.getClass().isArray();
    }
    /**
     * 去空格
     */
    public static String trim(String str) {
        return (str == null ? "" : str.trim());
    }
    /**
     * 截取字符串
     *
     * @param str   字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start) {
        if (str == null) {
            return NULLSTR;
        }
        if (start < 0) {
            start = str.length() + start;
        }
        if (start < 0) {
            start = 0;
        }
        if (start > str.length()) {
            return NULLSTR;
        }
        return str.substring(start);
    }
    /**
     * 截取字符串
     *
     * @param str   字符串
     * @param start 开始
     * @param end   结束
     * @return 结果
     */
    public static String substring(final String str, int start, int end) {
        if (str == null) {
            return NULLSTR;
        }
        if (end < 0) {
            end = str.length() + end;
        }
        if (start < 0) {
            start = str.length() + start;
        }
        if (end > str.length()) {
            end = str.length();
        }
        if (start > end) {
            return NULLSTR;
        }
        if (start < 0) {
            start = 0;
        }
        if (end < 0) {
            end = 0;
        }
        return str.substring(start, end);
    }
    /**
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例:<br>
     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param template 文本模板,被替换的部分用 {} 表示
     * @param params   参数值
     * @return 格式化后的文本
     */
    public static String format(String template, Object... params) {
        if (isEmpty(params) || isEmpty(template)) {
            return template;
        }
        return StrFormatter.format(template, params);
    }
    /**
     * 是否为http(s)://开头
     *
     * @param link 链接
     * @return 结果
     */
    public static boolean ishttp(String link) {
        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
    }
    /**
     * 字符串转set
     *
     * @param str 字符串
     * @param sep 分隔符
     * @return set集合
     */
    public static final Set<String> str2Set(String str, String sep) {
        return new HashSet<String>(str2List(str, sep, true, false));
    }
    /**
     * 字符串转list
     *
     * @param str         字符串
     * @param sep         分隔符
     * @param filterBlank 过滤纯空白
     * @param trim        去掉首尾空白
     * @return list集合
     */
    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
        List<String> list = new ArrayList<String>();
        if (StringUtils.isEmpty(str)) {
            return list;
        }
        // 过滤空白字符串
        if (filterBlank && StringUtils.isBlank(str)) {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split) {
            if (filterBlank && StringUtils.isBlank(string)) {
                continue;
            }
            if (trim) {
                string = string.trim();
            }
            list.add(string);
        }
        return list;
    }
    /**
     * 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
     *
     * @param array 给定的数组
     * @return boolean 结果
     */
    public static boolean containsAny(Collection<String> collection, String... array) {
        if (isEmpty(collection) || isEmpty(array)) {
            return false;
        } else {
            for (String str : array) {
                if (collection.contains(str)) {
                    return true;
                }
            }
            return false;
        }
    }
    /**
     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
     *
     * @param cs                  指定字符串
     * @param searchCharSequences 需要检查的字符串数组
     * @return 是否包含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
        if (isEmpty(cs) || isEmpty(searchCharSequences)) {
            return false;
        }
        for (CharSequence testStr : searchCharSequences) {
            if (containsIgnoreCase(cs, testStr)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 驼峰转下划线命名
     */
    public static String toUnderScoreCase(String str) {
        if (str == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (i > 0) {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            } else {
                preCharIsUpperCase = false;
            }
            curreCharIsUpperCase = Character.isUpperCase(c);
            if (i < (str.length() - 1)) {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }
            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
                sb.append(SEPARATOR);
            } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }
    /**
     * 是否包含字符串
     *
     * @param str  验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs) {
        if (str != null && strs != null) {
            for (String s : strs) {
                if (str.equalsIgnoreCase(trim(s))) {
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线,仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }
    /**
     * 驼峰式命名法 例如:user_name->userName
     */
    public static String toCamelCase(String s) {
        if (s == null) {
            return null;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == SEPARATOR) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    /**
     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
     *
     * @param str  指定字符串
     * @param strs 需要检查的字符串数组
     * @return 是否匹配
     */
    public static boolean matches(String str, List<String> strs) {
        if (isEmpty(str) || isEmpty(strs)) {
            return false;
        }
        for (String pattern : strs) {
            if (isMatch(pattern, str)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 判断url是否与规则配置:
     * ? 表示单个字符;
     * * 表示一层路径内的任意字符串,不可跨层级;
     * ** 表示任意层路径;
     *
     * @param pattern 匹配规则
     * @param url     需要匹配的url
     * @return
     */
    public static boolean isMatch(String pattern, String url) {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj) {
        return (T) obj;
    }
    /**
     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
     *
     * @param num  数字对象
     * @param size 字符串指定长度
     * @return 返回数字的字符串格式,该字符串为指定长度。
     */
    public static final String padl(final Number num, final int size) {
        return padl(num.toString(), size, '0');
    }
    /**
     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
     *
     * @param s    原始字符串
     * @param size 字符串指定长度
     * @param c    用于补齐的字符
     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
     */
    public static final String padl(final String s, final int size, final char c) {
        final StringBuilder sb = new StringBuilder(size);
        if (s != null) {
            final int len = s.length();
            if (s.length() <= size) {
                for (int i = size - len; i > 0; i--) {
                    sb.append(c);
                }
                sb.append(s);
            } else {
                return s.substring(len - size, len);
            }
        } else {
            for (int i = size; i > 0; i--) {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    //根据年龄 获取年龄段  0-1婴儿 1-4幼儿 5-11儿童 12-18少年 19-35青年 36-59中年 60+老年
    public static int getAgeType(Integer age) {
        int type = 0;
        if (1 < age && age < 4) {
            type = 1;
        } else if (5 < age && age < 11) {
            type = 2;
        } else if (12 < age && age < 18) {
            type = 3;
        } else if (19 < age && age < 35) {
            type = 4;
        } else if (36 < age && age < 59) {
            type = 5;
        } else if (60 < age && age < 150) {
            type = 6;
        }
        return type;
    }
}
src/main/java/com/example/utils/text/CharsetKit.java
New file
@@ -0,0 +1,91 @@
package com.example.utils.text;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.example.utils.StringUtils;
/**
 * 字符集工具类
 *
 * @author ltkj
 */
public class CharsetKit {
    /**
     * ISO-8859-1
     */
    public static final String ISO_8859_1 = "ISO-8859-1";
    /**
     * UTF-8
     */
    public static final String UTF_8 = "UTF-8";
    /**
     * GBK
     */
    public static final String GBK = "GBK";
    /**
     * ISO-8859-1
     */
    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
    /**
     * UTF-8
     */
    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
    /**
     * GBK
     */
    public static final Charset CHARSET_GBK = Charset.forName(GBK);
    /**
     * 转换为Charset对象
     *
     * @param charset 字符集,为空则返回默认字符集
     * @return Charset
     */
    public static Charset charset(String charset) {
        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
    }
    /**
     * 转换字符串的字符集编码
     *
     * @param source      字符串
     * @param srcCharset  源字符集,默认ISO-8859-1
     * @param destCharset 目标字符集,默认UTF-8
     * @return 转换后的字符集
     */
    public static String convert(String source, String srcCharset, String destCharset) {
        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
    }
    /**
     * 转换字符串的字符集编码
     *
     * @param source      字符串
     * @param srcCharset  源字符集,默认ISO-8859-1
     * @param destCharset 目标字符集,默认UTF-8
     * @return 转换后的字符集
     */
    public static String convert(String source, Charset srcCharset, Charset destCharset) {
        if (null == srcCharset) {
            srcCharset = StandardCharsets.ISO_8859_1;
        }
        if (null == destCharset) {
            destCharset = StandardCharsets.UTF_8;
        }
        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) {
            return source;
        }
        return new String(source.getBytes(srcCharset), destCharset);
    }
    /**
     * @return 系统字符集编码
     */
    public static String systemCharset() {
        return Charset.defaultCharset().name();
    }
}
src/main/java/com/example/utils/text/Convert.java
New file
@@ -0,0 +1,849 @@
package com.example.utils.text;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.example.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
/**
 * 类型转换器
 *
 * @author ltkj
 */
public class Convert {
    /**
     * 转换为字符串<br>
     * 如果给定的值为null,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static String toStr(Object value, String defaultValue) {
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof String) {
            return (String) value;
        }
        return value.toString();
    }
    /**
     * 转换为字符串<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static String toStr(Object value) {
        return toStr(value, null);
    }
    /**
     * 转换为字符<br>
     * 如果给定的值为null,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Character toChar(Object value, Character defaultValue) {
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Character) {
            return (Character) value;
        }
        final String valueStr = toStr(value, null);
        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
    }
    /**
     * 转换为字符<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Character toChar(Object value) {
        return toChar(value, null);
    }
    /**
     * 转换为byte<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Byte toByte(Object value, Byte defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Byte) {
            return (Byte) value;
        }
        if (value instanceof Number) {
            return ((Number) value).byteValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return Byte.parseByte(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为byte<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Byte toByte(Object value) {
        return toByte(value, null);
    }
    /**
     * 转换为Short<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Short toShort(Object value, Short defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Short) {
            return (Short) value;
        }
        if (value instanceof Number) {
            return ((Number) value).shortValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return Short.parseShort(valueStr.trim());
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为Short<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Short toShort(Object value) {
        return toShort(value, null);
    }
    /**
     * 转换为Number<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Number toNumber(Object value, Number defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return (Number) value;
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return NumberFormat.getInstance().parse(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为Number<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Number toNumber(Object value) {
        return toNumber(value, null);
    }
    /**
     * 转换为int<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Integer toInt(Object value, Integer defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Integer) {
            return (Integer) value;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(valueStr.trim());
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为int<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Integer toInt(Object value) {
        return toInt(value, null);
    }
    /**
     * 转换为Integer数组<br>
     *
     * @param str 被转换的值
     * @return 结果
     */
    public static Integer[] toIntArray(String str) {
        return toIntArray(",", str);
    }
    /**
     * 转换为Long数组<br>
     *
     * @param str 被转换的值
     * @return 结果
     */
    public static Long[] toLongArray(String str) {
        return toLongArray(",", str);
    }
    /**
     * 转换为Integer数组<br>
     *
     * @param split 分隔符
     * @param split 被转换的值
     * @return 结果
     */
    public static Integer[] toIntArray(String split, String str) {
        if (StringUtils.isEmpty(str)) {
            return new Integer[]{};
        }
        String[] arr = str.split(split);
        final Integer[] ints = new Integer[arr.length];
        for (int i = 0; i < arr.length; i++) {
            final Integer v = toInt(arr[i], 0);
            ints[i] = v;
        }
        return ints;
    }
    /**
     * 转换为Long数组<br>
     *
     * @param split 分隔符
     * @param str   被转换的值
     * @return 结果
     */
    public static Long[] toLongArray(String split, String str) {
        if (StringUtils.isEmpty(str)) {
            return new Long[]{};
        }
        String[] arr = str.split(split);
        final Long[] longs = new Long[arr.length];
        for (int i = 0; i < arr.length; i++) {
            final Long v = toLong(arr[i], null);
            longs[i] = v;
        }
        return longs;
    }
    /**
     * 转换为String数组<br>
     *
     * @param str 被转换的值
     * @return 结果
     */
    public static String[] toStrArray(String str) {
        return toStrArray(",", str);
    }
    /**
     * 转换为String数组<br>
     *
     * @param split 分隔符
     * @param split 被转换的值
     * @return 结果
     */
    public static String[] toStrArray(String split, String str) {
        return str.split(split);
    }
    /**
     * 转换为long<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Long toLong(Object value, Long defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Long) {
            return (Long) value;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            // 支持科学计数法
            return new BigDecimal(valueStr.trim()).longValue();
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为long<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Long toLong(Object value) {
        return toLong(value, null);
    }
    /**
     * 转换为double<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Double toDouble(Object value, Double defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Double) {
            return (Double) value;
        }
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            // 支持科学计数法
            return new BigDecimal(valueStr.trim()).doubleValue();
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为double<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Double toDouble(Object value) {
        return toDouble(value, null);
    }
    /**
     * 转换为Float<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Float toFloat(Object value, Float defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Float) {
            return (Float) value;
        }
        if (value instanceof Number) {
            return ((Number) value).floatValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return Float.parseFloat(valueStr.trim());
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为Float<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Float toFloat(Object value) {
        return toFloat(value, null);
    }
    /**
     * 转换为boolean<br>
     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Boolean toBool(Object value, Boolean defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        valueStr = valueStr.trim().toLowerCase();
        switch (valueStr) {
            case "true":
            case "yes":
            case "ok":
            case "1":
                return true;
            case "false":
            case "no":
            case "0":
                return false;
            default:
                return defaultValue;
        }
    }
    /**
     * 转换为boolean<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Boolean toBool(Object value) {
        return toBool(value, null);
    }
    /**
     * 转换为Enum对象<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     *
     * @param clazz        Enum的Class
     * @param value        值
     * @param defaultValue 默认值
     * @return Enum
     */
    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (clazz.isAssignableFrom(value.getClass())) {
            @SuppressWarnings("unchecked")
            E myE = (E) value;
            return myE;
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return Enum.valueOf(clazz, valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为Enum对象<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     *
     * @param clazz Enum的Class
     * @param value 值
     * @return Enum
     */
    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) {
        return toEnum(clazz, value, null);
    }
    /**
     * 转换为BigInteger<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static BigInteger toBigInteger(Object value, BigInteger defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof BigInteger) {
            return (BigInteger) value;
        }
        if (value instanceof Long) {
            return BigInteger.valueOf((Long) value);
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return new BigInteger(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为BigInteger<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static BigInteger toBigInteger(Object value) {
        return toBigInteger(value, null);
    }
    /**
     * 转换为BigDecimal<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        if (value instanceof Long) {
            return new BigDecimal((Long) value);
        }
        if (value instanceof Double) {
            return new BigDecimal((Double) value);
        }
        if (value instanceof Integer) {
            return new BigDecimal((Integer) value);
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr)) {
            return defaultValue;
        }
        try {
            return new BigDecimal(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 转换为BigDecimal<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static BigDecimal toBigDecimal(Object value) {
        return toBigDecimal(value, null);
    }
    /**
     * 将对象转为字符串<br>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
     *
     * @param obj 对象
     * @return 字符串
     */
    public static String utf8Str(Object obj) {
        return str(obj, CharsetKit.CHARSET_UTF_8);
    }
    /**
     * 将对象转为字符串<br>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
     *
     * @param obj         对象
     * @param charsetName 字符集
     * @return 字符串
     */
    public static String str(Object obj, String charsetName) {
        return str(obj, Charset.forName(charsetName));
    }
    /**
     * 将对象转为字符串<br>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
     *
     * @param obj     对象
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(Object obj, Charset charset) {
        if (null == obj) {
            return null;
        }
        if (obj instanceof String) {
            return (String) obj;
        } else if (obj instanceof byte[]) {
            return str((byte[]) obj, charset);
        } else if (obj instanceof Byte[]) {
            byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
            return str(bytes, charset);
        } else if (obj instanceof ByteBuffer) {
            return str((ByteBuffer) obj, charset);
        }
        return obj.toString();
    }
    /**
     * 将byte数组转为字符串
     *
     * @param bytes   byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(byte[] bytes, String charset) {
        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
    }
    /**
     * 解码字节码
     *
     * @param data    字符串
     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
     * @return 解码后的字符串
     */
    public static String str(byte[] data, Charset charset) {
        if (data == null) {
            return null;
        }
        if (null == charset) {
            return new String(data);
        }
        return new String(data, charset);
    }
    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集,如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, String charset) {
        if (data == null) {
            return null;
        }
        return str(data, Charset.forName(charset));
    }
    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集,如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, Charset charset) {
        if (null == charset) {
            charset = Charset.defaultCharset();
        }
        return charset.decode(data).toString();
    }
    // ----------------------------------------------------------------------- 全角半角转换
    /**
     * 半角转全角
     *
     * @param input String.
     * @return 全角字符串.
     */
    public static String toSBC(String input) {
        return toSBC(input, null);
    }
    /**
     * 半角转全角
     *
     * @param input         String
     * @param notConvertSet 不替换的字符集合
     * @return 全角字符串.
     */
    public static String toSBC(String input, Set<Character> notConvertSet) {
        char[] c = input.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (null != notConvertSet && notConvertSet.contains(c[i])) {
                // 跳过不替换的字符
                continue;
            }
            if (c[i] == ' ') {
                c[i] = '\u3000';
            } else if (c[i] < '\177') {
                c[i] = (char) (c[i] + 65248);
            }
        }
        return new String(c);
    }
    /**
     * 全角转半角
     *
     * @param input String.
     * @return 半角字符串
     */
    public static String toDBC(String input) {
        return toDBC(input, null);
    }
    /**
     * 替换全角为半角
     *
     * @param text          文本
     * @param notConvertSet 不替换的字符集合
     * @return 替换后的字符
     */
    public static String toDBC(String text, Set<Character> notConvertSet) {
        char[] c = text.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (null != notConvertSet && notConvertSet.contains(c[i])) {
                // 跳过不替换的字符
                continue;
            }
            if (c[i] == '\u3000') {
                c[i] = ' ';
            } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
                c[i] = (char) (c[i] - 65248);
            }
        }
        String returnString = new String(c);
        return returnString;
    }
    /**
     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
     *
     * @param n 数字
     * @return 中文大写数字
     */
    public static String digitUppercase(double n) {
        String[] fraction = {"角", "分"};
        String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
        String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};
        String head = n < 0 ? "负" : "";
        n = Math.abs(n);
        String s = "";
        for (int i = 0; i < fraction.length; i++) {
            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
        }
        if (s.length() < 1) {
            s = "整";
        }
        int integerPart = (int) Math.floor(n);
        for (int i = 0; i < unit[0].length && integerPart > 0; i++) {
            String p = "";
            for (int j = 0; j < unit[1].length && n > 0; j++) {
                p = digit[integerPart % 10] + unit[1][j] + p;
                integerPart = integerPart / 10;
            }
            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
        }
        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
    }
}
src/main/java/com/example/utils/text/StrFormatter.java
New file
@@ -0,0 +1,76 @@
package com.example.utils.text;
import com.example.utils.StringUtils;
/**
 * 字符串格式化
 *
 * @author ltkj
 */
public class StrFormatter {
    public static final String EMPTY_JSON = "{}";
    public static final char C_BACKSLASH = '\\';
    public static final char C_DELIM_START = '{';
    public static final char C_DELIM_END = '}';
    /**
     * 格式化字符串<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例:<br>
     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param strPattern 字符串模板
     * @param argArray   参数列表
     * @return 结果
     */
    public static String format(final String strPattern, final Object... argArray) {
        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) {
            return strPattern;
        }
        final int strPatternLength = strPattern.length();
        // 初始化定义好的长度以获得更好的性能
        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
        int handledPosition = 0;
        int delimIndex;// 占位符所在位置
        for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
            if (delimIndex == -1) {
                if (handledPosition == 0) {
                    return strPattern;
                } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
                    sbuf.append(strPattern, handledPosition, strPatternLength);
                    return sbuf.toString();
                }
            } else {
                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
                        // 转义符之前还有一个转义符,占位符依旧有效
                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
                        handledPosition = delimIndex + 2;
                    } else {
                        // 占位符被转义
                        argIndex--;
                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
                        sbuf.append(C_DELIM_START);
                        handledPosition = delimIndex + 1;
                    }
                } else {
                    // 正常占位符
                    sbuf.append(strPattern, handledPosition, delimIndex);
                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
                    handledPosition = delimIndex + 2;
                }
            }
        }
        // 加入最后一个占位符后所有的字符
        sbuf.append(strPattern, handledPosition, strPattern.length());
        return sbuf.toString();
    }
}
src/main/resources/application-linux.yaml
New file
@@ -0,0 +1,3 @@
config:
  path: /ltkj/ltkjprojectconf/config.properties
  dir: /ltkj/ltkjprojectconf
src/main/resources/application-win.yaml
New file
@@ -0,0 +1,3 @@
config:
  path: D:\ltkjprojectconf\config.properties
  dir: D:\ltkjprojectconf
src/main/resources/application.properties
File was deleted
src/main/resources/application.yaml
@@ -1,3 +1,8 @@
server:
  port: 14765
  servlet:
    context-path: /
mybatis-plus:
  # 搜索指定包别名
  typeAliasesPackage: com.example.domain
@@ -9,15 +14,12 @@
      logic-delete-value: 1
      update-strategy: not_null
spring:
  profiles:
    active: win
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://101.42.27.146:3306/ltkj_tj1.0?serverTimezone=GMT%2B8
        username: root
        password: Root_ltkj123
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
@@ -57,6 +59,3 @@
        wall:
          config:
            multi-statement-allow: true
config:
  path: D:\ltkjprojectconf\config.properties
  dir: D:\ltkjprojectconf
src/main/resources/logback.xml
@@ -1,25 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 日志存放路径  windows环境-->
    <springProfile name="win">
        <property name="log.path" value="logs"/>
    </springProfile>
    <!-- 输出到文件中 -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>src/main/resources/log.log</file>
        <append>true</append>
    <!-- 日志存放路径  linux环境-->
    <springProfile name="linux">
        <property name="log.path" value="/ltkj/jar/logs/sjpt"/>
    </springProfile>
    <!-- 日志输出格式 -->
    <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 彩色日志 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %d{yyyy-MM-dd HH:mm:ss} [%thread] %yellow(%-5level) %white([%-50.50class]) >>> %green(%msg) %n
            </pattern>
        </layout>
    </appender>
    <!-- 定义日志级别 -->
    <root level="INFO">
        <appender-ref ref="FILE"/>
        <appender-ref ref="STDOUT"/>
    <!-- 系统日志输出 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- 用户访问日志输出  -->
    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-user.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天回滚 daily -->
            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
<!--    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">-->
<!--        &lt;!&ndash; ... 其他配置 ... &ndash;&gt;-->
<!--        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!--            &lt;!&ndash; ... 其他配置 ... &ndash;&gt;-->
<!--            &lt;!&ndash; 设置最长保留期限为 7 天 &ndash;&gt;-->
<!--            <maxDays>7</maxDays>-->
<!--        </rollingPolicy>-->
<!--    </appender>-->
    <!-- 系统模块日志级别控制  -->
    <logger name="com.ltkj" level="info"/>
    <!-- Spring日志级别控制  -->
    <logger name="org.springframework" level="warn"/>
    <root level="info">
        <appender-ref ref="console"/>
    </root>
    <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
    <!--系统用户操作日志-->
    <logger name="sys-user" level="info">
        <appender-ref ref="sys-user"/>
    </logger>
</configuration>
src/main/resources/mapper/SysConfigMapper.xml
New file
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.SysConfigMapper">
    <resultMap type="SysConfig" id="SysConfigResult">
        <id property="configId" column="config_id"/>
        <result property="configName" column="config_name"/>
        <result property="configKey" column="config_key"/>
        <result property="configValue" column="config_value"/>
        <result property="configType" column="config_type"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
    </resultMap>
    <sql id="selectConfigVo">
        select config_id,
               config_name,
               config_key,
               config_value,
               config_type,
               create_by,
               create_time,
               update_by,
               update_time,
               remark
        from sys_config
    </sql>
    <!-- 查询条件 -->
    <sql id="sqlwhereSearch">
        <where>
            <if test="configId !=null">
                and config_id = #{configId}
            </if>
            <if test="configKey !=null and configKey != ''">
                and config_key = #{configKey}
            </if>
        </where>
    </sql>
    <select id="selectConfig" parameterType="SysConfig" resultMap="SysConfigResult">
        <include refid="selectConfigVo"/>
        <include refid="sqlwhereSearch"/>
    </select>
    <select id="selectConfigList" parameterType="SysConfig" resultMap="SysConfigResult">
        <include refid="selectConfigVo"/>
        <where>
            <if test="configName != null and configName != ''">
                AND config_name like concat('%', #{configName}, '%')
            </if>
            <if test="configType != null and configType != ''">
                AND config_type = #{configType}
            </if>
            <if test="configKey != null and configKey != ''">
                AND config_key like concat('%', #{configKey}, '%')
            </if>
            <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
                and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
            </if>
            <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
                and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
            </if>
        </where>
    </select>
    <select id="checkConfigKeyUnique" parameterType="String" resultMap="SysConfigResult">
        <include refid="selectConfigVo"/>
        where config_key = #{configKey} limit 1
    </select>
    <insert id="insertConfig" parameterType="SysConfig">
        insert into sys_config (
        <if test="configName != null and configName != '' ">config_name,</if>
        <if test="configKey != null and configKey != '' ">config_key,</if>
        <if test="configValue != null and configValue != '' ">config_value,</if>
        <if test="configType != null and configType != '' ">config_type,</if>
        <if test="createBy != null and createBy != ''">create_by,</if>
        <if test="remark != null and remark != ''">remark,</if>
        create_time
        )values(
        <if test="configName != null and configName != ''">#{configName},</if>
        <if test="configKey != null and configKey != ''">#{configKey},</if>
        <if test="configValue != null and configValue != ''">#{configValue},</if>
        <if test="configType != null and configType != ''">#{configType},</if>
        <if test="createBy != null and createBy != ''">#{createBy},</if>
        <if test="remark != null and remark != ''">#{remark},</if>
        sysdate()
        )
    </insert>
    <update id="updateConfig" parameterType="SysConfig">
        update sys_config
        <set>
            <if test="configName != null and configName != ''">config_name = #{configName},</if>
            <if test="configKey != null and configKey != ''">config_key = #{configKey},</if>
            <if test="configValue != null and configValue != ''">config_value = #{configValue},</if>
            <if test="configType != null and configType != ''">config_type = #{configType},</if>
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            <if test="remark != null">remark = #{remark},</if>
            update_time = sysdate()
        </set>
        where config_id = #{configId}
    </update>
    <delete id="deleteConfigById" parameterType="Long">
        delete
        from sys_config
        where config_id = #{configId}
    </delete>
    <delete id="deleteConfigByIds" parameterType="Long">
        delete from sys_config where config_id in
        <foreach item="configId" collection="array" open="(" separator="," close=")">
            #{configId}
        </foreach>
    </delete>
    <select id="tbhisproprice">
        {call tb_hisprodj()}
    </select>
</mapper>