Browse Source

init

master
Yen 3 years ago
commit
9541b7a882
  1. 2
      .gitignore
  2. 36
      README.en.md
  3. 39
      README.md
  4. 122
      pom.xml
  5. 42
      src/main/java/com/qs/cost/Application.java
  6. 19
      src/main/java/com/qs/cost/common/conf/CacheConfig.java
  7. 28
      src/main/java/com/qs/cost/common/conf/JacksonConfig.java
  8. 40
      src/main/java/com/qs/cost/common/conf/MyBatisConfig.java
  9. 24
      src/main/java/com/qs/cost/common/consts/ApiStatusConst.java
  10. 48
      src/main/java/com/qs/cost/common/consts/ApiUrlConst.java
  11. 17
      src/main/java/com/qs/cost/common/dto/BeanParams.java
  12. 30
      src/main/java/com/qs/cost/common/dto/HttpResult.java
  13. 45
      src/main/java/com/qs/cost/common/dto/PageVo.java
  14. 105
      src/main/java/com/qs/cost/common/dto/R.java
  15. 44
      src/main/java/com/qs/cost/common/dto/U8APIBo.java
  16. 32
      src/main/java/com/qs/cost/common/enums/HttpCode.java
  17. 17
      src/main/java/com/qs/cost/common/framework/exception/BaseException.java
  18. 27
      src/main/java/com/qs/cost/common/framework/exception/BusinessException.java
  19. 90
      src/main/java/com/qs/cost/common/framework/exception/UnifiedExceptionHandler.java
  20. 31
      src/main/java/com/qs/cost/common/framework/interceptor/ForbiddenInterceptor.java
  21. 21
      src/main/java/com/qs/cost/common/framework/mvc/CustomizationBean.java
  22. 160
      src/main/java/com/qs/cost/common/framework/mvc/HttpServletRequestFilter.java
  23. 42
      src/main/java/com/qs/cost/common/framework/mvc/SecurityMvcComponent.java
  24. 20
      src/main/java/com/qs/cost/common/utils/Assert.java
  25. 93
      src/main/java/com/qs/cost/common/utils/BeanCopierUtil.java
  26. 131
      src/main/java/com/qs/cost/common/utils/CollectionUtil.java
  27. 173
      src/main/java/com/qs/cost/common/utils/DateUtil.java
  28. 479
      src/main/java/com/qs/cost/common/utils/HttpService.java
  29. 121
      src/main/java/com/qs/cost/common/utils/HttpUtil.java
  30. 16
      src/main/java/com/qs/cost/common/utils/IdUtil.java
  31. 155
      src/main/java/com/qs/cost/common/utils/IdWorker.java
  32. 83
      src/main/java/com/qs/cost/common/utils/JsonUtil.java
  33. 47
      src/main/java/com/qs/cost/common/utils/SecureUtil.java
  34. 123
      src/main/java/com/qs/cost/common/utils/ServletUtil.java
  35. 132
      src/main/java/com/qs/cost/common/utils/StringCreateUtil.java
  36. 20
      src/main/java/com/qs/cost/common/utils/StringUtil.java
  37. 126
      src/main/java/com/qs/cost/common/utils/model/DesUtils.java
  38. 138
      src/main/java/com/qs/cost/module/controller/ApiController.java
  39. 54
      src/main/java/com/qs/cost/module/domain/ApiBook.java
  40. 97
      src/main/java/com/qs/cost/module/domain/ApiRequest.java
  41. 26
      src/main/java/com/qs/cost/module/domain/dto/JobCallbackParam.java
  42. 22
      src/main/java/com/qs/cost/module/domain/dto/JobDataBaseParam.java
  43. 16
      src/main/java/com/qs/cost/module/domain/dto/JobErpParam.java
  44. 50
      src/main/java/com/qs/cost/module/domain/dto/JobRequestParam.java
  45. 13
      src/main/java/com/qs/cost/module/mapper/ApiBookMapper.java
  46. 13
      src/main/java/com/qs/cost/module/mapper/ApiRequestMapper.java
  47. 99
      src/main/java/com/qs/cost/module/service/ApiService.java
  48. 25
      src/main/resources/application-dev.yml
  49. 3
      src/main/resources/application.yml
  50. 51
      src/main/resources/logback-spring.xml
  51. 78
      src/test/java/boot/PrintDemo.java
  52. 26
      src/test/java/http/HttpTest.http

2
.gitignore

@ -0,0 +1,2 @@
.idea/
jsl-cost-fast.iml

36
README.en.md

@ -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/)

39
README.md

@ -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/)

122
pom.xml

@ -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>

42
src/main/java/com/qs/cost/Application.java

@ -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"));
}
}

19
src/main/java/com/qs/cost/common/conf/CacheConfig.java

@ -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;
}
}

28
src/main/java/com/qs/cost/common/conf/JacksonConfig.java

@ -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;
}
}

40
src/main/java/com/qs/cost/common/conf/MyBatisConfig.java

@ -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;
}
}

24
src/main/java/com/qs/cost/common/consts/ApiStatusConst.java

@ -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;
}

48
src/main/java/com/qs/cost/common/consts/ApiUrlConst.java

@ -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";
}

17
src/main/java/com/qs/cost/common/dto/BeanParams.java

@ -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;
}

30
src/main/java/com/qs/cost/common/dto/HttpResult.java

@ -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;
}

45
src/main/java/com/qs/cost/common/dto/PageVo.java

@ -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);
}
}

105
src/main/java/com/qs/cost/common/dto/R.java

@ -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);
}
}

44
src/main/java/com/qs/cost/common/dto/U8APIBo.java

@ -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;
}

32
src/main/java/com/qs/cost/common/enums/HttpCode.java

@ -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;
}

17
src/main/java/com/qs/cost/common/framework/exception/BaseException.java

@ -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();
}

27
src/main/java/com/qs/cost/common/framework/exception/BusinessException.java

@ -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;
}
}

90
src/main/java/com/qs/cost/common/framework/exception/UnifiedExceptionHandler.java

@ -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();
}
}

31
src/main/java/com/qs/cost/common/framework/interceptor/ForbiddenInterceptor.java

@ -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;
}
}

21
src/main/java/com/qs/cost/common/framework/mvc/CustomizationBean.java

@ -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);
});
}
}

160
src/main/java/com/qs/cost/common/framework/mvc/HttpServletRequestFilter.java

@ -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();
}
};
}
}
}

42
src/main/java/com/qs/cost/common/framework/mvc/SecurityMvcComponent.java

@ -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);
}
}

20
src/main/java/com/qs/cost/common/utils/Assert.java

@ -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());
}
}

93
src/main/java/com/qs/cost/common/utils/BeanCopierUtil.java

@ -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;
}
}

131
src/main/java/com/qs/cost/common/utils/CollectionUtil.java

@ -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;
}
}

173
src/main/java/com/qs/cost/common/utils/DateUtil.java

@ -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;
}
}

479
src/main/java/com/qs/cost/common/utils/HttpService.java

@ -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 请求HTTPSK-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 请求HTTPSJSON形式
*
* @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;
}
}

121
src/main/java/com/qs/cost/common/utils/HttpUtil.java

@ -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;
}
}

16
src/main/java/com/qs/cost/common/utils/IdUtil.java

@ -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;
}
}

155
src/main/java/com/qs/cost/common/utils/IdWorker.java

@ -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;
}
}

83
src/main/java/com/qs/cost/common/utils/JsonUtil.java

@ -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;
}
}

47
src/main/java/com/qs/cost/common/utils/SecureUtil.java

@ -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;
}
}

123
src/main/java/com/qs/cost/common/utils/ServletUtil.java

@ -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();
}
}

132
src/main/java/com/qs/cost/common/utils/StringCreateUtil.java

@ -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);
}
}

20
src/main/java/com/qs/cost/common/utils/StringUtil.java

@ -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;
}
}

126
src/main/java/com/qs/cost/common/utils/model/DesUtils.java

@ -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();
}
}
}

138
src/main/java/com/qs/cost/module/controller/ApiController.java

@ -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();
}
}

54
src/main/java/com/qs/cost/module/domain/ApiBook.java

@ -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;
}

97
src/main/java/com/qs/cost/module/domain/ApiRequest.java

@ -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;
}

26
src/main/java/com/qs/cost/module/domain/dto/JobCallbackParam.java

@ -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;
}

22
src/main/java/com/qs/cost/module/domain/dto/JobDataBaseParam.java

@ -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;
}

16
src/main/java/com/qs/cost/module/domain/dto/JobErpParam.java

@ -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;
}

50
src/main/java/com/qs/cost/module/domain/dto/JobRequestParam.java

@ -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;
}

13
src/main/java/com/qs/cost/module/mapper/ApiBookMapper.java

@ -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> {
}

13
src/main/java/com/qs/cost/module/mapper/ApiRequestMapper.java

@ -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> {
}

99
src/main/java/com/qs/cost/module/service/ApiService.java

@ -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;
}
}

25
src/main/resources/application-dev.yml

@ -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

3
src/main/resources/application.yml

@ -0,0 +1,3 @@
spring:
profiles:
active: dev

51
src/main/resources/logback-spring.xml

@ -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>

78
src/test/java/boot/PrintDemo.java

@ -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;
}
}

26
src/test/java/http/HttpTest.http

@ -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…
Cancel
Save