commit
9541b7a882
52 changed files with 3491 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
.idea/ |
|||
jsl-cost-fast.iml |
@ -0,0 +1,36 @@ |
|||
# 嘉士利核销系统JAVA |
|||
|
|||
#### Description |
|||
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} |
|||
|
|||
#### Software Architecture |
|||
Software architecture description |
|||
|
|||
#### Installation |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### Instructions |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### Contribution |
|||
|
|||
1. Fork the repository |
|||
2. Create Feat_xxx branch |
|||
3. Commit your code |
|||
4. Create Pull Request |
|||
|
|||
|
|||
#### Gitee Feature |
|||
|
|||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md |
|||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com) |
|||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) |
|||
4. The most valuable open source project [GVP](https://gitee.com/gvp) |
|||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) |
|||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
@ -0,0 +1,39 @@ |
|||
# 嘉士利核销系统JAVA |
|||
|
|||
#### 介绍 |
|||
{**以下是 Gitee 平台说明,您可以替换此简介** |
|||
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 |
|||
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} |
|||
|
|||
#### 软件架构 |
|||
软件架构说明 |
|||
|
|||
|
|||
#### 安装教程 |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### 使用说明 |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### 参与贡献 |
|||
|
|||
1. Fork 本仓库 |
|||
2. 新建 Feat_xxx 分支 |
|||
3. 提交代码 |
|||
4. 新建 Pull Request |
|||
|
|||
|
|||
#### 特技 |
|||
|
|||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md |
|||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) |
|||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 |
|||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 |
|||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) |
|||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
@ -0,0 +1,122 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<!-- 核 心 依 赖 --> |
|||
<parent> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>2.7.0</version> |
|||
<relativePath/> |
|||
</parent> |
|||
<!-- 仓 库 地 址 --> |
|||
<version>1.0.0</version> |
|||
<!-- 项 目 描 述 --> |
|||
<name>JSL订单服务拓展</name> |
|||
<artifactId>order-ext</artifactId> |
|||
<description>JSL订单服务拓展</description> |
|||
|
|||
<!-- 版 本 管 理 --> |
|||
<properties> |
|||
<skipTests>true</skipTests> |
|||
<!-- JDK 版本 --> |
|||
<java.version>1.8</java.version> |
|||
<!-- Mysql 版 本 --> |
|||
<mysql.version>8.0.28</mysql.version> |
|||
<!-- MyBatis 版 本 --> |
|||
<mybatis.plus.version>3.5.1</mybatis.plus.version> |
|||
<!-- Fast Json 序 列 化 版 本 --> |
|||
<fastjson.version>1.2.79</fastjson.version> |
|||
<!-- Lombok 版 本 --> |
|||
<lombok.version>1.18.22</lombok.version> |
|||
<!-- jackson --> |
|||
<jackson.version>2.13.0</jackson.version> |
|||
<!-- 网 路 工 具 --> |
|||
<httpclient.version>4.5.7</httpclient.version> |
|||
<commons-httpclient.version>3.1</commons-httpclient.version> |
|||
<!-- Maven 配 置 编 译 JDK 版 本--> |
|||
<maven.compiler.target>8</maven.compiler.target> |
|||
<maven.compiler.source>8</maven.compiler.source> |
|||
<maven-resources-plugin.version>3.1.0</maven-resources-plugin.version> |
|||
</properties> |
|||
|
|||
<!-- 依 赖 管 理 --> |
|||
<dependencies> |
|||
<!-- Web 模块 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
<!-- 排除Tomcat依赖 --> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-tomcat</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<!-- Undertow依赖 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-undertow</artifactId> |
|||
</dependency> |
|||
<!-- 参 数 验 证 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-validation</artifactId> |
|||
</dependency> |
|||
<!-- 数 据 库 连 接 工 具 --> |
|||
<dependency> |
|||
<groupId>mysql</groupId> |
|||
<artifactId>mysql-connector-java</artifactId> |
|||
<version>${mysql.version}</version> |
|||
</dependency> |
|||
<!-- 数 据 库 操 作 框 架 --> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-boot-starter</artifactId> |
|||
<version>${mybatis.plus.version}</version> |
|||
</dependency> |
|||
<!-- JSON 解 析 工 具 --> |
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>fastjson</artifactId> |
|||
<version>${fastjson.version}</version> |
|||
</dependency> |
|||
<!-- 网络工具 --> |
|||
<dependency> |
|||
<groupId>org.apache.httpcomponents</groupId> |
|||
<artifactId>httpclient</artifactId> |
|||
<version>${httpclient.version}</version> |
|||
<scope>compile</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>commons-httpclient</groupId> |
|||
<artifactId>commons-httpclient</artifactId> |
|||
<version>${commons-httpclient.version}</version> |
|||
</dependency> |
|||
<!-- Lombok --> |
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
<version>${lombok.version}</version> |
|||
<scope>provided</scope> |
|||
</dependency> |
|||
<!-- hutool --> |
|||
<dependency> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-all</artifactId> |
|||
<version>5.6.2</version> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
<!-- 构建工具 --> |
|||
<build> |
|||
<!-- Maven 构 建 插 件 --> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
</project> |
@ -0,0 +1,42 @@ |
|||
package com.qs.cost; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.boot.builder.SpringApplicationBuilder; |
|||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; |
|||
import org.springframework.scheduling.annotation.EnableScheduling; |
|||
import org.springframework.transaction.annotation.EnableTransactionManagement; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.servlet.ServletContext; |
|||
import javax.servlet.ServletException; |
|||
import java.util.TimeZone; |
|||
|
|||
/** |
|||
* @author USER |
|||
*/ |
|||
@EnableScheduling |
|||
@EnableTransactionManagement |
|||
@SpringBootApplication |
|||
public class Application extends SpringBootServletInitializer { |
|||
|
|||
public static void main(String[] args) { |
|||
SpringApplication.run(Application.class,args); |
|||
} |
|||
|
|||
@Override |
|||
public void onStartup(ServletContext servletContext) throws ServletException { |
|||
super.onStartup(servletContext); |
|||
} |
|||
|
|||
@Override |
|||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { |
|||
return builder.sources(Application.class); |
|||
} |
|||
|
|||
@PostConstruct |
|||
void started() { |
|||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.qs.cost.common.conf; |
|||
|
|||
import org.springframework.cache.annotation.EnableCaching; |
|||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@EnableCaching |
|||
@Configuration |
|||
public class CacheConfig { |
|||
|
|||
@Bean |
|||
public ConcurrentMapCacheManager cacheManager() { |
|||
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); |
|||
//cacheManager.setStoreByValue(true); //true表示缓存一份副本,否则缓存引用
|
|||
return cacheManager; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.qs.cost.common.conf; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonInclude; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Primary; |
|||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; |
|||
|
|||
/** |
|||
* @Author: YenHex |
|||
* @Date: 2021/3/11 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Slf4j |
|||
@Configuration |
|||
public class JacksonConfig { |
|||
@Bean |
|||
@Primary |
|||
@ConditionalOnMissingBean(ObjectMapper.class) |
|||
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { |
|||
ObjectMapper objectMapper = builder.createXmlMapper(false).build(); |
|||
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); |
|||
return objectMapper; |
|||
} |
|||
} |
@ -0,0 +1,40 @@ |
|||
package com.qs.cost.common.conf; |
|||
|
|||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.mybatis.spring.annotation.MapperScan; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.transaction.annotation.EnableTransactionManagement; |
|||
|
|||
|
|||
/** |
|||
* @Author: YenHex |
|||
* @Date: 2021/3/4 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Slf4j |
|||
@Configuration |
|||
@EnableTransactionManagement |
|||
@MapperScan({ |
|||
"com.qs.cost.module.mapper", |
|||
}) |
|||
public class MyBatisConfig { |
|||
|
|||
|
|||
@Bean |
|||
public MybatisPlusInterceptor mybatisPlusInterceptor() { |
|||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
|||
// 添加防止全表更新与删除拦截器
|
|||
interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); |
|||
return interceptor; |
|||
} |
|||
|
|||
public BlockAttackInnerInterceptor blockAttackInnerInterceptor(){ |
|||
BlockAttackInnerInterceptor blockAttackInnerInterceptor=new BlockAttackInnerInterceptor(); |
|||
return blockAttackInnerInterceptor; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.cost.common.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
public class ApiStatusConst { |
|||
|
|||
/** 无需回调 */ |
|||
public static final int ApiRequest_Status_0 = 0; |
|||
|
|||
/** 等待回调 */ |
|||
public static final int ApiRequest_Status_1 = 1; |
|||
|
|||
/** 请求U8失败 */ |
|||
public static final int ApiRequest_Status_2 = 2; |
|||
|
|||
/** 请求U8成功,但回调客户端失败 */ |
|||
public static final int ApiRequest_Status_3 = 3; |
|||
|
|||
/** 回调客户端成功 */ |
|||
public static final int ApiRequest_Status_4 = 4; |
|||
|
|||
} |
@ -0,0 +1,48 @@ |
|||
package com.qs.cost.common.consts; |
|||
|
|||
/** |
|||
* ERP API地址 |
|||
* @Author: YenHex |
|||
* @Date: 2021/3/12 |
|||
* @Version: 1.0 |
|||
**/ |
|||
public class ApiUrlConst { |
|||
|
|||
public static final String U8API = "/Service/13Service.asmx/U8API"; |
|||
|
|||
/** |
|||
* 删除专业发票 |
|||
*/ |
|||
public static final String U8_DEL_ZYFP = "ZYFPDelete"; |
|||
|
|||
/** |
|||
* 删除普通发票 |
|||
*/ |
|||
public static final String U8_DEL_PTFP = "PTFPDelete"; |
|||
|
|||
/** |
|||
* 添加销售订单 |
|||
*/ |
|||
public static final String U8_ADD_XSDD = "XSDDAdd"; |
|||
|
|||
/** |
|||
* 添加销售报价单 |
|||
*/ |
|||
public static final String U8_ADD_XSBJ = "XSBJDAdd"; |
|||
|
|||
/** |
|||
* 添加专业发票 |
|||
*/ |
|||
public static final String U8_ADD_ZYFP = "ZYFPAdd"; |
|||
|
|||
/** |
|||
* 添加普通发票 |
|||
*/ |
|||
public static final String U8_ADD_PTFP = "PTFPAdd"; |
|||
|
|||
/** |
|||
* 获取销售订单编码 |
|||
*/ |
|||
public static final String U8_GET_XSDD_CODE = "GETXSDDCODE"; |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.cost.common.dto; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@AllArgsConstructor |
|||
public class BeanParams{ |
|||
private String json; |
|||
private String keyId; |
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.qs.cost.common.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 对请求结果统一封装 |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@Data |
|||
public class HttpResult { |
|||
|
|||
public HttpResult(Integer code,String message){ |
|||
this.message = message; |
|||
this.code = code; |
|||
} |
|||
|
|||
public HttpResult(Integer code,String message,String data){ |
|||
this.data = data; |
|||
this.message = message; |
|||
this.code = code; |
|||
} |
|||
|
|||
private String message; |
|||
|
|||
private Integer code; |
|||
|
|||
private String data; |
|||
|
|||
} |
@ -0,0 +1,45 @@ |
|||
package com.qs.cost.common.dto; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.util.Date; |
|||
import java.util.List; |
|||
|
|||
@Getter |
|||
@Setter |
|||
public class PageVo<T>{ |
|||
|
|||
/** |
|||
* 页幅 |
|||
*/ |
|||
private Integer pageSize; |
|||
|
|||
/** |
|||
* 页眉 |
|||
*/ |
|||
private Integer pageNum; |
|||
|
|||
/** |
|||
* 总数据数 |
|||
*/ |
|||
private Integer total; |
|||
|
|||
/** |
|||
* 总页数 |
|||
*/ |
|||
private Integer totalPage; |
|||
|
|||
/** |
|||
* 数据列表 |
|||
*/ |
|||
private List<T> list; |
|||
|
|||
public void initPageNum(Integer total,Integer pageSize,Integer pageNum){ |
|||
this.pageNum = pageNum; |
|||
this.pageSize = pageSize; |
|||
if(pageSize==null||pageSize==0){ return; } |
|||
this.totalPage = total%pageSize==0?total/pageSize:(total/pageSize+1); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,105 @@ |
|||
package com.qs.cost.common.dto; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 统一返回封装 |
|||
* 1.替代旧版的ViewResult |
|||
* 2.支持SmartDoc |
|||
* @author JcYen |
|||
* @date 2021/4/22 |
|||
* @version 2.0 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class R<T>{ |
|||
|
|||
public R( int status, String message) { |
|||
this.status = status; |
|||
this.message = message; |
|||
} |
|||
|
|||
public R( int status, String message, T data) { |
|||
this.status = status; |
|||
this.message = message; |
|||
this.data = data; |
|||
} |
|||
|
|||
/** |
|||
* 状态码 |
|||
*/ |
|||
private Integer status; |
|||
|
|||
/** |
|||
* 信息 |
|||
*/ |
|||
private String message; |
|||
|
|||
/** |
|||
* 数据 |
|||
*/ |
|||
private T data; |
|||
|
|||
public static final int SUCCESS_STATUS = 200; |
|||
public static final int LOGOUT_STATUS = 401; |
|||
public static final int FORBIDDEN_STATUS = 403; |
|||
public static final int NOT_FOUND_STATUS = 434; |
|||
public static final int FAILED_STATUS = 500; |
|||
|
|||
public static final String SUCCESS_TIPS = "操作成功"; |
|||
public static final String FAILED_TIPS = "操作失败"; |
|||
public static final String LOGOUT_TIPS = "登录无效"; |
|||
public static final String FORBIDDEN_TIPS = "无权限访问"; |
|||
public static final String NOT_FOUND_TIPS = "数据不存在或被移除"; |
|||
|
|||
public static R ok() { |
|||
return new R(SUCCESS_STATUS,SUCCESS_TIPS,null); |
|||
} |
|||
|
|||
public static R ok(String msg) { |
|||
return new R<>(SUCCESS_STATUS,msg,null); |
|||
} |
|||
|
|||
public static <TYPE> R<TYPE> ok(TYPE data) { |
|||
return new R<>(SUCCESS_STATUS,SUCCESS_TIPS,data); |
|||
} |
|||
|
|||
public static <TYPE> R<TYPE> ok(TYPE data,String message) { |
|||
return new R<>(SUCCESS_STATUS,message,data); |
|||
} |
|||
|
|||
public static R isTrue(Boolean bool){ |
|||
if(bool==null|| !bool){ |
|||
return error(); |
|||
} |
|||
return ok(); |
|||
} |
|||
|
|||
public R isTrue(Boolean bool, String message){ |
|||
if(bool==null|| !bool){ |
|||
return error(message); |
|||
} |
|||
return ok(null,message); |
|||
} |
|||
|
|||
|
|||
public static <TYPE> R<TYPE> isNotNull(TYPE object){ |
|||
if(object==null){ |
|||
return error(); |
|||
} |
|||
return ok(object,SUCCESS_TIPS); |
|||
} |
|||
|
|||
public static R error(String message) { |
|||
return new R<>(FAILED_STATUS,message,null); |
|||
} |
|||
|
|||
public static R error() { |
|||
return new R<>(FAILED_STATUS,FAILED_TIPS,null); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,44 @@ |
|||
package com.qs.cost.common.dto; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonAutoDetect; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import com.qs.cost.module.domain.dto.JobDataBaseParam; |
|||
import com.qs.cost.module.domain.dto.JobErpParam; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
*/ |
|||
@Builder |
|||
@Getter |
|||
@Setter |
|||
@JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY,getterVisibility= JsonAutoDetect.Visibility.NONE) |
|||
public class U8APIBo { |
|||
|
|||
@JsonProperty("Cmd") |
|||
private String cmd; |
|||
|
|||
@JsonProperty("Params") |
|||
private BeanParams params; |
|||
|
|||
@JsonProperty("SetYear") |
|||
private String setYear; |
|||
|
|||
@JsonProperty("SetBook") |
|||
private String setBook; |
|||
|
|||
@JsonProperty("RequestId") |
|||
private String requestId; |
|||
|
|||
//@JsonProperty("CallbackEnable")
|
|||
//private String callbackEnable;
|
|||
|
|||
@JsonProperty("ErpUser") |
|||
private JobErpParam erpUser; |
|||
|
|||
@JsonProperty("DataBase") |
|||
private JobDataBaseParam dataBase; |
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.cost.common.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* 请求状态码 |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum HttpCode { |
|||
|
|||
/** 基本参数 */ |
|||
SUCCESS(200,"操作成功"), |
|||
ERROR(200,"操作失败"), |
|||
ERROR_CALLBACK_URL_EMPTY(200,"回调地址为空"), |
|||
|
|||
/** 资源权限相关 */ |
|||
FORBIDDEN_403(403,"无权访问"), |
|||
FORBIDDEN_403_1(403,"账套未注册,无权访问"), |
|||
FORBIDDEN_403_2(403,"IP地址未注册,无权访问"), |
|||
|
|||
FORBIDDEN_404(404,"资源不存在或被移除"), |
|||
|
|||
; |
|||
|
|||
Integer code; |
|||
String msg; |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.cost.common.framework.exception; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2020/6/11 |
|||
* @Version 1.0 |
|||
*/ |
|||
public abstract class BaseException extends RuntimeException { |
|||
|
|||
public BaseException(String message){ |
|||
super(message); |
|||
} |
|||
|
|||
public abstract int getCode(); |
|||
|
|||
|
|||
} |
@ -0,0 +1,27 @@ |
|||
package com.qs.cost.common.framework.exception; |
|||
|
|||
import lombok.Getter; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2020/6/11 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class BusinessException extends BaseException implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
private Integer status; |
|||
|
|||
public BusinessException(String message,Integer status) { |
|||
super(message); |
|||
this.status = status; |
|||
} |
|||
|
|||
@Override |
|||
public int getCode() { |
|||
return status; |
|||
} |
|||
} |
@ -0,0 +1,90 @@ |
|||
package com.qs.cost.common.framework.exception; |
|||
|
|||
import com.qs.cost.common.dto.R; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.ConversionNotSupportedException; |
|||
import org.springframework.beans.TypeMismatchException; |
|||
import org.springframework.http.converter.HttpMessageNotReadableException; |
|||
import org.springframework.http.converter.HttpMessageNotWritableException; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.validation.BindException; |
|||
import org.springframework.validation.ObjectError; |
|||
import org.springframework.web.HttpMediaTypeNotAcceptableException; |
|||
import org.springframework.web.HttpMediaTypeNotSupportedException; |
|||
import org.springframework.web.HttpRequestMethodNotSupportedException; |
|||
import org.springframework.web.bind.MethodArgumentNotValidException; |
|||
import org.springframework.web.bind.MissingPathVariableException; |
|||
import org.springframework.web.bind.MissingServletRequestParameterException; |
|||
import org.springframework.web.bind.ServletRequestBindingException; |
|||
import org.springframework.web.bind.annotation.ControllerAdvice; |
|||
import org.springframework.web.bind.annotation.ExceptionHandler; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException; |
|||
import org.springframework.web.multipart.support.MissingServletRequestPartException; |
|||
import org.springframework.web.servlet.NoHandlerFoundException; |
|||
|
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @Author JcYen |
|||
* @Date 2019/6/7 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
@ControllerAdvice |
|||
public class UnifiedExceptionHandler { |
|||
|
|||
@ExceptionHandler(value = BindException.class) |
|||
@ResponseBody |
|||
public R handleBindException(BindException e) { |
|||
log.warn("参数绑定异常", e); |
|||
return R.error(); |
|||
} |
|||
|
|||
@ExceptionHandler(value = BusinessException.class) |
|||
@ResponseBody |
|||
public R handleBindException(BusinessException e) { |
|||
return new R(e.getCode(),e.getMessage()); |
|||
} |
|||
|
|||
@ExceptionHandler(MethodArgumentNotValidException.class) |
|||
@ResponseBody |
|||
public R handleValidException(MethodArgumentNotValidException e) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
List<ObjectError> allErrors = e.getBindingResult().getAllErrors(); |
|||
String message = allErrors.stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining(";")); |
|||
log.warn("参数校验异常:{}",message); |
|||
return R.error(); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
NoHandlerFoundException.class, |
|||
HttpRequestMethodNotSupportedException.class, |
|||
HttpMediaTypeNotSupportedException.class, |
|||
MissingPathVariableException.class, |
|||
MissingServletRequestParameterException.class, |
|||
TypeMismatchException.class, |
|||
HttpMessageNotReadableException.class, |
|||
HttpMessageNotWritableException.class, |
|||
HttpMediaTypeNotAcceptableException.class, |
|||
ServletRequestBindingException.class, |
|||
ConversionNotSupportedException.class, |
|||
MissingServletRequestPartException.class, |
|||
AsyncRequestTimeoutException.class |
|||
}) |
|||
@ResponseBody |
|||
public R handleServletException(Exception e) { |
|||
log.error("Servlet异常\n异常类型:{}\n异常信息:{}\n异常体:",e.getClass().getSimpleName(),e.getMessage(),e); |
|||
return R.error(); |
|||
} |
|||
|
|||
|
|||
@ExceptionHandler(value = Exception.class) |
|||
@ResponseBody |
|||
public R handleException(Exception e) { |
|||
log.error("Servlet异常\n异常类型:{}\n异常信息:{}\n异常体:",e.getClass().getSimpleName(),e.getMessage(),e); |
|||
return R.error(); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.qs.cost.common.framework.interceptor; |
|||
|
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
|
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.PrintWriter; |
|||
|
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2020/6/11 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class ForbiddenInterceptor implements HandlerInterceptor { |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
|||
try { |
|||
PrintWriter printWriter = response.getWriter(); |
|||
printWriter.println("{\"status\":403,\"message\":\"403 forbidden\"}"); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.qs.cost.common.framework.mvc; |
|||
|
|||
import io.undertow.server.DefaultByteBufferPool; |
|||
import io.undertow.websockets.jsr.WebSocketDeploymentInfo; |
|||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; |
|||
import org.springframework.boot.web.server.WebServerFactoryCustomizer; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
public class CustomizationBean implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> { |
|||
|
|||
@Override |
|||
public void customize(UndertowServletWebServerFactory factory) { |
|||
factory.addDeploymentInfoCustomizers(deploymentInfo -> { |
|||
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); |
|||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024)); |
|||
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); |
|||
}); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,160 @@ |
|||
package com.qs.cost.common.framework.mvc; |
|||
|
|||
import com.qs.cost.common.utils.DateUtil; |
|||
import com.qs.cost.common.utils.ServletUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import javax.servlet.*; |
|||
import javax.servlet.annotation.WebFilter; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletRequestWrapper; |
|||
import java.io.BufferedReader; |
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.IOException; |
|||
import java.io.InputStreamReader; |
|||
import java.nio.charset.StandardCharsets; |
|||
|
|||
/** |
|||
* HttpServletRequest 过滤器 |
|||
* 解决: request.getInputStream()只能读取一次的问题 |
|||
* 目标: 流可重复读 |
|||
* @Author YenHex |
|||
* @Date 2021/4/9 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Slf4j |
|||
@Component |
|||
@WebFilter(filterName = "HttpServletRequestFilter", urlPatterns = "/") |
|||
@Order(10000) |
|||
public class HttpServletRequestFilter implements Filter{ |
|||
|
|||
|
|||
@Override |
|||
public void init(FilterConfig filterConfig) throws ServletException { |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { |
|||
RequestWrapper requestWrapper = null; |
|||
if(servletRequest instanceof HttpServletRequest) { |
|||
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest); |
|||
} |
|||
long startTime = System.currentTimeMillis(); |
|||
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
|
|||
// 在chain.doFiler方法中传递新的request对象
|
|||
if(null == requestWrapper) { |
|||
filterChain.doFilter(servletRequest, servletResponse); |
|||
} else { |
|||
filterChain.doFilter(requestWrapper, servletResponse); |
|||
} |
|||
long endTime = System.currentTimeMillis(); |
|||
double diffTime = DateUtil.printRunTime(startTime,endTime); |
|||
assert servletRequest instanceof HttpServletRequest; |
|||
HttpServletRequest request = (HttpServletRequest)servletRequest; |
|||
//String tenant = request.getHeader("tenant");
|
|||
//String authorization = request.getHeader("Authorization");
|
|||
String url = request.getRequestURL().toString(); |
|||
String method = request.getMethod(); |
|||
String queryStr = request.getQueryString(); |
|||
String ip = request.getRemoteHost(); |
|||
//HttpSession session = request.getSession();
|
|||
//session.removeAttribute("JSESSIONID");
|
|||
//String sessionId = session.getId();
|
|||
String reqBody = ""; |
|||
if (ServletUtil.isJsonRequest(request)) { |
|||
reqBody = requestWrapper.getBody(); |
|||
} |
|||
//String host = request.getRemoteHost();
|
|||
//String servletPath = request.getServletPath();
|
|||
StringBuffer buffer = new StringBuffer("["+method+"] "+url + " IP: "+ip); |
|||
if(!StringUtils.isEmpty(queryStr)){ |
|||
buffer.append("\n参数: "+queryStr); |
|||
} |
|||
if(!StringUtils.isEmpty(reqBody)){ |
|||
buffer.append("\n请求体: "+reqBody); |
|||
} |
|||
buffer.append("\n耗时: "+diffTime+"秒"); |
|||
if(diffTime>4){ |
|||
log.warn("访问速度:{}秒,接口:{},参数:{},请求体:{}",diffTime,"["+method+"] "+url,queryStr,reqBody); |
|||
} |
|||
//buffer.append("\n耗时: "+diffTime+"秒 Session:"+sessionId);
|
|||
System.out.println(buffer.toString()); |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
|
|||
} |
|||
|
|||
/*** |
|||
* HttpServletRequest 包装器 |
|||
* 解决: request.getInputStream()只能读取一次的问题 |
|||
* 目标: 流可重复读 |
|||
*/ |
|||
public class RequestWrapper extends HttpServletRequestWrapper { |
|||
|
|||
/** |
|||
* 请求体 |
|||
*/ |
|||
private String mBody; |
|||
|
|||
public RequestWrapper(HttpServletRequest request) { |
|||
super(request); |
|||
mBody = getBody(request); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求体 |
|||
* @param request 请求 |
|||
* @return 请求体 |
|||
*/ |
|||
private String getBody(HttpServletRequest request) { |
|||
return ServletUtil.getBodyString(request); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求体 |
|||
* @return 请求体 |
|||
*/ |
|||
public String getBody() { |
|||
return mBody; |
|||
} |
|||
|
|||
@Override |
|||
public BufferedReader getReader() throws IOException { |
|||
return new BufferedReader(new InputStreamReader(getInputStream())); |
|||
} |
|||
|
|||
@Override |
|||
public ServletInputStream getInputStream() throws IOException { |
|||
// 创建字节数组输入流
|
|||
final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(StandardCharsets.UTF_8)); |
|||
|
|||
return new ServletInputStream() { |
|||
@Override |
|||
public boolean isFinished() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isReady() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public void setReadListener(ReadListener readListener) { |
|||
} |
|||
|
|||
@Override |
|||
public int read() throws IOException { |
|||
return bais.read(); |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,42 @@ |
|||
package com.qs.cost.common.framework.mvc; |
|||
|
|||
import com.qs.cost.common.framework.interceptor.ForbiddenInterceptor; |
|||
import org.springframework.boot.web.servlet.FilterRegistrationBean; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.cors.CorsConfiguration; |
|||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
|||
import org.springframework.web.filter.CorsFilter; |
|||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
|||
|
|||
/** |
|||
* @Author JcYen |
|||
* @Date 2019/7/17 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Component |
|||
public class SecurityMvcComponent implements WebMvcConfigurer { |
|||
|
|||
@Override |
|||
public void addInterceptors(InterceptorRegistry registry) { |
|||
//注册TestInterceptor拦截器
|
|||
registry.addInterceptor(new ForbiddenInterceptor()).addPathPatterns("/**").excludePathPatterns("/api/**"); |
|||
} |
|||
|
|||
@Bean |
|||
public CorsFilter corsFilter() { |
|||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); |
|||
CorsConfiguration config = new CorsConfiguration(); |
|||
config.addAllowedOrigin("*"); |
|||
config.setAllowCredentials(true); |
|||
config.addAllowedHeader("*"); |
|||
config.addAllowedMethod("*"); |
|||
source.registerCorsConfiguration("/**", config); |
|||
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); |
|||
bean.setOrder(0); |
|||
return new CorsFilter(source); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import com.qs.cost.common.enums.HttpCode; |
|||
import com.qs.cost.common.framework.exception.BusinessException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
public class Assert { |
|||
|
|||
public static void throwEx(Integer code,String msg){ |
|||
throw new BusinessException(msg, code); |
|||
} |
|||
|
|||
public static void throwEx(HttpCode httpCode){ |
|||
throw new BusinessException(httpCode.getMsg(),httpCode.getCode()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,93 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import org.springframework.cglib.beans.BeanCopier; |
|||
|
|||
import java.io.*; |
|||
import java.util.Collection; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* 取代BeanUtils.CP |
|||
* @Author JcYen |
|||
* @Date 2019/6/3 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class BeanCopierUtil { |
|||
|
|||
private static final Map<String, BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<String, BeanCopier>(); |
|||
|
|||
private static String genKey(Class<?> source, Class<?> target) { |
|||
return source.getName() + target.getName(); |
|||
} |
|||
|
|||
/** |
|||
* target字段值,会被source字段值覆盖(包括null) |
|||
* @param source |
|||
* @param target |
|||
*/ |
|||
public static <T> T copy(Object source, T target) { |
|||
String key = genKey(source.getClass(), target.getClass()); |
|||
BeanCopier copier; |
|||
if (!BEAN_COPIERS.containsKey(key)) { |
|||
copier = BeanCopier.create(source.getClass(), target.getClass(), false); |
|||
BEAN_COPIERS.put(key, copier); |
|||
} else { |
|||
copier = BEAN_COPIERS.get(key); |
|||
} |
|||
copier.copy(source, target, null); |
|||
return target; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 采用对象的序列化完成对象的深克隆 |
|||
* @param obj 待克隆的对象 |
|||
* @return |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static <T extends Serializable> T cloneObject(T obj) { |
|||
T cloneObj = null; |
|||
try { |
|||
// 写入字节流
|
|||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
|||
ObjectOutputStream obs = new ObjectOutputStream(out); |
|||
obs.writeObject(obj); |
|||
obs.close(); |
|||
|
|||
// 分配内存,写入原始对象,生成新对象
|
|||
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); |
|||
ObjectInputStream ois = new ObjectInputStream(ios); |
|||
// 返回生成的新对象
|
|||
cloneObj = (T) ois.readObject(); |
|||
ois.close(); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return cloneObj; |
|||
} |
|||
|
|||
/** |
|||
* 利用序列化完成集合的深克隆 |
|||
* |
|||
* @param collection 待克隆的集合 |
|||
* @return |
|||
* @throws ClassNotFoundException |
|||
* @throws IOException |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static <T> Collection<T> cloneCollection(Collection<T> collection) throws ClassNotFoundException, IOException{ |
|||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); |
|||
ObjectOutputStream out = new ObjectOutputStream(byteOut); |
|||
out.writeObject(collection); |
|||
out.close(); |
|||
|
|||
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); |
|||
ObjectInputStream in = new ObjectInputStream(byteIn); |
|||
Collection<T> dest = (Collection<T>) in.readObject(); |
|||
in.close(); |
|||
|
|||
return dest; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,131 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import java.util.*; |
|||
|
|||
/** |
|||
* 常用数据工具类 |
|||
* |
|||
* @Author JcYen |
|||
* @Date 2019/5/23 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class CollectionUtil { |
|||
|
|||
private CollectionUtil() { |
|||
super(); |
|||
} |
|||
|
|||
/** |
|||
* 判断一个集合是否为空 |
|||
*/ |
|||
public static <T> boolean isEmpty(Collection<T> col) { |
|||
if (col == null || col.isEmpty()) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public static <T> List<T> setToList(Set<T> colSet){ |
|||
if (colSet == null || colSet.isEmpty()){ |
|||
return new ArrayList<>(); |
|||
} |
|||
List<T> rs = new ArrayList<>(colSet); |
|||
return rs; |
|||
} |
|||
|
|||
/** |
|||
* 判断一个集合是否不为空 |
|||
*/ |
|||
public static <T> boolean isNotEmpty(Collection<T> col) { |
|||
return !isEmpty(col); |
|||
} |
|||
|
|||
/** |
|||
* 判断Map是否为空 |
|||
*/ |
|||
public static <K, V> boolean isEmpty(Map<K, V> map) { |
|||
if (map == null || map.isEmpty()) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 判断Map是否不为空为空 |
|||
*/ |
|||
public static <K, V> boolean isNotEmpty(Map<K, V> map) { |
|||
return !isEmpty(map); |
|||
} |
|||
|
|||
/** |
|||
* 去除list中的重复数据 |
|||
*/ |
|||
public static <T> List<T> removeRepeat(List<T> list) { |
|||
if (isEmpty(list)) { |
|||
return list; |
|||
} |
|||
List<T> result = new ArrayList<T>(); |
|||
for (T e : list) { |
|||
if (!result.contains(e)) { |
|||
result.add(e); |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
public static boolean hasNull(Object... objects){ |
|||
for (int i = 0; i < objects.length; i++) { |
|||
if(objects[i]==null){return true;} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public static boolean hasNotNull(Object... objects){ |
|||
for (int i = 0; i < objects.length; i++) { |
|||
if(objects[i]!=null){return true;} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 将集合转换为String数组 |
|||
*/ |
|||
public static <T> String[] toArray(List<T> list) { |
|||
if (isEmpty(list)) { |
|||
return null; |
|||
} |
|||
String[] result = new String[list.size()]; |
|||
for (int i = 0; i < list.size(); i++) { |
|||
result[i] = String.valueOf(list.get(i)); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 将list拆分成多给指定的大小的list |
|||
*/ |
|||
public static <T> List<List<T>> createList(List<T> target, int size) { |
|||
List<List<T>> listArr = new ArrayList<>(); |
|||
//获取被拆分的数组个数
|
|||
int arrSize = target.size()%size==0?target.size()/size:target.size()/size+1; |
|||
for(int i=0;i<arrSize;i++) { |
|||
List<T> sub = new ArrayList<T>(); |
|||
//把指定索引数据放入到list中
|
|||
for(int j=i*size;j<=size*(i+1)-1;j++) { |
|||
if(j<=target.size()-1) { |
|||
sub.add(target.get(j)); |
|||
} |
|||
} |
|||
listArr.add(sub); |
|||
} |
|||
return listArr; |
|||
} |
|||
|
|||
public static <T> T selectFirst(List<T> list){ |
|||
if(isNotEmpty(list)){ |
|||
return list.get(0); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,173 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import java.text.ParseException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.util.Calendar; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 日期工具类 |
|||
* Created by macro on 2019/1/29. |
|||
*/ |
|||
public class DateUtil { |
|||
|
|||
public static final String FORMAT_SIMPLE_DATE_OF_YEAR_NUM = "MMdd"; |
|||
public static final String FORMAT_SIMPLE_DATE_OF_YEAR = "MM-dd"; |
|||
public static final String FORMAT_DATE_OF_YEAR = "MM-dd HH:mm:ss"; |
|||
public static final String FORMAT_DATE = "yyyy-MM-dd HH:mm:ss"; |
|||
public static final String FORMAT_SIMPLE_DATE_NUM = "yyyyMMdd"; |
|||
public static final String FORMAT_DATE_NUM = "yyyyMMddHHmmss"; |
|||
public static final String FORMAT_CRON = "ss mm HH dd MM ?"; |
|||
|
|||
public static final String FORMAT_SIMPLE_DATE_ZH = "yyyy年MM月dd日"; |
|||
public static final String FORMAT_DATE_ZH = "yyyy年MM月dd日 HH时mm分ss秒"; |
|||
|
|||
|
|||
public static final String FORMAT_SIMPLE_DATE = "yyyy-MM-dd"; |
|||
public static final String FORMAT_SIMPLE_MONTH = "yyyy-MM"; |
|||
public static final String FORMAT_SIMPLE_YEAR = "yyyy"; |
|||
|
|||
/** |
|||
* 转换格式 |
|||
* @param dateStr |
|||
* @param format |
|||
* @return |
|||
*/ |
|||
public static Date getDateByFormatStr(String dateStr,String format){ |
|||
if(dateStr==null||format==null){return null;} |
|||
SimpleDateFormat sdf = new SimpleDateFormat(format); |
|||
Date date = null; |
|||
try { |
|||
date = sdf.parse(dateStr); |
|||
} catch (ParseException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return date; |
|||
} |
|||
|
|||
public static Double getDiffSecond(long beganTime, long endTime){ |
|||
return (double)(endTime - beganTime) / 1000.0D; |
|||
} |
|||
|
|||
/** 格式化日期字符串*/ |
|||
public static String formatDate(Date date,String format){ |
|||
SimpleDateFormat sdf = new SimpleDateFormat(format); |
|||
if(date!=null){ |
|||
return sdf.format(date); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static Date format(Date date,String format){ |
|||
String dateString = formatDate(date, format); |
|||
SimpleDateFormat sdf = new SimpleDateFormat(format); |
|||
try { |
|||
if(dateString!=null){ |
|||
return sdf.parse(dateString); |
|||
} |
|||
} catch (ParseException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
public static double printRunTime(long beganTime, long endTime) { |
|||
return (double)(endTime - beganTime) / 1000.0D; |
|||
} |
|||
|
|||
/** 获取一天的起始时间*/ |
|||
public static Date getBeginDateOfDay(Date date){ |
|||
Calendar calendar = Calendar.getInstance(); |
|||
calendar.setTime(date); |
|||
int day = calendar.get(Calendar.DATE); |
|||
calendar.set(Calendar.DATE,day-1); |
|||
calendar.set(Calendar.HOUR_OF_DAY,23); |
|||
calendar.set(Calendar.MINUTE,59); |
|||
calendar.set(Calendar.SECOND,59); |
|||
calendar.set(Calendar.MILLISECOND,99); |
|||
/*calendar.set(Calendar.HOUR_OF_DAY,0); |
|||
calendar.set(Calendar.MINUTE,0); |
|||
calendar.set(Calendar.SECOND,0); |
|||
calendar.set(Calendar.MILLISECOND,0);*/ |
|||
return calendar.getTime(); |
|||
} |
|||
|
|||
/** 获取一天的结束时间(23:59:59)*/ |
|||
public static Date getEndDateOfDay(Date date){ |
|||
Calendar calendar = Calendar.getInstance(); |
|||
calendar.setTime(date); |
|||
int day = calendar.get(Calendar.DATE); |
|||
calendar.set(Calendar.DATE,day+1); |
|||
calendar.set(Calendar.HOUR_OF_DAY,0); |
|||
calendar.set(Calendar.MINUTE,0); |
|||
calendar.set(Calendar.SECOND,0); |
|||
calendar.set(Calendar.MILLISECOND,0); |
|||
/*calendar.set(Calendar.HOUR_OF_DAY,23); |
|||
calendar.set(Calendar.MINUTE,59); |
|||
calendar.set(Calendar.SECOND,59); |
|||
calendar.set(Calendar.MILLISECOND,999);*/ |
|||
return calendar.getTime(); |
|||
} |
|||
|
|||
/** |
|||
* 获取未来某时间 |
|||
* @param date |
|||
* @param somedate |
|||
* @param calendarTimeSpan |
|||
*/ |
|||
public static Date getAfterSomedate(Date date,int somedate,int calendarTimeSpan){ |
|||
final Calendar calendar = Calendar.getInstance(); |
|||
calendar.setTime(date); |
|||
calendar.add(calendarTimeSpan,somedate); |
|||
return calendar.getTime(); |
|||
} |
|||
|
|||
/** 获取传入日期在某时间第一天 */ |
|||
public static Date getFirstDay(Date date,int calendarTimeSpan) { |
|||
Calendar cal = Calendar.getInstance(); |
|||
cal.setTime(date); |
|||
int last = cal.getActualMinimum(calendarTimeSpan); |
|||
cal.set(calendarTimeSpan, last); |
|||
return cal.getTime(); |
|||
} |
|||
|
|||
/** 获取传入日期在某时间最后一天 */ |
|||
public static Date getLastDay(Date date,int calendarTimeSpan) { |
|||
Calendar cal = Calendar.getInstance(); |
|||
cal.setTime(date); |
|||
int last = cal.getActualMaximum(calendarTimeSpan); |
|||
cal.set(calendarTimeSpan, last); |
|||
return cal.getTime(); |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 计算时差 |
|||
* @param smallDate |
|||
* @param bigDate |
|||
* @param unit 0天 1时 2分 3秒 |
|||
* @return |
|||
*/ |
|||
public static Long diffTime(Date smallDate,Date bigDate,int unit){ |
|||
long nd = 1000*24*60*60;//一天的毫秒数
|
|||
long nh = 1000*60*60;//一小时的毫秒数
|
|||
long nm = 1000*60;//一分钟的毫秒数
|
|||
long ns = 1000;//一秒钟的毫秒数
|
|||
|
|||
long t1 = smallDate.getTime(); |
|||
long t2 = bigDate.getTime(); |
|||
long diff = t2 - t1; |
|||
if(unit==0){ |
|||
return diff/nd; |
|||
}else if (unit==1){ |
|||
return diff%nd/nh; |
|||
}else if(unit==2){ |
|||
return diff%nd%nh/nm; |
|||
}else if(unit==3){ |
|||
return diff%nd%nh%nm/ns; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,479 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.http.HttpEntity; |
|||
import org.apache.http.HttpResponse; |
|||
import org.apache.http.HttpStatus; |
|||
import org.apache.http.NameValuePair; |
|||
import org.apache.http.client.ClientProtocolException; |
|||
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.client.methods.HttpRequestBase; |
|||
import org.apache.http.client.utils.URIBuilder; |
|||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; |
|||
import org.apache.http.entity.StringEntity; |
|||
import org.apache.http.impl.client.CloseableHttpClient; |
|||
import org.apache.http.impl.client.HttpClientBuilder; |
|||
import org.apache.http.impl.client.HttpClients; |
|||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; |
|||
import org.apache.http.message.BasicNameValuePair; |
|||
import org.apache.http.protocol.HTTP; |
|||
import org.apache.http.ssl.SSLContextBuilder; |
|||
import org.apache.http.ssl.TrustStrategy; |
|||
import org.apache.http.util.EntityUtils; |
|||
|
|||
import javax.net.ssl.SSLContext; |
|||
import java.io.*; |
|||
import java.net.URISyntaxException; |
|||
import java.nio.charset.Charset; |
|||
import java.security.cert.CertificateException; |
|||
import java.security.cert.X509Certificate; |
|||
import java.util.*; |
|||
|
|||
/** |
|||
* Created by JcYen on 2019/5/13 |
|||
* |
|||
* @Version v1.0 |
|||
**/ |
|||
@Slf4j |
|||
public class HttpService { |
|||
|
|||
private static PoolingHttpClientConnectionManager cm; |
|||
private static RequestConfig requestConfig; |
|||
private static String UTF_8 = "UTF-8"; |
|||
|
|||
private static void init() { |
|||
Integer poolSize = 10; |
|||
Integer connSize = 5; |
|||
Integer socketTimeout = 200; |
|||
Integer reqTimeout = 200; |
|||
Integer connTimeout = 200; |
|||
if (cm == null) { |
|||
cm = new PoolingHttpClientConnectionManager(); |
|||
cm.setMaxTotal(poolSize);// 整个连接池最大连接数
|
|||
cm.setDefaultMaxPerRoute(connSize);// 每路由最大连接数,默认值是2
|
|||
requestConfig = RequestConfig.custom() // 建立构造器
|
|||
.setConnectTimeout(connTimeout*1000)// 设置连接超时
|
|||
.setConnectionRequestTimeout(reqTimeout*1000)// 设置从连接池获取连接实例的超时
|
|||
.setSocketTimeout(socketTimeout*1000)// 设置读取超时
|
|||
.build();// 在提交请求之前 测试连接是否可用
|
|||
} |
|||
} |
|||
|
|||
/* 通过连接池获取HttpClient */ |
|||
private static CloseableHttpClient getHttpClient() { |
|||
init(); |
|||
return HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).build(); |
|||
} |
|||
|
|||
/* get的URL请求 */ |
|||
public static String httpGetRequest(String url) { |
|||
HttpGet httpGet = new HttpGet(url); |
|||
return getResult(httpGet); |
|||
} |
|||
|
|||
public static InputStream httpGetStream(String url) { |
|||
HttpGet httpGet = new HttpGet(url); |
|||
CloseableHttpClient httpClient = getHttpClient(); |
|||
try { |
|||
CloseableHttpResponse response = httpClient.execute(httpGet); |
|||
HttpEntity entity = response.getEntity(); |
|||
if (entity != null) { |
|||
return entity.getContent(); |
|||
} |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/* get的URL的参数map请求 */ |
|||
public static String httpGetRequest(String url, Map<String, Object> params) throws URISyntaxException { |
|||
URIBuilder ub = new URIBuilder(); |
|||
ub.setPath(url); |
|||
ArrayList<NameValuePair> pairs = covertParams2NVPS(params); |
|||
ub.setParameters(pairs); |
|||
HttpGet httpGet = new HttpGet(ub.build()); |
|||
return getResult(httpGet); |
|||
} |
|||
|
|||
/* get的URL参数map,头map参数 */ |
|||
public static String httpGetRequest(String url, Map<String, Object> headers, Map<String, Object> params) |
|||
throws URISyntaxException { |
|||
URIBuilder ub = new URIBuilder(); |
|||
ub.setPath(url); |
|||
ArrayList<NameValuePair> pairs = covertParams2NVPS(params); |
|||
ub.setParameters(pairs); |
|||
HttpGet httpGet = new HttpGet(ub.build()); |
|||
for (Map.Entry<String, Object> param : headers.entrySet()) { |
|||
httpGet.addHeader(param.getKey(), String.valueOf(param.getValue())); |
|||
} |
|||
return getResult(httpGet); |
|||
} |
|||
|
|||
/* post的URL请求 */ |
|||
public static String httpPostRequest(String url) { |
|||
HttpPost httpPost = new HttpPost(url); |
|||
return getResult(httpPost); |
|||
} |
|||
|
|||
/* post的URL的参数map请求 */ |
|||
public static String httpPostRequest(String url, Map<String, Object> params) throws UnsupportedEncodingException { |
|||
HttpPost httpPost = new HttpPost(url); |
|||
ArrayList<NameValuePair> pairs = covertParams2NVPS(params); |
|||
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8)); |
|||
log.debug("httpPostRequest -> URL:",httpPost.getURI().getPath()); |
|||
return getResult(httpPost); |
|||
} |
|||
/* post的URL的参数map请求 */ |
|||
public static HttpResult httpPostRequestWithResult(String url, Map<String, Object> params) { |
|||
HttpPost httpPost = new HttpPost(url); |
|||
ArrayList<NameValuePair> pairs = covertParams2NVPS(params); |
|||
try { |
|||
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8)); |
|||
} catch (UnsupportedEncodingException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
log.debug("httpPostRequest -> URL:",httpPost.getURI().getPath()); |
|||
return getHttpResult(httpPost); |
|||
} |
|||
|
|||
/* post的URL的参数map请求map头 */ |
|||
public static String httpPostRequest(String url, Map<String, Object> headers, Map<String, Object> params) |
|||
throws UnsupportedEncodingException { |
|||
HttpPost httpPost = new HttpPost(url); |
|||
for (Map.Entry<String, Object> param : headers.entrySet()) { |
|||
httpPost.addHeader(param.getKey(), String.valueOf(param.getValue())); |
|||
} |
|||
ArrayList<NameValuePair> pairs = covertParams2NVPS(params); |
|||
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8)); |
|||
return getResult(httpPost); |
|||
} |
|||
|
|||
/* post的URL的参数map请求map头及body信息 */ |
|||
@SuppressWarnings({ "rawtypes", "unchecked" }) |
|||
public static String postMap(String url, Map<String, String> headerMap, Map<String, String> contentMap) { |
|||
String result = null; |
|||
CloseableHttpClient httpClient = HttpClients.createDefault(); |
|||
HttpPost post = new HttpPost(url); |
|||
List<NameValuePair> content = new ArrayList<NameValuePair>(); |
|||
Iterator iterator = contentMap.entrySet().iterator(); // 将content生成entity
|
|||
while (iterator.hasNext()) { |
|||
Map.Entry<String, String> elem = (Map.Entry<String, String>) iterator.next(); |
|||
content.add(new BasicNameValuePair(elem.getKey(), elem.getValue())); |
|||
} |
|||
CloseableHttpResponse response = null; |
|||
try { |
|||
Iterator headerIterator = headerMap.entrySet().iterator(); // 循环增加header
|
|||
while (headerIterator.hasNext()) { |
|||
Map.Entry<String, String> elem = (Map.Entry<String, String>) headerIterator.next(); |
|||
post.addHeader(elem.getKey(), elem.getValue()); |
|||
} |
|||
if (content.size() > 0) { |
|||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(content, "UTF-8"); |
|||
post.setEntity(entity); |
|||
} |
|||
response = httpClient.execute(post); // 发送请求并接收返回数据
|
|||
if (response != null && response.getStatusLine().getStatusCode() == 200) { |
|||
HttpEntity entity = response.getEntity(); // 获取response的body部分
|
|||
result = EntityUtils.toString(entity); // 读取reponse的body部分并转化成字符串
|
|||
} |
|||
return result; |
|||
} catch (UnsupportedEncodingException e) { |
|||
e.printStackTrace(); |
|||
} catch (ClientProtocolException e) { |
|||
e.printStackTrace(); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
try { |
|||
httpClient.close(); |
|||
if (response != null) { |
|||
response.close(); |
|||
} |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/* 中间工具 */ |
|||
private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) { |
|||
ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>(); |
|||
for (Map.Entry<String, Object> param : params.entrySet()) { |
|||
pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue()))); |
|||
} |
|||
return pairs; |
|||
} |
|||
|
|||
/* 发送的json的参数请求 */ |
|||
public static String httpPostJSON(String url, String json) throws UnsupportedEncodingException { |
|||
log.info("json"+json); |
|||
HttpPost httpPost = new HttpPost(url); |
|||
StringEntity entity = new StringEntity(json); |
|||
entity.setContentEncoding(UTF_8); |
|||
entity.setContentType("application/json");// 发送json数据需要设置contentType
|
|||
httpPost.setEntity(entity); |
|||
return getResult(httpPost); |
|||
} |
|||
|
|||
/* 发送的json的参数请求 */ |
|||
public static HttpResponse httpImagePostJSON(String url, String json) throws UnsupportedEncodingException { |
|||
CloseableHttpClient httpClient = HttpClientBuilder.create().build(); |
|||
HttpPost httpPost = new HttpPost(url); |
|||
httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json"); |
|||
StringEntity entity; |
|||
entity = new StringEntity(json); |
|||
entity.setContentType("image/png"); |
|||
httpPost.setEntity(entity); |
|||
HttpResponse response = null; |
|||
try { |
|||
response = httpClient.execute(httpPost); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return response; |
|||
} |
|||
|
|||
/* 发送的xml字符串 */ |
|||
public static String httpPostXML(String url, String xmlData) throws UnsupportedEncodingException { |
|||
HttpPost post = new HttpPost(url); |
|||
StringEntity entity = new StringEntity(xmlData); |
|||
entity.setContentEncoding(UTF_8); |
|||
entity.setContentType("text/xml;charset=UTF-8"); |
|||
return getResult(post); |
|||
} |
|||
|
|||
private static String getResult(HttpRequestBase request) { |
|||
return getHttpResult(request).getData(); |
|||
} |
|||
|
|||
private static HttpResult getHttpResult(HttpRequestBase request) { |
|||
CloseableHttpClient httpClient = getHttpClient(); |
|||
String errMsg = null; |
|||
Integer code = 500; |
|||
try { |
|||
CloseableHttpResponse response = httpClient.execute(request); |
|||
code = response.getStatusLine().getStatusCode(); |
|||
HttpEntity entity = response.getEntity(); |
|||
if (entity != null) { |
|||
String data = EntityUtils.toString(entity, UTF_8); |
|||
response.close(); |
|||
HttpResult result = new HttpResult(code,null,data); |
|||
return result; |
|||
} |
|||
} catch (ClientProtocolException e) { |
|||
log.error(e.getMessage()); |
|||
errMsg = e.getMessage(); |
|||
} catch (IOException e) { |
|||
log.error(e.getMessage()); |
|||
errMsg = e.getMessage(); |
|||
} |
|||
return new HttpResult(code,errMsg); |
|||
} |
|||
|
|||
public static String getXMLString() { |
|||
StringBuffer sb = new StringBuffer(); |
|||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); |
|||
sb.append("<AastraIPPhoneInputScreen type=\"string\">"); |
|||
sb.append("<Title>Hello world!</Title>"); |
|||
sb.append("<Prompt>Enter value</Prompt>"); |
|||
sb.append("<URL>http://localhost/xmlserver/test.do</URL>"); |
|||
sb.append("<Parameter>value</Parameter>"); |
|||
sb.append("<Default></Default>"); |
|||
sb.append("</AastraIPPhoneInputScreen>"); |
|||
return sb.toString(); |
|||
} |
|||
|
|||
public static String doPost(String apiUrl, Object json) { |
|||
CloseableHttpClient httpClient = HttpClients.createDefault(); |
|||
String httpStr = null; |
|||
HttpPost httpPost = new HttpPost(apiUrl); |
|||
CloseableHttpResponse response = null; |
|||
try { |
|||
httpPost.setConfig(requestConfig); |
|||
StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解决中文乱码问题
|
|||
stringEntity.setContentEncoding("UTF-8"); |
|||
stringEntity.setContentType("application/json"); |
|||
httpPost.setEntity(stringEntity); |
|||
response = httpClient.execute(httpPost); |
|||
HttpEntity entity = response.getEntity(); |
|||
System.out.println(response.getStatusLine().getStatusCode()); |
|||
httpStr = EntityUtils.toString(entity, "UTF-8"); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
if (response != null) { |
|||
try { |
|||
EntityUtils.consume(response.getEntity()); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
return httpStr; |
|||
} |
|||
|
|||
/** |
|||
* 发送 SSL POST 请求(HTTPS),K-V形式 |
|||
*/ |
|||
public static String doPostSSLXml(String apiUrl, String xml) { |
|||
CloseableHttpClient httpClient = createSSLClientDefault(); |
|||
HttpPost httpPost = new HttpPost(apiUrl); |
|||
CloseableHttpResponse response = null; |
|||
String httpStr = null; |
|||
try { |
|||
httpPost.setConfig(requestConfig); |
|||
StringEntity stringEntity = new StringEntity(xml, "UTF-8");// 解决中文乱码问题
|
|||
stringEntity.setContentEncoding("UTF-8"); |
|||
stringEntity.setContentType("text/xml;charset=UTF-8"); |
|||
httpPost.setEntity(stringEntity); |
|||
response = httpClient.execute(httpPost); |
|||
System.out.println(response); |
|||
// int statusCode = response.getStatusLine().getStatusCode();
|
|||
// if (statusCode != HttpStatus.SC_OK) {
|
|||
// return null;
|
|||
// }
|
|||
HttpEntity entity = response.getEntity(); |
|||
// if (entity == null) {
|
|||
// return null;
|
|||
// }
|
|||
httpStr = EntityUtils.toString(entity, "utf-8"); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
if (response != null) { |
|||
try { |
|||
EntityUtils.consume(response.getEntity()); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
return httpStr; |
|||
} |
|||
|
|||
public static String doGetSSL(String apiUrl) throws ClientProtocolException, IOException { |
|||
CloseableHttpClient httpClient = createSSLClientDefault(); |
|||
HttpGet httpget = new HttpGet(apiUrl); |
|||
CloseableHttpResponse response = null; |
|||
String httpStr = null; |
|||
httpget.setConfig(requestConfig); |
|||
response = httpClient.execute(httpget); |
|||
HttpEntity entity = response.getEntity(); |
|||
httpStr = EntityUtils.toString(entity, "utf-8"); |
|||
return httpStr; |
|||
} |
|||
|
|||
public static String doPostSSL(String apiUrl, Map<String, Object> params) |
|||
throws ClientProtocolException, IOException { |
|||
CloseableHttpClient httpClient = createSSLClientDefault(); |
|||
HttpPost httpPost = new HttpPost(apiUrl); |
|||
CloseableHttpResponse response = null; |
|||
String httpStr = null; |
|||
httpPost.setConfig(requestConfig); |
|||
List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size()); |
|||
for (Map.Entry<String, Object> entry : params.entrySet()) { |
|||
NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString()); |
|||
pairList.add(pair); |
|||
} |
|||
httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8"))); |
|||
response = httpClient.execute(httpPost); |
|||
HttpEntity entity = response.getEntity(); |
|||
httpStr = EntityUtils.toString(entity, "utf-8"); |
|||
return httpStr; |
|||
} |
|||
|
|||
/** |
|||
* 发送 SSL POST 请求(HTTPS),JSON形式 |
|||
* |
|||
* @param apiUrl |
|||
* API接口URL |
|||
* @param json |
|||
* JSON对象 |
|||
* @return |
|||
*/ |
|||
public static String doPostSSL(String apiUrl, Object json) { |
|||
CloseableHttpClient httpClient = createSSLClientDefault(); |
|||
HttpPost httpPost = new HttpPost(apiUrl); |
|||
CloseableHttpResponse response = null; |
|||
String httpStr = null; |
|||
try { |
|||
httpPost.setConfig(requestConfig); |
|||
StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解决中文乱码问题
|
|||
stringEntity.setContentEncoding("UTF-8"); |
|||
stringEntity.setContentType("application/json"); |
|||
httpPost.setEntity(stringEntity); |
|||
response = httpClient.execute(httpPost); |
|||
int statusCode = response.getStatusLine().getStatusCode(); |
|||
if (statusCode != HttpStatus.SC_OK) { |
|||
return null; |
|||
} |
|||
HttpEntity entity = response.getEntity(); |
|||
if (entity == null) { |
|||
return null; |
|||
} |
|||
httpStr = EntityUtils.toString(entity, "utf-8"); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
if (response != null) { |
|||
try { |
|||
EntityUtils.consume(response.getEntity()); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
return httpStr; |
|||
} |
|||
|
|||
|
|||
|
|||
public static CloseableHttpClient createSSLClientDefault() { |
|||
try { |
|||
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { |
|||
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
|||
return true; |
|||
} |
|||
}).build(); |
|||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); |
|||
return HttpClients.custom().setSSLSocketFactory(sslsf).build(); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return HttpClients.createDefault(); |
|||
} |
|||
|
|||
@Getter |
|||
@Setter |
|||
public static class HttpResult { |
|||
|
|||
public HttpResult(Integer code,String message){ |
|||
this.massage = message; |
|||
this.code = code; |
|||
} |
|||
|
|||
public HttpResult(Integer code,String message,String data){ |
|||
this.data = data; |
|||
this.massage = message; |
|||
this.code = code; |
|||
} |
|||
|
|||
private String massage; |
|||
|
|||
private Integer code; |
|||
|
|||
private String data; |
|||
|
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,121 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import com.qs.cost.common.dto.HttpResult; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.http.HttpEntity; |
|||
import org.apache.http.NameValuePair; |
|||
import org.apache.http.client.ClientProtocolException; |
|||
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.client.methods.HttpRequestBase; |
|||
import org.apache.http.impl.client.CloseableHttpClient; |
|||
import org.apache.http.impl.client.HttpClients; |
|||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; |
|||
import org.apache.http.message.BasicNameValuePair; |
|||
import org.apache.http.util.EntityUtils; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.util.ArrayList; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
@Slf4j |
|||
public class HttpUtil { |
|||
|
|||
private static PoolingHttpClientConnectionManager cm; |
|||
private static RequestConfig requestConfig; |
|||
private static String UTF8 = "UTF-8"; |
|||
|
|||
private static void init() { |
|||
int poolSize = 10; |
|||
int connSize = 5; |
|||
int socketTimeout = 2000; |
|||
int reqTimeout = 2000; |
|||
int connTimeout = 2000; |
|||
if (cm == null) { |
|||
cm = new PoolingHttpClientConnectionManager(); |
|||
// 整个连接池最大连接数
|
|||
cm.setMaxTotal(poolSize); |
|||
// 每路由最大连接数,默认值是2
|
|||
cm.setDefaultMaxPerRoute(connSize); |
|||
// 建立构造器
|
|||
requestConfig = RequestConfig.custom() |
|||
// 设置连接超时
|
|||
.setConnectTimeout(connTimeout*1000) |
|||
// 设置从连接池获取连接实例的超时
|
|||
.setConnectionRequestTimeout(reqTimeout*1000) |
|||
// 设置读取超时
|
|||
.setSocketTimeout(socketTimeout*1000) |
|||
// 在提交请求之前 测试连接是否可用
|
|||
.build(); |
|||
} |
|||
} |
|||
/** 通过连接池获取HttpClient */ |
|||
private static CloseableHttpClient getHttpClient() { |
|||
init(); |
|||
return HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).build(); |
|||
} |
|||
|
|||
public static HttpResult get(String url){ |
|||
HttpGet httpGet = new HttpGet(url); |
|||
return getHttpResult(httpGet); |
|||
} |
|||
|
|||
/** post的URL的参数map请求 */ |
|||
public static HttpResult post(String url, Map<String, Object> params) { |
|||
HttpPost httpPost = new HttpPost(url); |
|||
ArrayList<NameValuePair> pairs = covertParams2NVPS(params); |
|||
try { |
|||
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF8)); |
|||
} catch (UnsupportedEncodingException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return getHttpResult(httpPost); |
|||
} |
|||
|
|||
/** |
|||
* 请求返回统一封装 |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
private static HttpResult getHttpResult(HttpRequestBase request) { |
|||
CloseableHttpClient httpClient = getHttpClient(); |
|||
String errMsg = null; |
|||
Integer code = 500; |
|||
try { |
|||
CloseableHttpResponse response = httpClient.execute(request); |
|||
code = response.getStatusLine().getStatusCode(); |
|||
HttpEntity entity = response.getEntity(); |
|||
if (entity != null) { |
|||
String data = EntityUtils.toString(entity, UTF8); |
|||
response.close(); |
|||
HttpResult result = new HttpResult(code,null,data); |
|||
return result; |
|||
} |
|||
} catch (ClientProtocolException e) { |
|||
log.error(e.getMessage()); |
|||
errMsg = e.getMessage(); |
|||
} catch (IOException e) { |
|||
log.error(e.getMessage()); |
|||
errMsg = e.getMessage(); |
|||
} |
|||
return new HttpResult(code,errMsg); |
|||
} |
|||
|
|||
/** 中间工具 */ |
|||
private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) { |
|||
ArrayList<NameValuePair> pairs = new ArrayList<>(); |
|||
for (Map.Entry<String, Object> param : params.entrySet()) { |
|||
pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue()))); |
|||
} |
|||
return pairs; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
public class IdUtil { |
|||
|
|||
public static final IdWorker ID_WORKER = new IdWorker(0,0); |
|||
|
|||
public static Long getLong(){ |
|||
return ID_WORKER.nextId()-1541318968076140673L; |
|||
} |
|||
} |
@ -0,0 +1,155 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import java.lang.management.ManagementFactory; |
|||
import java.net.InetAddress; |
|||
import java.net.NetworkInterface; |
|||
|
|||
/** |
|||
* <p>名称:IdWorker.java</p> |
|||
* <p>描述:分布式自增长ID</p> |
|||
* <pre> |
|||
* Twitter的 Snowflake JAVA实现方案 |
|||
* </pre> |
|||
* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用: |
|||
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000 |
|||
* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间, |
|||
* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识), |
|||
* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。 |
|||
* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), |
|||
* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。 |
|||
* <p> |
|||
* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加)) |
|||
* @author USER |
|||
*/ |
|||
public class IdWorker { |
|||
|
|||
// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
|
|||
private final static long twepoch = 1288834974657L; |
|||
// 机器标识位数
|
|||
private final static long workerIdBits = 5L; |
|||
// 数据中心标识位数
|
|||
private final static long datacenterIdBits = 5L; |
|||
// 机器ID最大值
|
|||
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); |
|||
// 数据中心ID最大值
|
|||
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); |
|||
// 毫秒内自增位
|
|||
private final static long sequenceBits = 12L; |
|||
// 机器ID偏左移12位
|
|||
private final static long workerIdShift = sequenceBits; |
|||
// 数据中心ID左移17位
|
|||
private final static long datacenterIdShift = sequenceBits + workerIdBits; |
|||
// 时间毫秒左移22位
|
|||
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; |
|||
|
|||
private final static long sequenceMask = -1L ^ (-1L << sequenceBits); |
|||
/* 上次生产id时间戳 */ |
|||
private static long lastTimestamp = -1L; |
|||
// 0,并发控制
|
|||
private long sequence = 0L; |
|||
|
|||
private final long workerId; |
|||
// 数据标识id部分
|
|||
private final long datacenterId; |
|||
|
|||
public IdWorker(){ |
|||
this.datacenterId = getDatacenterId(maxDatacenterId); |
|||
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); |
|||
} |
|||
/** |
|||
* @param workerId 工作机器ID |
|||
* @param datacenterId 序列号 |
|||
*/ |
|||
public IdWorker(long workerId, long datacenterId) { |
|||
if (workerId > maxWorkerId || workerId < 0) { |
|||
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); |
|||
} |
|||
if (datacenterId > maxDatacenterId || datacenterId < 0) { |
|||
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); |
|||
} |
|||
this.workerId = workerId; |
|||
this.datacenterId = datacenterId; |
|||
} |
|||
/** |
|||
* 获取下一个ID |
|||
* @return |
|||
*/ |
|||
public synchronized long nextId() { |
|||
long timestamp = timeGen(); |
|||
if (timestamp < lastTimestamp) { |
|||
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); |
|||
} |
|||
|
|||
if (lastTimestamp == timestamp) { |
|||
// 当前毫秒内,则+1
|
|||
sequence = (sequence + 1) & sequenceMask; |
|||
if (sequence == 0) { |
|||
// 当前毫秒内计数满了,则等待下一秒
|
|||
timestamp = tilNextMillis(lastTimestamp); |
|||
} |
|||
} else { |
|||
sequence = 0L; |
|||
} |
|||
lastTimestamp = timestamp; |
|||
// ID偏移组合生成最终的ID,并返回ID
|
|||
long nextId = ((timestamp - twepoch) << timestampLeftShift) |
|||
| (datacenterId << datacenterIdShift) |
|||
| (workerId << workerIdShift) | sequence; |
|||
|
|||
return nextId; |
|||
} |
|||
|
|||
private long tilNextMillis(final long lastTimestamp) { |
|||
long timestamp = this.timeGen(); |
|||
while (timestamp <= lastTimestamp) { |
|||
timestamp = this.timeGen(); |
|||
} |
|||
return timestamp; |
|||
} |
|||
|
|||
private long timeGen() { |
|||
return System.currentTimeMillis(); |
|||
} |
|||
|
|||
/** |
|||
* 获取 maxWorkerId |
|||
*/ |
|||
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { |
|||
StringBuffer mpid = new StringBuffer(); |
|||
mpid.append(datacenterId); |
|||
String name = ManagementFactory.getRuntimeMXBean().getName(); |
|||
if (!name.isEmpty()) { |
|||
/* |
|||
* GET jvmPid |
|||
*/ |
|||
mpid.append(name.split("@")[0]); |
|||
} |
|||
/* |
|||
* MAC + PID 的 hashcode 获取16个低位 |
|||
*/ |
|||
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); |
|||
} |
|||
|
|||
/** |
|||
* 数据标识id部分 |
|||
*/ |
|||
protected static long getDatacenterId(long maxDatacenterId) { |
|||
long id = 0L; |
|||
try { |
|||
InetAddress ip = InetAddress.getLocalHost(); |
|||
NetworkInterface network = NetworkInterface.getByInetAddress(ip); |
|||
if (network == null) { |
|||
id = 1L; |
|||
} else { |
|||
byte[] mac = network.getHardwareAddress(); |
|||
id = ((0x000000FF & (long) mac[mac.length - 1]) |
|||
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; |
|||
id = id % (maxDatacenterId + 1); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println(" getDatacenterId: " + e.getMessage()); |
|||
} |
|||
return id; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,83 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.fasterxml.jackson.annotation.JsonInclude; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
//import com.fasterxml.jackson.databind.DeserializationFeature;
|
|||
import com.fasterxml.jackson.databind.JavaType; |
|||
//import com.fasterxml.jackson.databind.MapperFeature;
|
|||
import com.fasterxml.jackson.databind.MapperFeature; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
|
|||
/** |
|||
* Jackson json序列化和反序列化工具类 |
|||
* @Author JcYen |
|||
* @Date 2019/6/3 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class JsonUtil { |
|||
|
|||
/** |
|||
* jackson对象 |
|||
*/ |
|||
static final ObjectMapper MAPPER = new ObjectMapper(); |
|||
|
|||
static { |
|||
MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); |
|||
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); |
|||
} |
|||
|
|||
/** |
|||
* 将对象转换成json字符串。 |
|||
*/ |
|||
public static String objectToJson(Object data) { |
|||
try { |
|||
String string = MAPPER.writeValueAsString(data); |
|||
return string; |
|||
} catch (JsonProcessingException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 将json结果集转化为对象 |
|||
* |
|||
* @param jsonData json数据 |
|||
* @param beanType 对象中的object类型 |
|||
*/ |
|||
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) { |
|||
try { |
|||
T t = MAPPER.readValue(jsonData, beanType); |
|||
return t; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 将json数据转换成pojo对象list |
|||
*/ |
|||
public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) { |
|||
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); |
|||
try { |
|||
List<T> list = MAPPER.readValue(jsonData, javaType); |
|||
return list; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static Map<String, Object> jsonToMap(String jsonStr){ |
|||
Map<String, Object> json = JSONObject.parseObject(jsonStr, Map.class); |
|||
return json; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,47 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import com.qs.cost.common.utils.model.DesUtils; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
public class SecureUtil extends cn.hutool.crypto.SecureUtil { |
|||
|
|||
|
|||
|
|||
/** |
|||
* 加密 |
|||
* @param secretKey |
|||
* @param clientId |
|||
* @return |
|||
*/ |
|||
public static String desEncrypt(String secretKey,String clientId){ |
|||
String formatStr = clientId+"-"+System.currentTimeMillis(); |
|||
DesUtils des = new DesUtils(secretKey); |
|||
try { |
|||
return des.encrypt(formatStr); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 解密 |
|||
* @param secretKey |
|||
* @param encryptText |
|||
* @return clientId |
|||
*/ |
|||
public static String desDecrypt(String secretKey,String encryptText){ |
|||
DesUtils des = new DesUtils(secretKey); |
|||
try { |
|||
String formatStr = des.decrypt(encryptText); |
|||
return formatStr.split("-")[0]; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,123 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.ServletRequest; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.BufferedReader; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.io.InputStreamReader; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.Enumeration; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@Slf4j |
|||
public class ServletUtil { |
|||
|
|||
/** |
|||
* 获取用户真实IP |
|||
* |
|||
* @return |
|||
*/ |
|||
public static String getIpAddress(HttpServletRequest request) { |
|||
// 优先取X-Real-IP
|
|||
String ip = request.getHeader("X-Real-IP"); |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("x-forwarded-for"); |
|||
} |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getRemoteAddr(); |
|||
if ("0:0:0:0:0:0:0:1".equals(ip)) { |
|||
ip = "undefined"; |
|||
} |
|||
} |
|||
if ("unknown".equalsIgnoreCase(ip)) { |
|||
ip = "undefined"; |
|||
return ip; |
|||
} |
|||
int pos = ip.indexOf(','); |
|||
if (pos >= 0) { |
|||
ip = ip.substring(0, pos); |
|||
} |
|||
return ip; |
|||
} |
|||
|
|||
/** |
|||
* 判断本次请求的数据类型是否为json |
|||
* @param request request |
|||
* @return true: 是 JSON 数据; false: 非 json 数据 |
|||
*/ |
|||
public static boolean isJsonRequest(HttpServletRequest request) { |
|||
if (request.getContentType() != null) { |
|||
return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || |
|||
request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 获取query参数 |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
public static Map<String, String> getParameterMapAll(HttpServletRequest request) { |
|||
Enumeration<String> parameters = request.getParameterNames(); |
|||
Map<String, String> params = new HashMap<>(); |
|||
while (parameters.hasMoreElements()) { |
|||
String parameter = parameters.nextElement(); |
|||
String value = request.getParameter(parameter); |
|||
params.put(parameter, value); |
|||
} |
|||
return params; |
|||
} |
|||
|
|||
/** |
|||
* 获取请求Body |
|||
* |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
public static String getBodyString(ServletRequest request) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
InputStream inputStream = null; |
|||
BufferedReader reader = null; |
|||
try { |
|||
inputStream = request.getInputStream(); |
|||
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); |
|||
String line = ""; |
|||
while ((line = reader.readLine()) != null) { |
|||
sb.append(line); |
|||
} |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
if (inputStream != null) { |
|||
try { |
|||
inputStream.close(); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
if (reader != null) { |
|||
try { |
|||
reader.close(); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,132 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import java.text.NumberFormat; |
|||
import java.util.Random; |
|||
|
|||
/** |
|||
* @Description 创建字符串工具类 |
|||
* @Author JcYen |
|||
* @Date 2019/4/9 |
|||
* @Version v1.0 |
|||
**/ |
|||
public class StringCreateUtil { |
|||
|
|||
private static final String CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
|||
|
|||
private static final String CHARSET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
|||
|
|||
/** |
|||
* 根据长度随机生成字符串(根据时间,有出现重复现象) |
|||
*/ |
|||
public static String randomTimeStr(int length) { |
|||
String randomStr = ""; |
|||
int randomInt; |
|||
Random random = new Random(System.currentTimeMillis()); |
|||
for (int i = 0; i < length; i++) { |
|||
randomInt = random.nextInt(CHARSET.length()); |
|||
randomStr += CHARSET.charAt(randomInt); |
|||
} |
|||
return randomStr; |
|||
} |
|||
|
|||
/** |
|||
* ID对应字符串匹配 |
|||
* @param id |
|||
* @return |
|||
*/ |
|||
public static String caveIdString(Long id){ |
|||
String idString = id.toString(); |
|||
String rs = ""; |
|||
for(int i=0;i<idString.length();i++){ |
|||
Integer c2idx = Integer.parseInt(idString.charAt(i)+""); |
|||
rs += CHARSET2.charAt(c2idx); |
|||
} |
|||
return reverseStr(rs); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 字符串倒序输出 |
|||
* @param str |
|||
* @return |
|||
*/ |
|||
private static String reverseStr(String str) { |
|||
StringBuffer buffer = new StringBuffer(str); |
|||
return buffer.reverse().toString().toLowerCase(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 根据长度随机生成字符串 |
|||
*/ |
|||
public static String randomStr(int length){ |
|||
Random random=new Random(); |
|||
StringBuffer sb=new StringBuffer(); |
|||
for(int i=0;i<length;i++){ |
|||
int number=random.nextInt(3); |
|||
long result=0; |
|||
switch(number){ |
|||
case 0: |
|||
result= Math.round(Math.random()*25+65); |
|||
sb.append(String.valueOf((char)result)); |
|||
break; |
|||
case 1: |
|||
result= Math.round(Math.random()*25+97); |
|||
sb.append(String.valueOf((char)result)); |
|||
break; |
|||
case 2: |
|||
sb.append(String.valueOf(new Random().nextInt(10))); |
|||
break; |
|||
} |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 生成随机数字字符串 |
|||
*/ |
|||
public static String randomNumStr(int length) { |
|||
Random random = new Random(); |
|||
String result = ""; |
|||
for (int i = 0; i < length; i++) { |
|||
result += random.nextInt(10); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 返回随机数 |
|||
* @param length |
|||
* @return |
|||
*/ |
|||
public static int randomNum(int length){ |
|||
String str = randomNumStr(length); |
|||
return Integer.parseInt(str); |
|||
} |
|||
|
|||
public static String ellipticalTitle(String title, Integer len){ |
|||
if(title!=null&&title.length()>len){ |
|||
return title.substring(len)+"..."; |
|||
}return title; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* description: 格式化数字,实现左侧补 0. |
|||
* @param num 格式化的数字 |
|||
* @param min 最小位数 |
|||
* @param max 最大位数 |
|||
*/ |
|||
public static String fill(int num , int min , int max) { |
|||
NumberFormat numberFormat = NumberFormat.getInstance(); |
|||
// 禁用数字格式化分组。 如: 000,001
|
|||
numberFormat.setGroupingUsed(false); |
|||
// 保留最小位数
|
|||
numberFormat.setMinimumIntegerDigits(min); |
|||
// 保留最大位数
|
|||
numberFormat.setMaximumIntegerDigits(max); |
|||
return numberFormat.format(num); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package com.qs.cost.common.utils; |
|||
|
|||
import org.springframework.util.StringUtils; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @version 1.0 |
|||
* @date 2021/6/16 |
|||
**/ |
|||
public class StringUtil { |
|||
|
|||
public static boolean isEmpty(Object obj){ |
|||
return StringUtils.isEmpty(obj); |
|||
} |
|||
|
|||
public static boolean isNotEmpty(String str){ |
|||
return str!=null && str.trim().length()>0; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,126 @@ |
|||
package com.qs.cost.common.utils.model; |
|||
|
|||
import java.security.Key; |
|||
|
|||
import javax.crypto.Cipher; |
|||
|
|||
/** |
|||
* 使用DES算法对字符串进行加密解密 (加密解密的操作步骤正好相反, 参考 {@link #encrypt(String)}, {@link #decrypt(String)}) |
|||
*/ |
|||
public class DesUtils { |
|||
private static String defaultSecretKey = "default_secret_key"; //默认密钥
|
|||
private Cipher encryptCipher = null; //加密器
|
|||
private Cipher decryptCipher = null; //解密器
|
|||
|
|||
public DesUtils() throws Exception { |
|||
this(defaultSecretKey); |
|||
} |
|||
|
|||
/** |
|||
* @param secretKey 加密解密使用的密钥 |
|||
*/ |
|||
public DesUtils(String secretKey) { |
|||
Key key; |
|||
try { |
|||
key = getKey(secretKey.getBytes()); |
|||
encryptCipher = Cipher.getInstance("DES"); |
|||
encryptCipher.init(Cipher.ENCRYPT_MODE, key); |
|||
decryptCipher = Cipher.getInstance("DES"); |
|||
decryptCipher.init(Cipher.DECRYPT_MODE, key); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加密 (逻辑: 1. 将要加密的字符串转换为字节数组(byte array)<br/> |
|||
* 2. 将第一步的字节数组作为输入使用加密器(Cipher)的doFinal方法进行加密, 返回字节数组<br/> |
|||
* 3. 把加密后的字节数组转换成十六进制的字符串)<br/> |
|||
* @param strIn 要加密的字符串 |
|||
* @return 返回加密后的十六进制字符串 |
|||
* @throws Exception |
|||
*/ |
|||
public String encrypt(String strIn) throws Exception { |
|||
return byteArr2HexStr(encrypt(strIn.getBytes())); |
|||
} |
|||
|
|||
public byte[] encrypt(byte[] arrB) throws Exception { |
|||
return encryptCipher.doFinal(arrB); |
|||
} |
|||
|
|||
/** |
|||
* 解密 (逻辑: 1. 把加密后的十六进制字符串转换成字节数组(byte array)<br/> |
|||
* 2. 将第一步的字节数组作为输入使用加密器(Cipher)的doFinal方法进行解密, 返回字节数组(byte array)<br/> |
|||
* 3. 把解密后的字节数组转换成字符串)<br/> |
|||
* @param strIn |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
public String decrypt(String strIn) throws Exception { |
|||
return new String(decrypt(hexStr2ByteArr(strIn))); |
|||
} |
|||
|
|||
public byte[] decrypt(byte[] arrB) throws Exception { |
|||
return decryptCipher.doFinal(arrB); |
|||
} |
|||
|
|||
public static String byteArr2HexStr(byte[] arrB) throws Exception { |
|||
int iLen = arrB.length; |
|||
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
|
|||
StringBuffer sb = new StringBuffer(iLen * 2); |
|||
for (int i = 0; i < iLen; i++) { |
|||
int intTmp = arrB[i]; |
|||
// 把负数转换为正数
|
|||
while (intTmp < 0) { |
|||
intTmp = intTmp + 256; |
|||
} |
|||
// 小于0F的数需要在前面补0
|
|||
if (intTmp < 16) { |
|||
sb.append("0"); |
|||
} |
|||
sb.append(Integer.toString(intTmp, 16)); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
public static byte[] hexStr2ByteArr(String strIn) throws Exception { |
|||
byte[] arrB = strIn.getBytes(); |
|||
int iLen = arrB.length; |
|||
// 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
|
|||
byte[] arrOut = new byte[iLen / 2]; |
|||
for (int i = 0; i < iLen; i = i + 2) { |
|||
String strTmp = new String(arrB, i, 2); |
|||
arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16); |
|||
} |
|||
return arrOut; |
|||
} |
|||
|
|||
private Key getKey(byte[] arrBTmp) throws Exception { |
|||
// 创建一个空的8位字节数组(默认值为0)
|
|||
byte[] arrB = new byte[8]; |
|||
// 将原始字节数组转换为8位
|
|||
for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) { |
|||
arrB[i] = arrBTmp[i]; |
|||
} |
|||
// 生成密钥
|
|||
Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES"); |
|||
return key; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 用法实例 |
|||
*/ |
|||
public static void main(String[] args) { |
|||
try { |
|||
String test = "liwc"; |
|||
DesUtils des = new DesUtils("leemenz"); //自定义密钥
|
|||
System.out.println("加密前的字符:" + test); |
|||
System.out.println("加密后的字符:" + des.encrypt(test)); |
|||
System.out.println("解密后的字符:" + des.decrypt(des.encrypt(test))); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,138 @@ |
|||
package com.qs.cost.module.controller; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.qs.cost.common.consts.ApiStatusConst; |
|||
import com.qs.cost.common.consts.ApiUrlConst; |
|||
import com.qs.cost.common.dto.BeanParams; |
|||
import com.qs.cost.common.dto.R; |
|||
import com.qs.cost.common.dto.U8APIBo; |
|||
import com.qs.cost.common.enums.HttpCode; |
|||
import com.qs.cost.common.utils.*; |
|||
import com.qs.cost.module.domain.ApiBook; |
|||
import com.qs.cost.module.domain.ApiRequest; |
|||
import com.qs.cost.module.domain.dto.JobDataBaseParam; |
|||
import com.qs.cost.module.domain.dto.JobErpParam; |
|||
import com.qs.cost.module.domain.dto.JobRequestParam; |
|||
import com.qs.cost.module.mapper.ApiRequestMapper; |
|||
import com.qs.cost.module.service.ApiService; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import javax.annotation.Resource; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.validation.Valid; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class ApiController { |
|||
|
|||
@Value("${server.secret-key}") |
|||
private String secretKey; |
|||
|
|||
@Resource |
|||
private ApiService apiService; |
|||
|
|||
@Resource |
|||
private ApiRequestMapper apiRequestMapper; |
|||
|
|||
/** |
|||
* 核销系统请求,保存任务队列 |
|||
* @param request |
|||
* @param jobParam |
|||
* @return |
|||
*/ |
|||
@PostMapping("/jobRequest") |
|||
public R jobRequest(HttpServletRequest request,@RequestBody @Valid JobRequestParam jobParam){ |
|||
boolean callback = jobParam.getCallbackState()!=null&&jobParam.getCallbackState().equals(1); |
|||
String tenant = jobParam.getBook(); |
|||
String token = request.getHeader("Authorization"); |
|||
if(token==null||!token.equals(secretKey)){Assert.throwEx(HttpCode.FORBIDDEN_403);} |
|||
ApiBook bookConf = apiService.getBook(tenant); |
|||
if(bookConf==null){Assert.throwEx(HttpCode.FORBIDDEN_403_1);} |
|||
if(callback&&StringUtil.isEmpty(jobParam.getCallbackHost())){Assert.throwEx(HttpCode.ERROR_CALLBACK_URL_EMPTY);} |
|||
String pk = IdUtil.getLong()+""; |
|||
JobErpParam erpParam = new JobErpParam(); |
|||
erpParam.setPassword(bookConf.getErpPwd()); |
|||
erpParam.setUsername(bookConf.getErpUser()); |
|||
JobDataBaseParam dataBaseParam = new JobDataBaseParam(); |
|||
dataBaseParam.setDbname(bookConf.getDbName()); |
|||
dataBaseParam.setHost(bookConf.getDbHost()); |
|||
dataBaseParam.setUsername(bookConf.getDbUser()); |
|||
dataBaseParam.setPassword(bookConf.getDbPwd()); |
|||
dataBaseParam.setPort(bookConf.getDbPort()); |
|||
//拼装请求参数
|
|||
U8APIBo u8APIBo = U8APIBo.builder() |
|||
.cmd(jobParam.getCmd()) |
|||
.setBook(jobParam.getBook()) |
|||
.setYear(jobParam.getYear()) |
|||
.params(new BeanParams(jobParam.getJsonBody(),jobParam.getKeyId())) |
|||
.erpUser(erpParam) |
|||
.dataBase(dataBaseParam) |
|||
.requestId(pk) |
|||
.build(); |
|||
String params = JsonUtil.objectToJson(u8APIBo); |
|||
String reqUrl = bookConf.getU8Host().concat(ApiUrlConst.U8API); |
|||
ApiRequest apiRequestLog = new ApiRequest(); |
|||
apiRequestLog.setId(pk); |
|||
apiRequestLog.setTenant(jobParam.getBook()); |
|||
apiRequestLog.setClientHost(jobParam.getCallbackHost()); |
|||
apiRequestLog.setApiPk(jobParam.getPk()); |
|||
apiRequestLog.setApiCmd(jobParam.getCmd()); |
|||
apiRequestLog.setApiYear(jobParam.getYear()); |
|||
apiRequestLog.setApiKeyId(jobParam.getKeyId()); |
|||
apiRequestLog.setReqParams(params); |
|||
apiRequestLog.setReqUrl(reqUrl); |
|||
apiRequestLog.setReqParams(JsonUtil.objectToJson(u8APIBo)); |
|||
apiRequestLog.setRetryTimes(0); |
|||
apiRequestLog.setSettingRetryTimes(jobParam.getSettingRetryTimes()); |
|||
apiRequestLog.setNeedCallback(callback?1:0); |
|||
apiRequestMapper.insert(apiRequestLog); |
|||
return R.ok(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 轮询任务(定时任务触发) |
|||
* @return |
|||
*/ |
|||
@PostMapping("/doJob") |
|||
public R pollingJob(){ |
|||
LambdaQueryWrapper<ApiRequest> wrapper4U8 = new LambdaQueryWrapper<>(); |
|||
wrapper4U8.lt(ApiRequest::getStatus,3); |
|||
wrapper4U8.eq(ApiRequest::getInterruptStatus,0); |
|||
//请求U8
|
|||
List<ApiRequest> sendU8List = apiRequestMapper.selectList(wrapper4U8); |
|||
for (ApiRequest apiRequest : sendU8List) { |
|||
apiService.reqU8Service(apiRequest); |
|||
} |
|||
//执行回调
|
|||
LambdaQueryWrapper<ApiRequest> wrapper4Client = new LambdaQueryWrapper<>(); |
|||
wrapper4Client.eq(ApiRequest::getStatus,3); |
|||
wrapper4Client.eq(ApiRequest::getInterruptStatus,0); |
|||
//请求客户端
|
|||
List<ApiRequest> callbackList = apiRequestMapper.selectList(wrapper4Client); |
|||
for (ApiRequest apiRequest : callbackList) { |
|||
if(apiRequest.getRetryTimes()<=0){ |
|||
apiRequest.setInterruptStatus(1); |
|||
apiRequestMapper.updateById(apiRequest); |
|||
continue; |
|||
} |
|||
boolean callbackSuccess = apiService.callback(apiRequest); |
|||
if(callbackSuccess){ |
|||
//判断是否成功
|
|||
int retryTime = apiRequest.getRetryTimes()==null?0:apiRequest.getRetryTimes(); |
|||
apiRequest.setRetryTimes(retryTime+1); |
|||
apiRequest.setStatus(ApiStatusConst.ApiRequest_Status_4); |
|||
} |
|||
apiRequestMapper.updateById(apiRequest); |
|||
} |
|||
return R.ok(); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,54 @@ |
|||
package com.qs.cost.module.domain; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 账套信息 实体类 |
|||
* @author JcYen |
|||
* @since 2021-10-12 |
|||
*/ |
|||
@Data |
|||
@TableName("api_book") |
|||
public class ApiBook implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@TableId(type = IdType.AUTO) |
|||
private Long id; |
|||
|
|||
/** 租户 */ |
|||
private String tenant; |
|||
|
|||
/** 账套名称 */ |
|||
private String name; |
|||
|
|||
/** 主题 */ |
|||
private String topic; |
|||
|
|||
/** 对应品牌 */ |
|||
private String brand; |
|||
|
|||
/** 账套 */ |
|||
private String book; |
|||
|
|||
/** 年份 */ |
|||
private Integer year; |
|||
|
|||
/** 请求地址 */ |
|||
private String u8Host; |
|||
private String erpUser; |
|||
private String erpPwd; |
|||
private String dbName; |
|||
private String dbHost; |
|||
private String dbPort; |
|||
private String dbUser; |
|||
private String dbPwd; |
|||
|
|||
} |
|||
|
@ -0,0 +1,97 @@ |
|||
package com.qs.cost.module.domain; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.util.Date; |
|||
import java.io.Serializable; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import lombok.Data; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
|
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* 请求记录历史 实体类 |
|||
* @author YenHex |
|||
* @since 2022-02-28 |
|||
*/ |
|||
@Data |
|||
@TableName("api_request") |
|||
public class ApiRequest implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** id */ |
|||
@TableId(type = IdType.INPUT) |
|||
private String id; |
|||
|
|||
/** 账套、租户 */ |
|||
@NotNull(message = "账套、租户不能为空") |
|||
private String tenant; |
|||
|
|||
/** 客户端IP */ |
|||
@NotNull(message = "客户端IP不能为空") |
|||
private String clientHost; |
|||
|
|||
/** 接口名称 */ |
|||
@NotNull(message = "接口名称不能为空") |
|||
private String apiCmd; |
|||
|
|||
/** 接口数据主键 */ |
|||
@NotNull(message = "接口数据主键不能为空") |
|||
private String apiPk; |
|||
|
|||
/** 接口年份 */ |
|||
@NotNull(message = "接口年份不能为空") |
|||
private String apiYear; |
|||
|
|||
/** 接口keyId */ |
|||
@NotNull(message = "接口keyId不能为空") |
|||
private String apiKeyId; |
|||
|
|||
/** 请求地址 */ |
|||
@NotNull(message = "请求地址不能为空") |
|||
private String reqUrl; |
|||
|
|||
/** 请求参数 */ |
|||
@NotNull(message = "请求参数不能为空") |
|||
private String reqParams; |
|||
|
|||
/** 请求时间 */ |
|||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") |
|||
private LocalDateTime reqTime; |
|||
|
|||
/** HTTP返回的状态码 */ |
|||
private Integer respCode; |
|||
|
|||
/** 请求耗时(单位秒) */ |
|||
private Double respTimes; |
|||
|
|||
/** 数据返回结果 */ |
|||
private String respContext; |
|||
|
|||
/** 重试次数 */ |
|||
private Integer retryTimes; |
|||
|
|||
/** 设置重试次数 */ |
|||
private Integer settingRetryTimes; |
|||
|
|||
private Integer needCallback; |
|||
|
|||
/** 状态【0->无需回调;1->;未回调等待回调;2->请求U8失败;3->请求U8成功,但回调客户端失败;4->回调客户端成功】*/ |
|||
private Integer status; |
|||
|
|||
/** 是否中断 */ |
|||
private Integer interruptStatus; |
|||
|
|||
/** 创建时间 */ |
|||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") |
|||
private LocalDateTime createTime; |
|||
|
|||
} |
|||
|
@ -0,0 +1,26 @@ |
|||
package com.qs.cost.module.domain.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@Data |
|||
public class JobCallbackParam { |
|||
|
|||
/** |
|||
* 回调参数 |
|||
*/ |
|||
@NotNull |
|||
private String jsonBody; |
|||
|
|||
/** |
|||
* 回调ID |
|||
*/ |
|||
@NotNull |
|||
private String keyId; |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.qs.cost.module.domain.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/6/20 |
|||
*/ |
|||
@Data |
|||
public class JobDataBaseParam { |
|||
|
|||
private String host; |
|||
|
|||
private String port; |
|||
|
|||
private String username; |
|||
|
|||
private String password; |
|||
|
|||
private String dbname; |
|||
|
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.qs.cost.module.domain.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/6/20 |
|||
*/ |
|||
@Data |
|||
public class JobErpParam { |
|||
|
|||
private String username; |
|||
|
|||
private String password; |
|||
|
|||
} |
@ -0,0 +1,50 @@ |
|||
package com.qs.cost.module.domain.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.Max; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@Data |
|||
public class JobRequestParam { |
|||
|
|||
/** |
|||
* 拓展:存储单号 |
|||
*/ |
|||
private String pk; |
|||
|
|||
@NotNull |
|||
private String cmd; |
|||
|
|||
@NotNull |
|||
private String year; |
|||
|
|||
@NotNull |
|||
private String book; |
|||
|
|||
@NotNull |
|||
private String jsonBody; |
|||
|
|||
private String keyId; |
|||
|
|||
/** |
|||
* 是否需要回调(不需要回调,只触发一次) |
|||
*/ |
|||
private Integer callbackState; |
|||
|
|||
/** |
|||
* 设置失败重连次数 |
|||
*/ |
|||
@Max(value = 10,message = "尝试次数不能超过10次") |
|||
private Integer settingRetryTimes; |
|||
|
|||
/** |
|||
* 回调地址 |
|||
*/ |
|||
private String callbackHost; |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
package com.qs.cost.module.mapper; |
|||
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.qs.cost.module.domain.ApiBook; |
|||
|
|||
/** |
|||
* 账套信息 Mapper |
|||
* @Author JcYen |
|||
* @Date 2021-10-12 |
|||
*/ |
|||
public interface ApiBookMapper extends BaseMapper<ApiBook> { |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
package com.qs.cost.module.mapper; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.qs.cost.module.domain.ApiRequest; |
|||
|
|||
/** |
|||
* 请求记录历史 Mapper |
|||
* @author YenHex |
|||
* @date 2022-02-25 |
|||
*/ |
|||
public interface ApiRequestMapper extends BaseMapper<ApiRequest> { |
|||
|
|||
} |
|||
|
@ -0,0 +1,99 @@ |
|||
package com.qs.cost.module.service; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.qs.cost.common.consts.ApiStatusConst; |
|||
import com.qs.cost.common.dto.HttpResult; |
|||
import com.qs.cost.common.utils.DateUtil; |
|||
import com.qs.cost.common.utils.HttpUtil; |
|||
import com.qs.cost.common.utils.JsonUtil; |
|||
import com.qs.cost.module.domain.ApiBook; |
|||
import com.qs.cost.module.domain.ApiRequest; |
|||
import com.qs.cost.module.mapper.ApiBookMapper; |
|||
import com.qs.cost.module.mapper.ApiRequestMapper; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
@AllArgsConstructor |
|||
public class ApiService { |
|||
|
|||
private final ApiRequestMapper apiRequestMapper; |
|||
private final ApiBookMapper bookConfMapper; |
|||
|
|||
public ApiBook getBook(String tenant){ |
|||
LambdaQueryWrapper<ApiBook> wrapper = new LambdaQueryWrapper<>(); |
|||
wrapper.eq(ApiBook::getTenant,tenant); |
|||
List<ApiBook> list = bookConfMapper.selectList(wrapper); |
|||
return list.size()>0?list.get(0):null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 请求U8服务 |
|||
* @param apiRequestLog |
|||
*/ |
|||
public void reqU8Service(ApiRequest apiRequestLog){ |
|||
//超过失败次数
|
|||
int retryTimes = apiRequestLog.getSettingRetryTimes() - apiRequestLog.getRetryTimes(); |
|||
if(apiRequestLog.getStatus().equals(ApiStatusConst.ApiRequest_Status_2)&&retryTimes<=0){ |
|||
apiRequestLog.setInterruptStatus(1); |
|||
apiRequestMapper.updateById(apiRequestLog); |
|||
return; |
|||
} |
|||
Map<String,Object> maps = new HashMap<>(); |
|||
maps.put("json",apiRequestLog.getReqParams()); |
|||
//请求U8接口
|
|||
long startTime = System.currentTimeMillis(); |
|||
HttpResult httpResult = HttpUtil.post(apiRequestLog.getReqUrl(),maps); |
|||
long endTime = System.currentTimeMillis(); |
|||
//回调保存基本信息
|
|||
apiRequestLog.setRespContext(JsonUtil.objectToJson(httpResult)); |
|||
apiRequestLog.setRespTimes(DateUtil.printRunTime(startTime,endTime)); |
|||
apiRequestLog.setRespCode(httpResult.getCode()); |
|||
//判断是否成功
|
|||
int retryTime = apiRequestLog.getRetryTimes()==null?0:apiRequestLog.getRetryTimes(); |
|||
if(httpResult.getCode().equals(200)){ |
|||
if(httpResult.getData()!=null&&httpResult.getData().contains("code\":200")){ |
|||
apiRequestLog.setStatus(ApiStatusConst.ApiRequest_Status_4); |
|||
//请求客户端
|
|||
if(apiRequestLog.getNeedCallback().equals(1)){ |
|||
boolean callbackResult = callback(apiRequestLog); |
|||
if(!callbackResult){ |
|||
apiRequestLog.setStatus(ApiStatusConst.ApiRequest_Status_3); |
|||
} |
|||
} |
|||
}else { |
|||
apiRequestLog.setStatus(ApiStatusConst.ApiRequest_Status_2); |
|||
} |
|||
}else { |
|||
apiRequestLog.setStatus(ApiStatusConst.ApiRequest_Status_1); |
|||
} |
|||
apiRequestLog.setRetryTimes(retryTime+1); |
|||
apiRequestMapper.updateById(apiRequestLog); |
|||
} |
|||
|
|||
/** |
|||
* 请求回调 |
|||
* @param apiRequestLog |
|||
* @return |
|||
*/ |
|||
public boolean callback(ApiRequest apiRequestLog){ |
|||
HttpResult callback = HttpUtil.post(apiRequestLog.getClientHost(),JsonUtil.jsonToMap(apiRequestLog.getRespContext())); |
|||
if(callback.getCode().equals(200)){ |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
#服务配置 |
|||
server: |
|||
secret-key: 7c0b22847a4abf282399bdfca150c6b6ca8e67d171e7f948 |
|||
port: 7701 |
|||
#SpringBoot相关 |
|||
spring: |
|||
#数据源 |
|||
datasource: |
|||
url: jdbc:mysql://127.0.0.1:3333/jsl_cost_api?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai |
|||
username: root |
|||
password: 123456 |
|||
driverClassName: com.mysql.cj.jdbc.Driver |
|||
#mybatis plus |
|||
mybatis-plus: |
|||
type-aliases-package: com.qs.cost.module.domain |
|||
configuration: |
|||
map-underscore-to-camel-case: true |
|||
global-config: |
|||
banner: false |
|||
db-config: |
|||
logic-delete-value: 1 # 逻辑已删除值(默认为 1) |
|||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) |
|||
# 日志配置 |
|||
logging: |
|||
config: classpath:logback-spring.xml |
@ -0,0 +1,3 @@ |
|||
spring: |
|||
profiles: |
|||
active: dev |
@ -0,0 +1,51 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<configuration> |
|||
<!-- 文件输出格式 --> |
|||
<property name="PATTERN" |
|||
value="%boldMagenta(%-12(%d{yyyy-MM-dd HH:mm:ss.SSS})) |- %highlight(%-5level) %cyan([%thread]) %green(%c [%L]) -| %msg%n"/> |
|||
<property name="LOG_NAME" value="qs"/> |
|||
<!--所在项目包 打印日志用--> |
|||
<property name="PACKAGE_NAME" value="com.qs"/> |
|||
<!-- test文件路径 --> |
|||
<property name="TEST_FILE_PATH" value="logs/${LOG_NAME}"/> |
|||
<!-- pro文件路径 --> |
|||
<property name="PROD_FILE_PATH" value="logs/${LOG_NAME}"/> |
|||
|
|||
<!-- 开发环境 --> |
|||
<springProfile name="dev"> |
|||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>${PATTERN}</pattern> |
|||
</encoder> |
|||
</appender> |
|||
<!-- 输出到文件 --> |
|||
<logger name="org.springframework.jdbc.core.JdbcTemplate" level="INFO"/> |
|||
<logger name="${PACKAGE_NAME}" level="debug"/> |
|||
<root level="info"> |
|||
<appender-ref ref="CONSOLE"/> |
|||
</root> |
|||
</springProfile> |
|||
|
|||
<!-- 生产环境 --> |
|||
<springProfile name="prod"> |
|||
<appender name="PROD_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${PROD_FILE_PATH}${file.separator}${LOG_NAME}.log</file> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
|||
<!-- rollover daily --> |
|||
<fileNamePattern>${PROD_FILE_PATH}${file.separator}${LOG_NAME}-%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
|||
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB --> |
|||
<maxHistory>7</maxHistory> |
|||
<maxFileSize>100MB</maxFileSize> |
|||
<totalSizeCap>1GB</totalSizeCap> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern> |
|||
[%date{yyyy-MM-dd HH:mm:ss.SSS}] %X{logthreadId} %-5level %logger{80} %method %line - %msg%n |
|||
</pattern> |
|||
</encoder> |
|||
</appender> |
|||
<root level="info"> |
|||
<appender-ref ref="PROD_FILE"/> |
|||
</root> |
|||
</springProfile> |
|||
</configuration> |
@ -0,0 +1,78 @@ |
|||
package boot; |
|||
|
|||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; |
|||
import com.qs.cost.common.dto.HttpResult; |
|||
import com.qs.cost.common.utils.HttpUtil; |
|||
import com.qs.cost.common.utils.SecureUtil; |
|||
|
|||
import java.net.URLEncoder; |
|||
import java.util.*; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2021/12/1 |
|||
*/ |
|||
public class PrintDemo { |
|||
|
|||
public static final String lzyunli = "lzyunli"; |
|||
|
|||
|
|||
public static void main(String[] args) { |
|||
String date = "2020/04/25-2022/07/26"; |
|||
String page = "1"; |
|||
String pageSize = "10"; |
|||
|
|||
String params = "date="+date+"&page="+page+"&page_size="+pageSize; |
|||
System.out.println(params); |
|||
|
|||
String signParam = lzyunli + params + lzyunli; |
|||
System.out.println("加密前:"+signParam); |
|||
String sign = SecureUtil.md5(signParam); |
|||
params = params+"&sign="+sign; |
|||
HttpResult httpResult = HttpUtil.get("http://wsapp6.lzyunli.com/getOrder?"+params); |
|||
System.out.println(httpResult.getData()); |
|||
} |
|||
|
|||
/** |
|||
* @param paraMap 请求参数 |
|||
* @param urlEncode 是否utf-8编码 |
|||
* @return |
|||
*/ |
|||
public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode) { |
|||
String buff = ""; |
|||
Map<String, String> tmpMap = paraMap; |
|||
try { |
|||
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet()); |
|||
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
|
|||
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() { |
|||
@Override |
|||
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) { |
|||
return (o1.getKey()).toString().compareTo(o2.getKey()); |
|||
} |
|||
}); |
|||
// 构造返回字符串键值对的格式
|
|||
StringBuilder buf = new StringBuilder(); |
|||
for (Map.Entry<String, String> item : infoIds) { |
|||
if (StringUtils.isNotBlank(item.getKey())) { |
|||
String key = item.getKey(); |
|||
String val = item.getValue(); |
|||
if (urlEncode) { |
|||
val = URLEncoder.encode(val, "utf-8"); |
|||
} |
|||
|
|||
buf.append(key + "=" + val); |
|||
buf.append("&"); |
|||
} |
|||
|
|||
} |
|||
buff = buf.toString(); |
|||
if (buff.isEmpty() == false) { |
|||
buff = buff.substring(0, buff.length() - 1); |
|||
} |
|||
} catch (Exception e) { |
|||
return null; |
|||
} |
|||
return buff; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
|
|||
|
|||
### |
|||
POST localhost:7701/api/jobRequest |
|||
Accept:application/json |
|||
Content-Type: application/json |
|||
Authorization: 7c0b22847a4abf282399bdfca150c6b6ca8e67d171e7f948 |
|||
|
|||
{ |
|||
"pk": "2022021601184", |
|||
"cmd": "XSBJDAdd", |
|||
"year": "2020", |
|||
"book": "001", |
|||
"jsonBody": "[{\"cCode\":\"202202160183\",\"dDate\":\"2022-02-16 00:00:00\",\"cSTCode\":\"01\",\"cCusCode\":\"11503001\",\"cDepCode\":\"1011010\",\"cMaker\":\"张春华\",\"iTaxRate\":13,\"cMemo\":\"\",\"cDefine11\":\"侯卫松19928948050增值税发票,尽快发货\",\"cDefine10\":\"04b21b7b9a2d46e693f36f5bf030837a\",\"Details\":[{\"cInvCode\":\"301010759\",\"iQuotedPrice\":106,\"iQuantity\":5,\"iTaxUnitPrice\":106,\"iSum\":530.0,\"cDefine32\":\"7fdf05d1e87d483d99208fb893e75fae\",\"cMemo\":\"\"},{\"cInvCode\":\"302010481\",\"iQuotedPrice\":48,\"iQuantity\":10,\"iTaxUnitPrice\":48,\"iSum\":480.0,\"cDefine32\":\"0cecae9542534e02b29f3f0b9f795887\",\"cMemo\":\"\"},{\"cInvCode\":\"302020255\",\"iQuotedPrice\":98,\"iQuantity\":3,\"iTaxUnitPrice\":98,\"iSum\":294.0,\"cDefine32\":\"8733797c438847a191a9a9d1f5e45f2a\",\"cMemo\":\"\"},{\"cInvCode\":\"302020256\",\"iQuotedPrice\":98,\"iQuantity\":3,\"iTaxUnitPrice\":98,\"iSum\":294.0,\"cDefine32\":\"fab18c81e68e4a64ad47b701f1a74c15\",\"cMemo\":\"\"},{\"cInvCode\":\"302020306\",\"iQuotedPrice\":63,\"iQuantity\":3,\"iTaxUnitPrice\":63,\"iSum\":189.0,\"cDefine32\":\"cc98b54569ef4b7ca42bbb6783afd00f\",\"cMemo\":\"\"},{\"cInvCode\":\"302020309\",\"iQuotedPrice\":63,\"iQuantity\":3,\"iTaxUnitPrice\":63,\"iSum\":189.0,\"cDefine32\":\"f097ff1a4dce4fffa30efe0aef11d18f\",\"cMemo\":\"\"},{\"cInvCode\":\"303020094\",\"iQuotedPrice\":109,\"iQuantity\":10,\"iTaxUnitPrice\":109,\"iSum\":1090.0,\"cDefine32\":\"095a5916fc734e78af479977be7a9e49\",\"cMemo\":\"\"},{\"cInvCode\":\"303020095\",\"iQuotedPrice\":109,\"iQuantity\":10,\"iTaxUnitPrice\":109,\"iSum\":1090.0,\"cDefine32\":\"2efc1c6997a24a24999f52bf8e167457\",\"cMemo\":\"\"},{\"cInvCode\":\"303020096\",\"iQuotedPrice\":109,\"iQuantity\":3,\"iTaxUnitPrice\":109,\"iSum\":327.0,\"cDefine32\":\"942c5b315da543bc93c49ef966d6c849\",\"cMemo\":\"\"},{\"cInvCode\":\"303020124\",\"iQuotedPrice\":68,\"iQuantity\":70,\"iTaxUnitPrice\":68,\"iSum\":4760.0,\"cDefine32\":\"1ec3cfe6bbf6490bbf832a0bc08757a8\",\"cMemo\":\"\"},{\"cInvCode\":\"303020125\",\"iQuotedPrice\":68,\"iQuantity\":100,\"iTaxUnitPrice\":68,\"iSum\":6800.0,\"cDefine32\":\"4fa6a12ac9c7472e9d9d0d576160665c\",\"cMemo\":\"\"},{\"cInvCode\":\"30302848\",\"iQuotedPrice\":68,\"iQuantity\":30,\"iTaxUnitPrice\":68,\"iSum\":2040.0,\"cDefine32\":\"cbae2b978ba84652b943d27b8007e732\",\"cMemo\":\"\"},{\"cInvCode\":\"304010059\",\"iQuotedPrice\":49,\"iQuantity\":120,\"iTaxUnitPrice\":49,\"iSum\":5880.0,\"cDefine32\":\"de5d4aa0f57c49e0aebde80c3780ab32\",\"cMemo\":\"\"},{\"cInvCode\":\"304010062\",\"iQuotedPrice\":49,\"iQuantity\":100,\"iTaxUnitPrice\":49,\"iSum\":4900.0,\"cDefine32\":\"4a0b5fd2282f49aebccf09778ab49b1f\",\"cMemo\":\"\"},{\"cInvCode\":\"304010064\",\"iQuotedPrice\":49,\"iQuantity\":80,\"iTaxUnitPrice\":49,\"iSum\":3920.0,\"cDefine32\":\"edd27c1749af45589858bdf53d85caa8\",\"cMemo\":\"\"},{\"cInvCode\":\"304010221\",\"iQuotedPrice\":49,\"iQuantity\":30,\"iTaxUnitPrice\":49,\"iSum\":1470.0,\"cDefine32\":\"517cbbcdd21a49bfa819862890c5dba7\",\"cMemo\":\"\"},{\"cInvCode\":\"304010274\",\"iQuotedPrice\":100.8,\"iQuantity\":10,\"iTaxUnitPrice\":100.8,\"iSum\":1008.0,\"cDefine32\":\"7153427e03a64362aa6f9fc6f806b817\",\"cMemo\":\"\"},{\"cInvCode\":\"304010276\",\"iQuotedPrice\":100.8,\"iQuantity\":10,\"iTaxUnitPrice\":100.8,\"iSum\":1008.0,\"cDefine32\":\"240abf6b0fa440d1945c9d750de3670a\",\"cMemo\":\"\"},{\"cInvCode\":\"307020199\",\"iQuotedPrice\":64,\"iQuantity\":50,\"iTaxUnitPrice\":64,\"iSum\":3200.0,\"cDefine32\":\"8c7aa7dc67db4acd90a40dbcfb707e1c\",\"cMemo\":\"\"},{\"cInvCode\":\"307020202\",\"iQuotedPrice\":64,\"iQuantity\":20,\"iTaxUnitPrice\":64,\"iSum\":1280.0,\"cDefine32\":\"cf801053d2014684b508a2ca9f5e30d2\",\"cMemo\":\"\"},{\"cInvCode\":\"307020205\",\"iQuotedPrice\":64,\"iQuantity\":50,\"iTaxUnitPrice\":64,\"iSum\":3200.0,\"cDefine32\":\"a59cfb39f4db44a3b07376d8aba9c699\",\"cMemo\":\"\"},{\"cInvCode\":\"307020208\",\"iQuotedPrice\":64,\"iQuantity\":20,\"iTaxUnitPrice\":64,\"iSum\":1280.0,\"cDefine32\":\"59c0ca57d39f47368c474a7924481994\",\"cMemo\":\"\"}]}]", |
|||
"keyId": "", |
|||
"callbackHost": "http://localhost:7701/test", |
|||
"callbackState": 1, |
|||
"settingRetryTimes": 2 |
|||
} |
|||
|
|||
|
|||
### |
|||
POST localhost:7701/api/doJob |
|||
Accept:application/json |
|||
Content-Type: application/json |
|||
|
Loading…
Reference in new issue