最新版宁夏武警公众号项目后端
This commit is contained in:
23
src/main/java/com/guahao/common/Exception/BadException.java
Normal file
23
src/main/java/com/guahao/common/Exception/BadException.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.guahao.common.Exception;
|
||||
|
||||
|
||||
import com.guahao.common.response.ResultCodeEnum;
|
||||
|
||||
/**
|
||||
* @author Mr.zs
|
||||
* @date 2024/8/2
|
||||
*/
|
||||
public class BadException extends RuntimeException{
|
||||
private Integer code;
|
||||
|
||||
public BadException(ResultCodeEnum resultCodeEnum) {
|
||||
// 调用父类的方法添加信息
|
||||
super(resultCodeEnum.getMessage());
|
||||
this.code = resultCodeEnum.getCode();
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
113
src/main/java/com/guahao/common/Exception/CustomException.java
Normal file
113
src/main/java/com/guahao/common/Exception/CustomException.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.guahao.common.Exception;
|
||||
|
||||
/**
|
||||
* @description: 自定义运行时异常,抛出不符合业务需求的结果异常
|
||||
* @author zmc
|
||||
*/
|
||||
|
||||
|
||||
public class CustomException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 消息是否为属性文件中的Key
|
||||
*/
|
||||
private boolean propertiesKey = true;
|
||||
|
||||
/**
|
||||
* 构造一个基本异常.
|
||||
*
|
||||
* @param message 信息描述
|
||||
*/
|
||||
public CustomException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个基本异常.
|
||||
*
|
||||
* @param errorCode 错误编码
|
||||
* @param message 信息描述
|
||||
*/
|
||||
public CustomException(String errorCode, String message)
|
||||
{
|
||||
this(errorCode, message, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个基本异常.
|
||||
*
|
||||
* @param errorCode 错误编码
|
||||
* @param message 信息描述
|
||||
*/
|
||||
public CustomException(String errorCode, String message, Throwable cause)
|
||||
{
|
||||
this(errorCode, message, cause, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个基本异常.
|
||||
*
|
||||
* @param errorCode 错误编码
|
||||
* @param message 信息描述
|
||||
* @param propertiesKey 消息是否为属性文件中的Key
|
||||
*/
|
||||
public CustomException(String errorCode, String message, boolean propertiesKey)
|
||||
{
|
||||
super(message);
|
||||
this.setErrorCode(errorCode);
|
||||
this.setPropertiesKey(propertiesKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个基本异常.
|
||||
*
|
||||
* @param errorCode 错误编码
|
||||
* @param message 信息描述
|
||||
*/
|
||||
public CustomException(String errorCode, String message, Throwable cause, boolean propertiesKey)
|
||||
{
|
||||
super(message, cause);
|
||||
this.setErrorCode(errorCode);
|
||||
this.setPropertiesKey(propertiesKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个基本异常.
|
||||
*
|
||||
* @param message 信息描述
|
||||
* @param cause 根异常类(可以存入任何异常)
|
||||
*/
|
||||
public CustomException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public String getErrorCode()
|
||||
{
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(String errorCode)
|
||||
{
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public boolean isPropertiesKey()
|
||||
{
|
||||
return propertiesKey;
|
||||
}
|
||||
|
||||
public void setPropertiesKey(boolean propertiesKey)
|
||||
{
|
||||
this.propertiesKey = propertiesKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.guahao.common.Exception;
|
||||
|
||||
public class LogicException extends RuntimeException {
|
||||
private static final long serialVersionUID = 6482296763929242398L;
|
||||
public LogicException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.guahao.common.Exception;
|
||||
|
||||
public class PermissionException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1037777380808266509L;
|
||||
|
||||
public PermissionException(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
12
src/main/java/com/guahao/common/base/BaseMapper.java
Normal file
12
src/main/java/com/guahao/common/base/BaseMapper.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.guahao.common.base;
|
||||
|
||||
import tk.mybatis.mapper.common.Mapper;
|
||||
import tk.mybatis.mapper.common.MySqlMapper;
|
||||
|
||||
/**
|
||||
* 通用mapper,被其他mapper所继承
|
||||
* @description:
|
||||
* @author:王艳
|
||||
*/
|
||||
public interface BaseMapper<T> extends Mapper<T>, MySqlMapper<T> {
|
||||
}
|
||||
95
src/main/java/com/guahao/common/base/BaseService.java
Normal file
95
src/main/java/com/guahao/common/base/BaseService.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package com.guahao.common.base;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
//import com.guahao.api.satisfaction.model.Questions;
|
||||
import com.guahao.api.satisfaction.model.Questionnaire;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tk.mybatis.mapper.entity.Example;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用service,被其他service所继承
|
||||
*
|
||||
* @description:
|
||||
* @author:王艳
|
||||
*/
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public abstract class BaseService<T> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public abstract BaseMapper getMapper();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void insertList(List<T> recordList) {
|
||||
getMapper().insertList(recordList);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void insertUseGeneratedKeys(T record) throws Exception {
|
||||
getMapper().insertUseGeneratedKeys(record);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void insertSelective(T t) throws Exception {
|
||||
getMapper().insertSelective(t);
|
||||
}
|
||||
|
||||
public void deleteByExample(Example example) throws Exception {
|
||||
getMapper().deleteByExample(example);
|
||||
}
|
||||
|
||||
public void deleteByPrimaryKey(Object key) throws Exception {
|
||||
getMapper().deleteByPrimaryKey(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void updateByPrimaryKeySelective(T record) throws Exception {
|
||||
getMapper().updateByPrimaryKeySelective(record);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void updateByExampleSelective(T record, Example example) throws Exception {
|
||||
getMapper().updateByExampleSelective(record, example);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void updateByExample(T record, Example example) throws Exception {
|
||||
getMapper().updateByExample(record, example);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void updateByPrimaryKey(T t) throws Exception {
|
||||
getMapper().updateByPrimaryKey(t);
|
||||
}
|
||||
|
||||
public int selectCountByExample(Example example) throws Exception {
|
||||
return getMapper().selectCountByExample(example);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public int selectCount(T record) throws Exception {
|
||||
return getMapper().selectCount(record);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<T> selectByExample(Example example) throws Exception {
|
||||
return getMapper().selectByExample(example);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public T selectByPrimaryKey(Object key) throws Exception {
|
||||
return (T) getMapper().selectByPrimaryKey(key);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<T> selectAll() throws Exception {
|
||||
return getMapper().selectAll();
|
||||
}
|
||||
public List<T> getPageList(Example example, int currentPage, int perPageTotal) throws Exception{
|
||||
PageHelper.startPage(currentPage, perPageTotal);
|
||||
return getMapper().selectByExample(example);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
135
src/main/java/com/guahao/common/base/PageBean.java
Normal file
135
src/main/java/com/guahao/common/base/PageBean.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package com.guahao.common.base;
|
||||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.guahao.common.response.ErrorCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PageBean<T> {
|
||||
private int code; // 返回状态码
|
||||
private String message; // 返回消息
|
||||
private long totalRecords; // 总记录数
|
||||
private int totalPages; // 总页数
|
||||
private int pageIndex; // 第几页
|
||||
private int pageSize; // 页面大小
|
||||
private List<T> data; // 结果集
|
||||
private int currentPage;
|
||||
private int perPageTotal;
|
||||
|
||||
public PageBean() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装Page对象,因为直接返回Page对象,在JSON处理以及 其他情况下会被当成List来处理而出现一些问题。
|
||||
*
|
||||
* @param list
|
||||
* page结果
|
||||
*/
|
||||
public PageBean(int code, String msg, List<T> list) {
|
||||
this.code = code;
|
||||
this.message = msg;
|
||||
if (list instanceof Page) {
|
||||
Page<T> page = (Page<T>) list;
|
||||
this.data = page;
|
||||
this.totalRecords = page.getTotal();
|
||||
this.totalPages = page.getPages();
|
||||
this.pageIndex = page.getPageNum();
|
||||
this.pageSize = page.getPageSize();
|
||||
}
|
||||
}
|
||||
|
||||
public PageBean(List<T> list) {
|
||||
this.code = ErrorCode.SUCCESS.code;
|
||||
this.message = ErrorCode.SUCCESS.desc;
|
||||
if (list instanceof Page) {
|
||||
Page<T> page = (Page<T>) list;
|
||||
this.data = page;
|
||||
this.totalRecords = page.getTotal();
|
||||
this.totalPages = page.getPages();
|
||||
this.pageIndex = page.getPageNum();
|
||||
this.pageSize = page.getPageSize();
|
||||
}
|
||||
}
|
||||
|
||||
public PageBean(List<T> list,ErrorCode errorCode) {
|
||||
this.code = errorCode.code;
|
||||
this.message = errorCode.desc;
|
||||
if (list instanceof Page) {
|
||||
Page<T> page = (Page<T>) list;
|
||||
this.data = page;
|
||||
this.totalRecords = page.getTotal();
|
||||
this.totalPages = page.getPages();
|
||||
this.pageIndex = page.getPageNum();
|
||||
this.pageSize = page.getPageSize();
|
||||
}
|
||||
}
|
||||
|
||||
public PageBean(int code, String msg, List<T> list, int pageIndex, int pageSize) {
|
||||
this.code = code;
|
||||
this.message = msg;
|
||||
this.data = list;
|
||||
this.totalRecords = (list==null?0:list.size()) ;
|
||||
this.totalPages = (int) Math.ceil(Double.valueOf(totalRecords)/pageSize);
|
||||
this.pageIndex = pageIndex;
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public List<T> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(List<T> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public long getTotalRecords() {
|
||||
return totalRecords;
|
||||
}
|
||||
|
||||
public void setTotalRecords(long totalRecords) {
|
||||
this.totalRecords = totalRecords;
|
||||
}
|
||||
|
||||
public int getTotalPages() {
|
||||
return totalPages;
|
||||
}
|
||||
|
||||
public void setTotalPages(int totalPages) {
|
||||
this.totalPages = totalPages;
|
||||
}
|
||||
|
||||
public int getPageIndex() {
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
public void setPageIndex(int pageIndex) {
|
||||
this.pageIndex = pageIndex;
|
||||
}
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
}
|
||||
39
src/main/java/com/guahao/common/base/PageParams.java
Normal file
39
src/main/java/com/guahao/common/base/PageParams.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.guahao.common.base;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@ApiModel("分页条件")
|
||||
public abstract class PageParams implements Serializable {
|
||||
@ApiModelProperty(value = "当前页码" ,required = true)
|
||||
protected Integer currentPage;
|
||||
@ApiModelProperty(value = "页面大小" ,required = true)
|
||||
protected Integer pageSize;
|
||||
|
||||
public void startPage(){
|
||||
PageHelper.startPage(currentPage,pageSize);
|
||||
}
|
||||
|
||||
public void orderBy(String asc){
|
||||
PageHelper.orderBy(asc);
|
||||
}
|
||||
|
||||
public Integer getCurrentPage() {
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
public void setCurrentPage(Integer currentPage) {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
|
||||
public Integer getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(Integer pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/guahao/common/base/UUIdGenId.java
Normal file
18
src/main/java/com/guahao/common/base/UUIdGenId.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.guahao.common.base;
|
||||
|
||||
import tk.mybatis.mapper.genid.GenId;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author xiaobu
|
||||
* @version JDK1.8.0_171
|
||||
* @date on 2019/3/27 11:37
|
||||
* @description V1.0
|
||||
*/
|
||||
public class UUIdGenId implements GenId<String> {
|
||||
@Override
|
||||
public String genId(String s, String s1) {
|
||||
return UUID.randomUUID().toString().replace("-","");
|
||||
}
|
||||
}
|
||||
29
src/main/java/com/guahao/common/config/WxPayConfig.java
Normal file
29
src/main/java/com/guahao/common/config/WxPayConfig.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.guahao.common.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "wx.pay")
|
||||
public class WxPayConfig {
|
||||
private String mchId;
|
||||
private String apiKey;
|
||||
private String appId;
|
||||
private String secret;
|
||||
private String certPath;
|
||||
|
||||
// getter和setter方法
|
||||
public String getMchId() { return mchId; }
|
||||
public void setMchId(String mchId) { this.mchId = mchId; }
|
||||
public String getApiKey() { return apiKey; }
|
||||
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
|
||||
public String getAppId() { return appId; }
|
||||
public void setAppId(String appId) { this.appId = appId; }
|
||||
public String getSecret() { return secret; }
|
||||
public void setSecret(String secret) { this.secret = secret; }
|
||||
|
||||
public String getCertPath() {return certPath;}
|
||||
|
||||
public void setCertPath(String certPath) {this.certPath = certPath;}
|
||||
|
||||
}
|
||||
58
src/main/java/com/guahao/common/filter/FilterCors.java
Normal file
58
src/main/java/com/guahao/common/filter/FilterCors.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.guahao.common.filter;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lxd
|
||||
* @Title: 支持跨域访问
|
||||
* @Package
|
||||
* @Description:
|
||||
* @date 2020/5/22 14:46
|
||||
*/
|
||||
@WebFilter(filterName = "Filter_CORS",urlPatterns = "/*")
|
||||
public class FilterCors implements Filter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletResponse res = (HttpServletResponse) response;
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
|
||||
// 不使用*,自动适配跨域域名,避免携带Cookie时失效
|
||||
String origin = req.getHeader("Origin");
|
||||
if (StringUtils.isNotBlank(origin)) {
|
||||
// System.out.println("经过过滤器这里1");
|
||||
res.setHeader("Access-Control-Allow-Origin", origin);
|
||||
}
|
||||
// System.out.println("经过过滤器这里2");
|
||||
|
||||
// 自适应所有自定义头
|
||||
String headers = req.getHeader("Access-Control-Request-Headers");
|
||||
if (StringUtils.isNotBlank(headers)) {
|
||||
res.setHeader("Access-Control-Allow-Headers", headers);
|
||||
res.setHeader("Access-Control-Expose-Headers", headers);
|
||||
}
|
||||
|
||||
// 允许跨域的请求方法类型
|
||||
res.setHeader("Access-Control-Allow-Methods", "*");
|
||||
// 预检命令(OPTIONS)缓存时间,单位:秒
|
||||
res.setHeader("Access-Control-Max-Age", "3600");
|
||||
// 明确许可客户端发送Cookie,不允许删除字段即可
|
||||
res.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
33
src/main/java/com/guahao/common/filter/FilterEncoding.java
Normal file
33
src/main/java/com/guahao/common/filter/FilterEncoding.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.guahao.common.filter;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* 编码过滤
|
||||
* @author lxd
|
||||
*/
|
||||
@WebFilter(filterName = "Filter_Encoding",urlPatterns = "/*")
|
||||
public class FilterEncoding implements Filter {
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
chain.doFilter(request , response);
|
||||
}
|
||||
@Override
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
|
||||
}
|
||||
}
|
||||
125
src/main/java/com/guahao/common/response/ErrorCode.java
Normal file
125
src/main/java/com/guahao/common/response/ErrorCode.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package com.guahao.common.response;
|
||||
|
||||
/**
|
||||
* 错误码定义
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
public enum ErrorCode {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS(200, "执行成功"),
|
||||
/**
|
||||
* 刷新token
|
||||
*/
|
||||
REFRESH_TOKEN(99999, "刷新token"),
|
||||
/**
|
||||
* 参数不合法
|
||||
*/
|
||||
NOTNULL_PARAMS(100001, "参数不合法:"),
|
||||
/**
|
||||
* 用户名或密码错误
|
||||
*/
|
||||
ERROR_USERORPASSWORD(100002, "用户名或密码错误"),
|
||||
/**
|
||||
* 用户已停用
|
||||
*/
|
||||
NOTVALID_USER(100012, "该用户已停用"),
|
||||
/**
|
||||
* 无此用户信息
|
||||
*/
|
||||
NO_USER(100013, "未查询到患者信息"),
|
||||
/**
|
||||
* 名称错误
|
||||
*/
|
||||
ERROR_NAME(100014, "名称与身份证不匹配,请重新输入"),
|
||||
/**
|
||||
* 手机号不匹配
|
||||
*/
|
||||
ERROR_PHONE(100015, "手机号不匹配,请到窗口解决"),
|
||||
/**
|
||||
* 修改手机号失败
|
||||
*/
|
||||
ERROR_UPDATE_PHONE(100016, "修改手机号失败"),
|
||||
/**
|
||||
* 用户名不能为空
|
||||
*/
|
||||
ISNULL_USERNAME(100003, "用户名不能为空"),
|
||||
/**
|
||||
* 密码不能为空
|
||||
*/
|
||||
ISNULL_PASSWORD(100004, "密码不能为空"),
|
||||
/**
|
||||
* 逻辑错误
|
||||
*/
|
||||
ERROR_LOGIC(100005, ""),
|
||||
/**
|
||||
* 文件上传失败
|
||||
*/
|
||||
ERROR_FILE_UPLOAD(100006, "文件上传失败:"),
|
||||
/**
|
||||
* 文件下载失败
|
||||
*/
|
||||
ERROR_FILE_DOWNLOAD(100007, "文件下载失败:"),
|
||||
/**
|
||||
* 用户无此权限
|
||||
*/
|
||||
NOTE_OWN_AUTH(100008, "用户无此权限:"),
|
||||
/**
|
||||
* 订阅失败
|
||||
*/
|
||||
FAILED_SUBSCRIPTION_ADD(100009, "订阅失败"),
|
||||
/**
|
||||
* token失效
|
||||
*/
|
||||
INVALID_TOKEN(110001, "登录失效,请重新登录"),
|
||||
/**
|
||||
* token过期
|
||||
*/
|
||||
TIMEOUT_TOKEN(110011, "登录过期,请重新登录"),
|
||||
/**
|
||||
* token非法
|
||||
*/
|
||||
ERROR_TOKEN(110021, "登录非法"),
|
||||
/**
|
||||
* 鉴权错误
|
||||
*/
|
||||
INVALID_AUTHOR(110002, "鉴权错误"),
|
||||
|
||||
/**
|
||||
* 鉴权错误
|
||||
*/
|
||||
EXPIRES_AUTHOR(110003, "授权过期"),
|
||||
/**
|
||||
* 登录超时,请重新登录
|
||||
*/
|
||||
LOGIN_OUTTIME(110004, "登录超时,请重新登录"),
|
||||
/**
|
||||
* 只支持查询半年内的报告
|
||||
*/
|
||||
TIME_OVERDUE(110005, "只支持查询半年内的报告"),
|
||||
/**
|
||||
* 系统异常
|
||||
*/
|
||||
EXCEPTION_SYS(120001, "系统异常--"),
|
||||
|
||||
EXCEPTION_SKY(120002, "");
|
||||
|
||||
|
||||
public int code;
|
||||
public String desc;
|
||||
|
||||
private ErrorCode(int code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public boolean equal(ErrorCode other) {
|
||||
if (this.code == other.code) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.guahao.common.response;
|
||||
/**
|
||||
* http返回值状态码定义
|
||||
* @author lxd
|
||||
*
|
||||
*/
|
||||
public enum HttpResponseStatus {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS(200,"成功"),
|
||||
ERROR_CHECK(400,"验证错误"),
|
||||
ERROR_AUTHOR(403,"鉴权错误"),
|
||||
ERROR_SYS(500,"系统内部错误"),
|
||||
OTHER(900,"其他");
|
||||
private int code;
|
||||
private String desc;
|
||||
|
||||
private HttpResponseStatus(int code,String desc){
|
||||
this.code = code ;
|
||||
this.desc =desc;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public static HttpResponseStatus getResponseStatus(int errorCode){
|
||||
if(errorCode==100000 || errorCode==99999){
|
||||
return SUCCESS;
|
||||
}
|
||||
if(errorCode>100000 && errorCode<=110000){
|
||||
return ERROR_CHECK;
|
||||
}
|
||||
if(errorCode>110000 && errorCode<=120000){
|
||||
return ERROR_AUTHOR;
|
||||
}
|
||||
if(errorCode>120000 && errorCode<=130000){
|
||||
return ERROR_SYS;
|
||||
}
|
||||
return OTHER;
|
||||
}
|
||||
}
|
||||
235
src/main/java/com/guahao/common/response/ResponseResult.java
Normal file
235
src/main/java/com/guahao/common/response/ResponseResult.java
Normal file
@@ -0,0 +1,235 @@
|
||||
package com.guahao.common.response;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 自定义响应返回值
|
||||
*
|
||||
* @ClassName: ResponseResult
|
||||
* @Description:TODO(响应返回值)
|
||||
* @author: lxd
|
||||
* @date: 2019年9月10日 下午5:01:33
|
||||
*/
|
||||
public class ResponseResult<T> implements Serializable {
|
||||
/**
|
||||
* @Fields serialVersionUID : TODO(用一句话描述这个变量表示什么)
|
||||
*/
|
||||
private static final long serialVersionUID = -4461307964012001505L;
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
private String refreshToken;
|
||||
private T data;
|
||||
|
||||
|
||||
private ResponseResult() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功后的响应码和响应描述
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult success() {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=ErrorCode.SUCCESS.code;
|
||||
responseResult.message=ErrorCode.SUCCESS.desc;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功后的响应码和响应描述以及数据
|
||||
*
|
||||
* @param Object data 数据
|
||||
* @return ResponseResult
|
||||
*/
|
||||
public static ResponseResult success(Object data) {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=ErrorCode.SUCCESS.code;
|
||||
responseResult.message=ErrorCode.SUCCESS.desc;
|
||||
responseResult.data=data;
|
||||
return responseResult;
|
||||
}
|
||||
/**
|
||||
* 返回成功后的响应码和响应描述以及数据
|
||||
*
|
||||
* @param refreshToken 验证信息
|
||||
* @param data 数据
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult success(String refreshToken,Object data) {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code = ErrorCode.SUCCESS.code;
|
||||
responseResult.message = ErrorCode.SUCCESS.desc;
|
||||
responseResult.refreshToken = refreshToken;
|
||||
responseResult.data=data;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
public static ResponseResult success(String message,String refreshToken,Object data) {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code = ErrorCode.SUCCESS.code;
|
||||
responseResult.message = message;
|
||||
responseResult.refreshToken = refreshToken;
|
||||
responseResult.data=data;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回http响应
|
||||
*
|
||||
* @param errorCode 错误码
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult failure(ErrorCode errorCode) {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=errorCode.code;
|
||||
responseResult.message=errorCode.desc;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回http响应
|
||||
*
|
||||
* @param errorCode 错误码
|
||||
* @param data 响应数据
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult failure(ErrorCode errorCode, Object data) {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=errorCode.code;
|
||||
responseResult.message=errorCode.desc;
|
||||
responseResult.data=data;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回默认系统异常响应
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult sysError() {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=ErrorCode.EXCEPTION_SYS.code;
|
||||
responseResult.message=ErrorCode.EXCEPTION_SYS.desc;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回系统异常响应
|
||||
*
|
||||
* @param exception 系统异常描述
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult sysError(String exception) {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=ErrorCode.EXCEPTION_SYS.code;
|
||||
responseResult.message = exception;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回 token 过期异常响应
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult sysLoginError() {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=ErrorCode.TIMEOUT_TOKEN.code;
|
||||
responseResult.message = ErrorCode.TIMEOUT_TOKEN.desc;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 token 注入异常响应
|
||||
* @return
|
||||
*/
|
||||
public static ResponseResult sysTokenError() {
|
||||
ResponseResult responseResult = new ResponseResult();
|
||||
responseResult.code=ErrorCode.INVALID_TOKEN.code;
|
||||
responseResult.message = ErrorCode.INVALID_TOKEN.desc;
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向http响应添加响应数据
|
||||
*
|
||||
* @param data 响应返回数据
|
||||
* @return
|
||||
*/
|
||||
public ResponseResult addData(Object data) {
|
||||
this.setData((T)data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseResult addRefreshToken(String refreshToken) {
|
||||
this.setRefreshToken(refreshToken);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 向http响应添加总数量
|
||||
*
|
||||
* @param total 总数量
|
||||
* @return
|
||||
*/
|
||||
/*public ResponseResult addTotal(int total) {
|
||||
this.setTotal(total);
|
||||
return this;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 追加响应错误描述信息
|
||||
*
|
||||
* @param message 响应描述信息
|
||||
* @return
|
||||
*/
|
||||
public ResponseResult appendMessage(String message) {
|
||||
this.message=(this.message == null ? "" : this.message.toString()) + message;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this, SerializerFeature.WriteMapNullValue);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
57
src/main/java/com/guahao/common/response/ResultCodeEnum.java
Normal file
57
src/main/java/com/guahao/common/response/ResultCodeEnum.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.guahao.common.response;
|
||||
|
||||
/**
|
||||
* @author Mr.zs
|
||||
* @date 2024/8/2
|
||||
*/
|
||||
public enum ResultCodeEnum {
|
||||
SUCCESS(true, 20000, "成功"),
|
||||
|
||||
UNKNOWN_REASON(false, 20001, "未知错误"),
|
||||
|
||||
UNLOGIN(false, 20002, "认证信息失败!"),
|
||||
|
||||
TOKEN_ERROR(false, 20003, "令牌不合法"),
|
||||
|
||||
HIS_RESPONSE_ERR(false, 20004, "院内响应失败"),
|
||||
|
||||
REQUEST_ERROR(false, 40001, "请求失败"),
|
||||
|
||||
HIS_NULL_OBJECT(false, 20005, "his空对象"),
|
||||
|
||||
YB_1101_ERROR(false, 30001, "医保响应失败"),
|
||||
|
||||
YB_LB_ERROR(false, 30002, "医保类型不满足"),
|
||||
|
||||
YB_ERROR(false, 30003, "医保响应失败");
|
||||
|
||||
|
||||
private final Boolean success;
|
||||
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
ResultCodeEnum(Boolean success, Integer code, String message) {
|
||||
this.success = success;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResultCodeEnum{" + "success=" + success + ", code=" + code + ", message='" + message + '\'' + '}';
|
||||
}
|
||||
}
|
||||
71
src/main/java/com/guahao/common/util/AmountUtil.java
Normal file
71
src/main/java/com/guahao/common/util/AmountUtil.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 金额处理工具类
|
||||
*/
|
||||
public final class AmountUtil {
|
||||
|
||||
private AmountUtil() {
|
||||
throw new AssertionError("No instances for utility class");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串金额转换为 BigDecimal
|
||||
* 支持格式: "123", "123.45", " 123.45 ", "-123.45", "+123.45"
|
||||
*
|
||||
* @param amountStr 金额字符串
|
||||
* @return BigDecimal 对象,如果输入为 null 或无效格式,返回 BigDecimal.ZERO
|
||||
*/
|
||||
public static BigDecimal parseAmount(String amountStr) {
|
||||
return parseAmount(amountStr, BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串金额转换为 BigDecimal,支持指定默认值
|
||||
*
|
||||
* @param amountStr 金额字符串
|
||||
* @param defaultValue 解析失败时返回的默认值
|
||||
* @return BigDecimal 对象
|
||||
*/
|
||||
public static BigDecimal parseAmount(String amountStr, BigDecimal defaultValue) {
|
||||
if (amountStr == null || amountStr.trim().isEmpty()) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
return new BigDecimal(amountStr.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("金额格式错误: " + amountStr);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串金额转换为 BigDecimal,并指定小数位数和舍入模式
|
||||
*
|
||||
* @param amountStr 金额字符串
|
||||
* @param scale 保留小数位数(如 2 表示保留两位小数)
|
||||
* @param roundingMode 舍入模式,如 RoundingMode.HALF_UP(四舍五入)
|
||||
* @return BigDecimal 对象,格式错误或 null 返回 0.00
|
||||
*/
|
||||
public static BigDecimal parseAmount(String amountStr, int scale, RoundingMode roundingMode) {
|
||||
BigDecimal amount = parseAmount(amountStr, BigDecimal.ZERO);
|
||||
return amount.setScale(scale, roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全比较两个金额字符串大小
|
||||
*
|
||||
* @param a 金额A
|
||||
* @param b 金额B
|
||||
* @return a > b 返回 1, a < b 返回 -1, a == b 返回 0
|
||||
*/
|
||||
public static int compare(String a, String b) {
|
||||
BigDecimal amountA = parseAmount(a, BigDecimal.ZERO);
|
||||
BigDecimal amountB = parseAmount(b, BigDecimal.ZERO);
|
||||
return amountA.compareTo(amountB);
|
||||
}
|
||||
}
|
||||
298
src/main/java/com/guahao/common/util/DateUtils.java
Normal file
298
src/main/java/com/guahao/common/util/DateUtils.java
Normal file
@@ -0,0 +1,298 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName: DateUtils
|
||||
* @Description:
|
||||
* @Author T.W
|
||||
* @Date 2023/3/31
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Component
|
||||
public class DateUtils {
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
// 添加时间格式常量
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss";
|
||||
|
||||
|
||||
public static final String parseDateToStr(final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间加分钟
|
||||
* @param minute
|
||||
* @return
|
||||
*/
|
||||
public static String addMinute(int minute){
|
||||
Calendar nowTime = Calendar.getInstance();
|
||||
nowTime.add(Calendar.MINUTE, minute);
|
||||
return parseDateToStr(YYYYMMDDHHMMSS,nowTime.getTime());
|
||||
}
|
||||
/**
|
||||
* 获取HIS系统时间并格式化为yyyy-MM-dd
|
||||
* @return 格式为"yyyy-MM-dd"的时间字符串
|
||||
*/
|
||||
public static String getHisDate() {
|
||||
try {
|
||||
String times = null;
|
||||
// 封装XML请求
|
||||
String getTimeXML = XmlUtil.getHisTime();
|
||||
// 发送SOAP请求
|
||||
String respGetTimeXml = SoapUtil.soapMethod(getTimeXML);
|
||||
// 解析响应XML
|
||||
Map<String, Object> mapGetTime = XmlUtil.parse(respGetTimeXml);
|
||||
|
||||
if ("1".equals(mapGetTime.get("returncode").toString())) {
|
||||
if (mapGetTime.get("CurrDateTime") != null) {
|
||||
String currDateTime = mapGetTime.get("CurrDateTime").toString();
|
||||
// 解析原始时间格式
|
||||
SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
Date date = originalFormat.parse(currDateTime);
|
||||
// 格式化为yyyy-MM-dd
|
||||
times = parseDateToStr("yyyy-MM-dd", date);
|
||||
}
|
||||
}
|
||||
return times;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取HIS时间
|
||||
* @param
|
||||
* @return “2025/8/22 15:32:15”
|
||||
*/
|
||||
public static String getTimes() {
|
||||
try {
|
||||
String Times = null;
|
||||
// 封装xml
|
||||
String getTimeXML = XmlUtil.getHisTime();
|
||||
// 发送soap请求
|
||||
String respGetTimeXml = null;
|
||||
respGetTimeXml = SoapUtil.soapMethod(getTimeXML);
|
||||
// 解析报文
|
||||
Map<String, Object> mapGetTime = XmlUtil.parse(respGetTimeXml);
|
||||
if (mapGetTime.get("returncode").toString().equals("1")) {
|
||||
if (mapGetTime.get("CurrDateTime") != null) {
|
||||
Times= mapGetTime.get("CurrDateTime").toString();
|
||||
}
|
||||
}
|
||||
return Times;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取HIS系统时间并格式化为 "yyyy年M月d日 HH:mm" 格式
|
||||
* @return 格式为 "2023年9月5日 16:57" 的时间字符串
|
||||
*/
|
||||
public static String getHisDateTimeChineseFormat() {
|
||||
try {
|
||||
String times = null;
|
||||
// 封装XML请求
|
||||
String getTimeXML = XmlUtil.getHisTime();
|
||||
// 发送SOAP请求
|
||||
String respGetTimeXml = SoapUtil.soapMethod(getTimeXML);
|
||||
// 解析响应XML
|
||||
Map<String, Object> mapGetTime = XmlUtil.parse(respGetTimeXml);
|
||||
|
||||
if ("1".equals(mapGetTime.get("returncode").toString())) {
|
||||
if (mapGetTime.get("CurrDateTime") != null) {
|
||||
String currDateTime = mapGetTime.get("CurrDateTime").toString();
|
||||
// 解析原始时间格式
|
||||
SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
Date date = originalFormat.parse(currDateTime);
|
||||
// 格式化为中国式日期时间格式
|
||||
times = parseDateToStr("yyyy年M月d日 HH:mm", date);
|
||||
}
|
||||
}
|
||||
return times;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将"yyyy/MM/dd HH:mm:ss"格式的时间字符串转换为时间戳
|
||||
* @param timeStr 时间字符串,格式为"yyyy/MM/dd HH:mm:ss"
|
||||
* @return 时间戳(秒)
|
||||
*/
|
||||
public static String getTimeStamp(String timeStr) {
|
||||
try {
|
||||
SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
|
||||
Date date = format.parse(timeStr);
|
||||
return String.valueOf(date.getTime() / 1000); // 返回秒级时间戳
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("时间字符串转换失败: " + timeStr, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HIS系统当前时间的时间戳(毫秒级)
|
||||
* @return 毫秒级时间戳,类似于System.currentTimeMillis()
|
||||
*/
|
||||
public static long getHisCurrentTimeMillis() {
|
||||
try {
|
||||
// 获取HIS时间字符串
|
||||
String hisTime = getTimes(); // 格式为"yyyy/MM/dd HH:mm:ss"
|
||||
if (hisTime != null) {
|
||||
// 将HIS时间字符串解析为Date对象
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
Date date = format.parse(hisTime);
|
||||
// 返回毫秒级时间戳
|
||||
return date.getTime();
|
||||
}
|
||||
// 如果获取HIS时间失败,则返回系统当前时间戳
|
||||
return System.currentTimeMillis();
|
||||
} catch (Exception e) {
|
||||
// 发生异常时也返回系统当前时间戳,保证方法可用性
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取HIS时间戳
|
||||
* @return 秒级时间戳
|
||||
*/
|
||||
public static String getTimesStamp() {
|
||||
try {
|
||||
String timeStr = getTimes();
|
||||
if (timeStr != null) {
|
||||
return getTimeStamp(timeStr);
|
||||
}
|
||||
throw new RuntimeException("获取HIS时间失败");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将yyyyMMddHHmmss格式的时间转换为秒级时间戳格式
|
||||
*/
|
||||
public static long checkTimeStamp(String time) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
LocalDateTime dateTime = LocalDateTime.parse(time, formatter);
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
|
||||
}
|
||||
/**
|
||||
* 获取HIS系统时间并格式化为yyyyMMddHHmmss
|
||||
* @return 格式为"yyyyMMddHHmmss"的时间字符串
|
||||
*/
|
||||
public static String getHisDateTime_yyyyMMddHHmmss() {
|
||||
try {
|
||||
String hisTime = getTimes(); // 获取HIS时间,格式为"yyyy/MM/dd HH:mm:ss"
|
||||
if (hisTime != null) {
|
||||
// 将"yyyy/MM/dd HH:mm:ss"格式转换为"yyyyMMddHHmmss"格式
|
||||
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
Date date = inputFormat.parse(hisTime);
|
||||
return outputFormat.format(date);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取添加分钟后的HIS时间
|
||||
* @param
|
||||
* @return "20250822073335"
|
||||
*/
|
||||
public static String getAddTimes(int minute) {
|
||||
try {
|
||||
String Times = null;
|
||||
// 封装xml
|
||||
String getTimeXML = XmlUtil.getHisTime();
|
||||
// 发送soap请求
|
||||
String respGetTimeXml = null;
|
||||
respGetTimeXml = SoapUtil.soapMethod(getTimeXML);
|
||||
// 解析报文
|
||||
Map<String, Object> mapGetTime = XmlUtil.parse(respGetTimeXml);
|
||||
if (mapGetTime.get("returncode").toString().equals("1")) {
|
||||
if (mapGetTime.get("CurrDateTime") != null) {
|
||||
// 获取原始时间字符串
|
||||
String currDateTime = mapGetTime.get("CurrDateTime").toString();
|
||||
|
||||
// 定义原始时间格式
|
||||
SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
// 将字符串解析为Date对象
|
||||
Date date = originalFormat.parse(currDateTime);
|
||||
|
||||
// 使用Calendar增加15分钟
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.MINUTE, minute);
|
||||
// 格式化为所需的yyyyMMddHHmmss格式
|
||||
Times = parseDateToStr(YYYYMMDDHHMMSS, calendar.getTime());
|
||||
}
|
||||
}
|
||||
return Times;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 获取HIS系统时间并格式化为yyyy-MM-dd HH:mm:ss
|
||||
* @return 格式为"yyyy-MM-dd HH:mm:ss"的时间字符串
|
||||
*/
|
||||
public static String getHisDateTime() {
|
||||
try {
|
||||
String times = null;
|
||||
// 封装XML请求
|
||||
String getTimeXML = XmlUtil.getHisTime();
|
||||
// 发送SOAP请求
|
||||
String respGetTimeXml = SoapUtil.soapMethod(getTimeXML);
|
||||
// 解析响应XML
|
||||
Map<String, Object> mapGetTime = XmlUtil.parse(respGetTimeXml);
|
||||
|
||||
if ("1".equals(mapGetTime.get("returncode").toString())) {
|
||||
if (mapGetTime.get("CurrDateTime") != null) {
|
||||
String currDateTime = mapGetTime.get("CurrDateTime").toString();
|
||||
// 解析原始时间格式
|
||||
SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
Date date = originalFormat.parse(currDateTime);
|
||||
// 格式化为yyyy-MM-dd HH:mm:ss
|
||||
times = parseDateToStr("yyyy-MM-dd HH:mm:ss", date);
|
||||
}
|
||||
}
|
||||
return times;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 拼接起始日期和结束日期
|
||||
* @param startTime
|
||||
* @param endTime
|
||||
* @return
|
||||
*/
|
||||
public static String formatRange(String startTime, String endTime) {
|
||||
// 定义日期解析格式
|
||||
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
try {
|
||||
// 解析日期
|
||||
Date startDate = inputFormat.parse(startTime);
|
||||
|
||||
Date endDate = inputFormat.parse(endTime);
|
||||
|
||||
// 定义输出格式
|
||||
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMdd");
|
||||
|
||||
// 拼接结果
|
||||
return outputFormat.format(startDate) + "-" + outputFormat.format(endDate);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
139
src/main/java/com/guahao/common/util/EscapeUtils.java
Normal file
139
src/main/java/com/guahao/common/util/EscapeUtils.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
/**
|
||||
* @ClassName: EscapeUtils
|
||||
* @Description:
|
||||
* @Author T.W
|
||||
* @Date 2023/3/31
|
||||
* @Version 1.0
|
||||
*/
|
||||
public class EscapeUtils {
|
||||
private final static String[] hex = { "00", "01", "02", "03", "04", "05",
|
||||
"06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", "10",
|
||||
"11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B",
|
||||
"1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26",
|
||||
"27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31",
|
||||
"32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C",
|
||||
"3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
|
||||
"48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52",
|
||||
"53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D",
|
||||
"5E", "5F", "60", "61", "62", "63", "64", "65", "66", "67", "68",
|
||||
"69", "6A", "6B", "6C", "6D", "6E", "6F", "70", "71", "72", "73",
|
||||
"74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E",
|
||||
"7F", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
|
||||
"8A", "8B", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94",
|
||||
"95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
|
||||
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA",
|
||||
"AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3", "B4", "B5",
|
||||
"B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", "C0",
|
||||
"C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
|
||||
"CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6",
|
||||
"D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1",
|
||||
"E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC",
|
||||
"ED", "EE", "EF", "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
|
||||
"F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" };
|
||||
|
||||
private final static byte[] val = { 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F };
|
||||
|
||||
/** */
|
||||
/**
|
||||
* 编码
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
public static String escape(String s) {
|
||||
StringBuffer sbuf = new StringBuffer();
|
||||
int len = s.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
int ch = s.charAt(i);
|
||||
if ('A' <= ch && ch <= 'Z') {
|
||||
sbuf.append((char) ch);
|
||||
} else if ('a' <= ch && ch <= 'z') {
|
||||
sbuf.append((char) ch);
|
||||
} else if ('0' <= ch && ch <= '9') {
|
||||
sbuf.append((char) ch);
|
||||
} else if (ch == '-' || ch == '_' || ch == '.' || ch == '!'
|
||||
|| ch == '~' || ch == '*' || ch == '\'' || ch == '('
|
||||
|| ch == ')') {
|
||||
sbuf.append((char) ch);
|
||||
} else if (ch <= 0x007F) {
|
||||
sbuf.append('%');
|
||||
sbuf.append(hex[ch]);
|
||||
} else {
|
||||
sbuf.append('%');
|
||||
sbuf.append('u');
|
||||
sbuf.append(hex[(ch >>> 8)]);
|
||||
sbuf.append(hex[(0x00FF & ch)]);
|
||||
}
|
||||
}
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码 说明:本方法保证 不论参数s是否经过escape()编码,均能得到正确的“解码”结果
|
||||
*
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
public static String unescape(String s) {
|
||||
StringBuffer sbuf = new StringBuffer();
|
||||
int i = 0;
|
||||
int len = s.length();
|
||||
while (i < len) {
|
||||
int ch = s.charAt(i);
|
||||
if ('A' <= ch && ch <= 'Z') {
|
||||
sbuf.append((char) ch);
|
||||
} else if ('a' <= ch && ch <= 'z') {
|
||||
sbuf.append((char) ch);
|
||||
} else if ('0' <= ch && ch <= '9') {
|
||||
sbuf.append((char) ch);
|
||||
} else if (ch == '-' || ch == '_' || ch == '.' || ch == '!'
|
||||
|| ch == '~' || ch == '*' || ch == '\'' || ch == '('
|
||||
|| ch == ')') {
|
||||
sbuf.append((char) ch);
|
||||
} else if (ch == '%') {
|
||||
int cint = 0;
|
||||
if ('u' != s.charAt(i + 1)) {
|
||||
cint = (cint << 4) | val[s.charAt(i + 1)];
|
||||
cint = (cint << 4) | val[s.charAt(i + 2)];
|
||||
i += 2;
|
||||
} else {
|
||||
cint = (cint << 4) | val[s.charAt(i + 2)];
|
||||
cint = (cint << 4) | val[s.charAt(i + 3)];
|
||||
cint = (cint << 4) | val[s.charAt(i + 4)];
|
||||
cint = (cint << 4) | val[s.charAt(i + 5)];
|
||||
i += 5;
|
||||
}
|
||||
sbuf.append((char) cint);
|
||||
} else {
|
||||
sbuf.append((char) ch);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/com/guahao/common/util/FileUtil.java
Normal file
18
src/main/java/com/guahao/common/util/FileUtil.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
public class FileUtil {
|
||||
|
||||
public static void uploadFile(byte[] file, String filePath, String fileName) throws Exception {
|
||||
File targetFile = new File(filePath);
|
||||
if (!targetFile.exists()) {
|
||||
targetFile.mkdirs();
|
||||
}
|
||||
FileOutputStream out = new FileOutputStream(filePath + fileName);
|
||||
out.write(file);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
348
src/main/java/com/guahao/common/util/FtpUtil.java
Normal file
348
src/main/java/com/guahao/common/util/FtpUtil.java
Normal file
@@ -0,0 +1,348 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
/**
|
||||
* @ClassName: ftpUtil
|
||||
* @Description:
|
||||
* @Author T.W
|
||||
* @Date 2021/10/23
|
||||
* @Version 1.0
|
||||
*/
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class FtpUtil {
|
||||
/**
|
||||
* 日志对象
|
||||
**/
|
||||
private static final Logger logger = LoggerFactory.getLogger(FtpUtil.class);
|
||||
|
||||
/**
|
||||
* 该目录不存在
|
||||
*/
|
||||
public static final String DIR_NOT_EXIST = "该目录不存在";
|
||||
|
||||
/**
|
||||
* "该目录下没有文件
|
||||
*/
|
||||
public static final String DIR_CONTAINS_NO_FILE = "该目录下没有文件";
|
||||
|
||||
/**
|
||||
* FTP地址
|
||||
**/
|
||||
private String ftpAddress = "192.168.150.3";
|
||||
// private String ftpAddress = "127.0.0.1";
|
||||
/**
|
||||
* FTP端口
|
||||
**/
|
||||
// @Value("${ftp.port}")
|
||||
private int ftpPort = 20015;
|
||||
/**
|
||||
* FTP用户名
|
||||
**/
|
||||
// @Value("${ftp.username}")
|
||||
private String ftpUsername = "reportpdf";
|
||||
/**
|
||||
* FTP密码
|
||||
**/
|
||||
// @Value("${ftp.password}")
|
||||
private String ftpPassword = "123.com";
|
||||
/**
|
||||
* FTP基础目录
|
||||
**/
|
||||
// @Value("${ftp.basepath}")
|
||||
private String basePath;
|
||||
|
||||
/**
|
||||
* 本地字符编码
|
||||
**/
|
||||
private static String localCharset = "GBK";
|
||||
|
||||
/**
|
||||
* FTP协议里面,规定文件名编码为iso-8859-1
|
||||
**/
|
||||
private static String serverCharset = "ISO-8859-1";
|
||||
|
||||
/**
|
||||
* UTF-8字符编码
|
||||
**/
|
||||
private static final String CHARSET_UTF8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* OPTS UTF8字符串常量
|
||||
**/
|
||||
private static final String OPTS_UTF8 = "OPTS UTF8";
|
||||
|
||||
/**
|
||||
* 设置缓冲区大小4M
|
||||
**/
|
||||
private static final int BUFFER_SIZE = 1024 * 1024 * 4;
|
||||
|
||||
/**
|
||||
* FTPClient对象
|
||||
**/
|
||||
private static FTPClient ftpClient = null;
|
||||
|
||||
public boolean downloadFile(String ftpPath, String savePath, String filename) {
|
||||
// 登录
|
||||
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
|
||||
if (ftpClient != null) {
|
||||
try {
|
||||
String path = changeEncoding(basePath + ftpPath);
|
||||
// 判断是否存在该目录
|
||||
if (!ftpClient.changeWorkingDirectory(path)) {
|
||||
logger.error(basePath + ftpPath + DIR_NOT_EXIST);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
ftpClient.enterLocalPassiveMode(); // 设置被动模式,开通一个端口来传输数据
|
||||
// String[] fs = ftpClient.listNames();
|
||||
// // 判断该目录下是否有文件
|
||||
// if (fs == null || fs.length == 0) {
|
||||
// logger.error(basePath + ftpPath + DIR_CONTAINS_NO_FILE);
|
||||
// return Boolean.FALSE;
|
||||
// }
|
||||
// for (String ff : fs) {
|
||||
//String ftpName = new String(ff.getBytes(serverCharset), localCharset);
|
||||
File file = new File(savePath + filename);
|
||||
try (OutputStream os = new FileOutputStream(file)) {
|
||||
ftpClient.retrieveFile(filename, os);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
// }
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
|
||||
closeConnect();
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载该目录下所有文件到本地
|
||||
*
|
||||
* @param ftpPath FTP服务器上的相对路径,例如:test/123
|
||||
* @param savePath 保存文件到本地的路径,例如:D:/test
|
||||
* @return 成功返回true,否则返回false
|
||||
*/
|
||||
public boolean downloadFiles(String ftpPath, String savePath) {
|
||||
// 登录
|
||||
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
|
||||
if (ftpClient != null) {
|
||||
try {
|
||||
String path = changeEncoding(basePath + ftpPath);
|
||||
// 判断是否存在该目录
|
||||
if (!ftpClient.changeWorkingDirectory(path)) {
|
||||
logger.error(basePath + ftpPath + DIR_NOT_EXIST);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
ftpClient.enterLocalPassiveMode(); // 设置被动模式,开通一个端口来传输数据
|
||||
String[] fs = ftpClient.listNames();
|
||||
// 判断该目录下是否有文件
|
||||
if (fs == null || fs.length == 0) {
|
||||
logger.error(basePath + ftpPath + DIR_CONTAINS_NO_FILE);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
for (String ff : fs) {
|
||||
String ftpName = new String(ff.getBytes(serverCharset), localCharset);
|
||||
File file = new File(savePath + '/' + ftpName);
|
||||
try (OutputStream os = new FileOutputStream(file)) {
|
||||
ftpClient.retrieveFile(ff, os);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("下载文件失败", e);
|
||||
} finally {
|
||||
closeConnect();
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接FTP服务器
|
||||
*
|
||||
* @param address 地址,如:127.0.0.1
|
||||
* @param port 端口,如:21
|
||||
* @param username 用户名,如:root
|
||||
* @param password 密码,如:root
|
||||
*/
|
||||
private void login(String address, int port, String username, String password) {
|
||||
ftpClient = new FTPClient();
|
||||
try {
|
||||
logger.debug("address:" + address);
|
||||
logger.debug("port:" + port);
|
||||
logger.debug("username:" + username);
|
||||
logger.debug("password:" + password);
|
||||
ftpClient.connect(address, port);
|
||||
logger.debug("connect ftpserver:" + address + " success");
|
||||
|
||||
ftpClient.login(username, password);
|
||||
logger.debug("login ftpserver:" + address + " success");
|
||||
|
||||
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
|
||||
//限制缓冲区大小
|
||||
ftpClient.setBufferSize(BUFFER_SIZE);
|
||||
int reply = ftpClient.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
closeConnect();
|
||||
logger.error("FTP服务器连接失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("FTP登录失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FTP服务器路径编码转换
|
||||
*
|
||||
* @param ftpPath FTP服务器路径
|
||||
* @return String
|
||||
*/
|
||||
private static String changeEncoding(String ftpPath) {
|
||||
String directory = null;
|
||||
try {
|
||||
if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(OPTS_UTF8, "ON"))) {
|
||||
localCharset = CHARSET_UTF8;
|
||||
}
|
||||
directory = new String(ftpPath.getBytes(localCharset), serverCharset);
|
||||
} catch (Exception e) {
|
||||
logger.error("路径编码转换失败", e);
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭FTP连接
|
||||
*/
|
||||
private void closeConnect() {
|
||||
if (ftpClient != null && ftpClient.isConnected()) {
|
||||
try {
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
} catch (IOException e) {
|
||||
logger.error("关闭FTP连接失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定目录下是否含有指定文件
|
||||
*
|
||||
* @param ftpPath FTP服务器文件相对路径,例如:test/123
|
||||
* @param fileName 要下载的文件名,例如:test.txt
|
||||
* @return 成功返回true,否则返回false
|
||||
*/
|
||||
public boolean checkFileInFtp(String ftpPath, String fileName) {
|
||||
// 登录
|
||||
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
|
||||
if (ftpClient != null) {
|
||||
try {
|
||||
String path = changeEncoding(basePath + ftpPath);
|
||||
// 判断是否存在该目录
|
||||
if (!ftpClient.changeWorkingDirectory(path)) {
|
||||
logger.error(basePath + ftpPath + DIR_NOT_EXIST);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
ftpClient.enterLocalPassiveMode(); // 设置被动模式,开通一个端口来传输数据
|
||||
String[] fs = ftpClient.listNames();
|
||||
// 判断该目录下是否有文件
|
||||
if (fs == null || fs.length == 0) {
|
||||
logger.error(basePath + ftpPath + DIR_CONTAINS_NO_FILE);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
for (String ff : fs) {
|
||||
String ftpName = new String(ff.getBytes(serverCharset), localCharset);
|
||||
if (ftpName.equals(fileName)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("请求出错", e);
|
||||
} finally {
|
||||
closeConnect();
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载该目录下所有文件到本地 根据实际需要修改执行逻辑
|
||||
*
|
||||
* @param ftpPath FTP服务器上的相对路径,例如:test/123
|
||||
* @param savePath 保存文件到本地的路径,例如:D:/test
|
||||
* @return 成功返回true,否则返回false
|
||||
*/
|
||||
public Map<String, Object> downLoadTableFile(String ftpPath, String savePath) {
|
||||
// 登录
|
||||
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
if (ftpClient != null) {
|
||||
try {
|
||||
String path = changeEncoding(basePath + "/" + ftpPath);
|
||||
// 判断是否存在该目录
|
||||
if (!ftpClient.changeWorkingDirectory(path)) {
|
||||
logger.error(basePath + "/" + ftpPath + DIR_NOT_EXIST);
|
||||
resultMap.put("result", false);
|
||||
return resultMap;
|
||||
}
|
||||
ftpClient.enterLocalPassiveMode(); // 设置被动模式,开通一个端口来传输数据
|
||||
String[] fs = ftpClient.listNames();
|
||||
// 判断该目录下是否有文件
|
||||
if (fs == null || fs.length == 0) {
|
||||
logger.error(basePath + "/" + ftpPath + DIR_CONTAINS_NO_FILE);
|
||||
resultMap.put("result", false);
|
||||
return resultMap;
|
||||
}
|
||||
List<String> tableFileNameList = new ArrayList<>();
|
||||
//根据表名创建文件夹
|
||||
String tableDirName = savePath + "/" + ftpPath;
|
||||
File tableDirs=new File(tableDirName);
|
||||
if(!tableDirs.exists()){
|
||||
tableDirs.mkdirs();
|
||||
}
|
||||
for (String ff : fs) {
|
||||
String ftpName = new String(ff.getBytes(serverCharset), localCharset);
|
||||
File file = new File(tableDirName + "/" + ftpName);
|
||||
//存储文件名导入时使用
|
||||
tableFileNameList.add(tableDirName + "/" + ftpName);
|
||||
try (OutputStream os = new FileOutputStream(file)) {
|
||||
ftpClient.retrieveFile(ff, os);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
resultMap.put("fileNameList", tableFileNameList);
|
||||
resultMap.put("result", true);
|
||||
return resultMap;
|
||||
} catch (IOException e) {
|
||||
logger.error("下载文件失败", e);
|
||||
} finally {
|
||||
closeConnect();
|
||||
}
|
||||
}
|
||||
resultMap.put("result", false);
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
135
src/main/java/com/guahao/common/util/FtpWebUtil.java
Normal file
135
src/main/java/com/guahao/common/util/FtpWebUtil.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import cn.hutool.log.Log;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @ClassName: FtpWebUtil
|
||||
* @Description:
|
||||
* @Author T.W
|
||||
* @Date 2021/11/24
|
||||
* @Version 1.0
|
||||
*/
|
||||
public class FtpWebUtil {
|
||||
private static Logger logger = LoggerFactory.getLogger(FtpWebUtil.class);
|
||||
|
||||
|
||||
public static byte[] readInputStream(InputStream inStream) throws Exception {
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
//创建一个Buffer字符串
|
||||
byte[] buffer = new byte[4096];
|
||||
//每次读取的字符串长度,如果为-1,代表全部读取完毕
|
||||
int len = 0;
|
||||
//使用一个输入流从buffer里把数据读取出来
|
||||
while ((len = inStream.read(buffer)) != -1) {
|
||||
//用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
|
||||
outStream.write(buffer, 0, len);
|
||||
}
|
||||
//关闭输入流
|
||||
inStream.close();
|
||||
//把outStream里的数据写入内存
|
||||
return outStream.toByteArray();
|
||||
}
|
||||
|
||||
public int downloadpdf(String urlname, String pathname, String filename) throws IOException {
|
||||
try {
|
||||
logger.debug("urlname:" + urlname);
|
||||
logger.debug("pathname:" + pathname);
|
||||
logger.debug("filename:" + filename);
|
||||
|
||||
//new一个URL对象
|
||||
// URL url = new URL("ftp://reportpdf:123.com@192.168.150.3:20015/15100142_0219515.pdf");
|
||||
URL url = new URL(urlname);
|
||||
//打开链接
|
||||
URLConnection conn = url.openConnection();
|
||||
//超时响应时间为5秒
|
||||
conn.setConnectTimeout(5 * 1000);
|
||||
//通过输入流获取图片数据
|
||||
InputStream inStream = conn.getInputStream();
|
||||
//得到图片的二进制数据,以二进制封装得到数据,具有通用性
|
||||
byte[] data = readInputStream(inStream);
|
||||
//new一个文件对象用来保存图片,默认保存当前工程根目录
|
||||
File imageFile = new File(pathname + filename);
|
||||
//创建输出流
|
||||
FileOutputStream outStream = new FileOutputStream(imageFile);
|
||||
//写入数据
|
||||
outStream.write(data);
|
||||
//关闭输出流
|
||||
outStream.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static FTPClient ftpConnection(String ip, String port, String username, String password) throws IOException {
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
try {
|
||||
ftpClient.connect(ip, Integer.parseInt(port));
|
||||
ftpClient.login(username, password);
|
||||
int replyCode = ftpClient.getReplyCode(); //是否成功登录服务器
|
||||
if (!FTPReply.isPositiveCompletion(replyCode)) {
|
||||
ftpClient.disconnect();
|
||||
logger.error("--ftp连接失败--");
|
||||
System.exit(1);
|
||||
}
|
||||
ftpClient.enterLocalPassiveMode();//这句最好加告诉对面服务器开一个端口
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ftpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载方法
|
||||
*
|
||||
* @param ftpClient FTPClient对象
|
||||
* @param newFileName 新文件名
|
||||
* @param fileName 原文件名
|
||||
* @param downUrl 下载路径
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static boolean downFile(FTPClient ftpClient, String newFileName, String fileName, String downUrl) throws IOException {
|
||||
boolean isTrue = false;
|
||||
OutputStream os = null;
|
||||
|
||||
File localFile = new File("/home/igzh/hnwj-pdf/LISPDF/" + downUrl + "/" + newFileName);
|
||||
ftpClient.changeWorkingDirectory(downUrl);
|
||||
ftpClient.enterLocalPassiveMode();
|
||||
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
// FTPFile[] ftpFiles = ftpClient.listFiles();
|
||||
|
||||
os = Files.newOutputStream(localFile.toPath());
|
||||
isTrue = ftpClient.retrieveFile(new String(fileName.getBytes(), StandardCharsets.ISO_8859_1), os);
|
||||
// isTrue = ftpClient.retrieveFile(new String(fileName.getBytes(), StandardCharsets.UTF_8), os);
|
||||
// isTrue = ftpClient.retrieveFile(new String(fileName.getBytes(), "GBK"), os);
|
||||
os.close();
|
||||
return isTrue;
|
||||
|
||||
}
|
||||
|
||||
public static void close(FTPClient ftpClient) throws IOException {
|
||||
if (ftpClient != null && ftpClient.isConnected()) {
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
105
src/main/java/com/guahao/common/util/HttpClientUtil.java
Normal file
105
src/main/java/com/guahao/common/util/HttpClientUtil.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class HttpClientUtil {
|
||||
|
||||
private static final String CONTENT_TYPE_TEXT_JSON = "text/json";
|
||||
|
||||
private static final RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(2000)
|
||||
.setSocketTimeout(10000).build();
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
// public static String doPost(String url, Map<String,String> map){
|
||||
public static String doPost(String url, Object param){
|
||||
String charset = "utf-8";
|
||||
HttpClient httpClient = null;
|
||||
HttpPost httpPost = null;
|
||||
String result = null;
|
||||
try{
|
||||
httpClient = new DefaultHttpClient();
|
||||
httpPost = new HttpPost(url);
|
||||
httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
|
||||
httpPost.setConfig(requestConfig);
|
||||
String parameter = JSON.toJSONString(param);
|
||||
|
||||
StringEntity se = null;
|
||||
System.out.println(parameter);
|
||||
se = new StringEntity(parameter, "utf-8");
|
||||
se.setContentType(CONTENT_TYPE_TEXT_JSON);
|
||||
se.setContentEncoding("utf-8");
|
||||
httpPost.setEntity(se);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPost);
|
||||
if(response != null){
|
||||
HttpEntity resEntity = response.getEntity();
|
||||
if(resEntity != null){
|
||||
result = EntityUtils.toString(resEntity,charset);
|
||||
}
|
||||
}
|
||||
}catch(Exception ex){
|
||||
ex.printStackTrace();
|
||||
}finally{
|
||||
httpClient.getConnectionManager().shutdown();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static String doGet(String url, Map<String,String> map){
|
||||
String charset = "utf-8";
|
||||
HttpClient httpClient = null;
|
||||
HttpGet httpPost = null;
|
||||
String result = null;
|
||||
try{
|
||||
httpClient = new DefaultHttpClient();
|
||||
List<NameValuePair> list = new ArrayList<NameValuePair>();
|
||||
if (map!=null) {
|
||||
Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
|
||||
while(iterator.hasNext()){
|
||||
Entry<String,String> elem = iterator.next();
|
||||
list.add(new BasicNameValuePair(elem.getKey(),elem.getValue()));
|
||||
}
|
||||
}
|
||||
if(list.size() > 0){
|
||||
String paramString = URLEncodedUtils.format(list, HTTP.UTF_8);
|
||||
url = url +"?"+ paramString;
|
||||
}
|
||||
httpPost = new HttpGet(url);
|
||||
HttpResponse response = httpClient.execute(httpPost);
|
||||
if(response != null){
|
||||
HttpEntity resEntity = response.getEntity();
|
||||
if(resEntity != null){
|
||||
result = EntityUtils.toString(resEntity,charset);
|
||||
}
|
||||
}
|
||||
}catch(Exception ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
64
src/main/java/com/guahao/common/util/IDUtil.java
Normal file
64
src/main/java/com/guahao/common/util/IDUtil.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
public class IDUtil {
|
||||
|
||||
public static String randomId() {
|
||||
return UUID.randomUUID().toString().split("-")[0];
|
||||
}
|
||||
|
||||
public static String getCurCode(String Cur, BigDecimal Val){
|
||||
if("USD".equals(Cur)){
|
||||
if(Val.compareTo(BigDecimal.valueOf(1)) == 0 || Val.compareTo(BigDecimal.valueOf(2)) == 0){
|
||||
return "US1";
|
||||
}
|
||||
if(Val.compareTo(BigDecimal.valueOf(5)) == 0 || Val.compareTo(BigDecimal.valueOf(10)) == 0 || Val.compareTo(BigDecimal.valueOf(20)) == 0){
|
||||
return "US2";
|
||||
}else{
|
||||
return "US3";
|
||||
}
|
||||
}
|
||||
// else if("AUD".equals(Cur)){
|
||||
// if(Val.compareTo(BigDecimal.valueOf(1)) == 0 || Val.compareTo(BigDecimal.valueOf(2)) == 0){
|
||||
// return "AU1";
|
||||
// }else{
|
||||
// return "AU2";
|
||||
// }
|
||||
// }
|
||||
else{
|
||||
return Cur;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多个BigDecimal数相加和
|
||||
*
|
||||
* @param i
|
||||
* @param arg
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal getBigDecimalSum(BigDecimal i, BigDecimal... arg) {
|
||||
BigDecimal sum = i;
|
||||
for (BigDecimal b : arg) {
|
||||
sum = sum.add(b);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 差
|
||||
* @param i
|
||||
* @param arg
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal getBigDecimalDifference(BigDecimal i, BigDecimal... arg) {
|
||||
BigDecimal difference = i;
|
||||
for (BigDecimal b : arg) {
|
||||
difference = difference.subtract(b);
|
||||
}
|
||||
return difference;
|
||||
}
|
||||
|
||||
}
|
||||
103
src/main/java/com/guahao/common/util/IPUtil.java
Normal file
103
src/main/java/com/guahao/common/util/IPUtil.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 客户端IP地址获取工具类
|
||||
*
|
||||
* @author SangChengZhi
|
||||
* @date 2025年09月24日 15:45
|
||||
*/
|
||||
public final class IPUtil {
|
||||
|
||||
/**
|
||||
* 私有构造函数,防止实例化
|
||||
*/
|
||||
private IPUtil() {
|
||||
throw new AssertionError("No instances for utility class");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端真实IP地址
|
||||
*
|
||||
* 优先顺序:
|
||||
* 1. X-Forwarded-For
|
||||
* 2. X-Real-IP
|
||||
* 3. Proxy-Client-IP
|
||||
* 4. WL-Proxy-Client-IP
|
||||
* 5. request.getRemoteAddr()
|
||||
*
|
||||
* 注意:X-Forwarded-For 可能包含多个IP(通过逗号分隔),取第一个非unknown的
|
||||
*
|
||||
* @param request HttpServletRequest对象,不可为null
|
||||
* @return 客户端IP地址,若无法获取则返回"0.0.0.0"
|
||||
*/
|
||||
public static String getClientIp(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "0.0.0.0";
|
||||
}
|
||||
|
||||
String ipAddress = null;
|
||||
|
||||
// 优先级顺序检查
|
||||
ipAddress = firstValidIp(request.getHeader("X-Forwarded-For"));
|
||||
if (isValidIp(ipAddress)) {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
ipAddress = request.getHeader("X-Real-IP");
|
||||
if (isValidIp(ipAddress)) {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
ipAddress = request.getHeader("Proxy-Client-IP");
|
||||
if (isValidIp(ipAddress)) {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
||||
if (isValidIp(ipAddress)) {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
// 最后使用远程地址
|
||||
// ipAddress = request.getRemoteAddr();
|
||||
|
||||
// 处理本地回环地址(IPv4 和 IPv6)
|
||||
if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) {
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
return ipAddress != null ? ipAddress : "0.0.0.0";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查IP是否有效(非null、非空、非unknown)
|
||||
*
|
||||
* @param ip IP地址
|
||||
* @return 是否有效
|
||||
*/
|
||||
private static boolean isValidIp(String ip) {
|
||||
return ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 X-Forwarded-For 头中提取第一个有效的IP地址
|
||||
*
|
||||
* @param xForwardedFor X-Forwarded-For 头值
|
||||
* @return 第一个有效IP,无效则返回null
|
||||
*/
|
||||
private static String firstValidIp(String xForwardedFor) {
|
||||
if (!isValidIp(xForwardedFor)) {
|
||||
return null;
|
||||
}
|
||||
String[] ips = xForwardedFor.split(",");
|
||||
for (String ip : ips) {
|
||||
ip = ip.trim();
|
||||
if (isValidIp(ip)) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
126
src/main/java/com/guahao/common/util/Ic.java
Normal file
126
src/main/java/com/guahao/common/util/Ic.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Mr.zs
|
||||
* @date 2025/4/9
|
||||
*/
|
||||
public class Ic {
|
||||
// 主要计算方法,identifyCard是传入的15位身份证号
|
||||
public static String get18Ic(String identifyCard) {
|
||||
|
||||
String retId = "";
|
||||
String id17 = "";
|
||||
int sum = 0;
|
||||
int y = 0;
|
||||
// 定义数组加权因子
|
||||
int[] wf = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
|
||||
// 定义数组存放校验码
|
||||
String[] cc = {"1", "0", "x", "9", "8", "7", "6", "5", "4", "3", "2"};
|
||||
// 在原15位数身份证的第六位数后面插入19
|
||||
id17 = identifyCard.substring(0, 6) + "19" + identifyCard.substring(6);
|
||||
// 17位数字和系数相乘,结果相加
|
||||
for (int i = 0; i < 17; i++) {
|
||||
sum = sum + Integer.valueOf(id17.substring(i, i + 1)) * wf[i];
|
||||
|
||||
}
|
||||
// 计算余数
|
||||
y = sum % 11;
|
||||
// 通过模获得对应的校验码cc[yy];
|
||||
retId = id17 + cc[y];
|
||||
return retId;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 读取文件,参数fileName为传入的要读取文件的路径及文件名
|
||||
public static void readFileByLines(String fileName) {
|
||||
|
||||
File file = new File(fileName);
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
|
||||
reader = new BufferedReader(new FileReader(file));
|
||||
String tempString = null;
|
||||
int line = 1;
|
||||
// 一次读取一行
|
||||
while ((tempString = reader.readLine()) != null) {
|
||||
// 新的身份证号码
|
||||
String returnId = get18Ic(tempString);
|
||||
// 写入到文件中
|
||||
WriterFile(returnId);
|
||||
|
||||
line++;
|
||||
|
||||
}
|
||||
reader.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
|
||||
try {
|
||||
reader.close();
|
||||
} catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 存入文件的名称
|
||||
static String file = null;
|
||||
|
||||
// 参数returnID为返回的18位身份证号,是要写入文件的内容
|
||||
public static void WriterFile(String returnID) {
|
||||
FileWriter fw = null;
|
||||
// 如果文件不存在,创建文件
|
||||
try {
|
||||
File f = new File(file);
|
||||
fw = new FileWriter(f, true);
|
||||
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
PrintWriter pw = new PrintWriter(fw);
|
||||
pw.println(returnID + "\n");
|
||||
pw.flush();
|
||||
try {
|
||||
fw.flush();
|
||||
pw.close();
|
||||
fw.close();
|
||||
} catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 以日期作为输出文件的文件名
|
||||
public static String FileName() {
|
||||
SimpleDateFormat simpleDateFormat;
|
||||
simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
Date date = new Date();
|
||||
String filenametxt = simpleDateFormat.format(date);
|
||||
return filenametxt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// String ic = get18Ic("130503670401001");
|
||||
//
|
||||
// System.out.println(ic);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
179
src/main/java/com/guahao/common/util/JedisUntils.java
Normal file
179
src/main/java/com/guahao/common/util/JedisUntils.java
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* JedisUntils.java
|
||||
* com.test
|
||||
*
|
||||
* Function: TODO
|
||||
*
|
||||
* ver date author
|
||||
* ──────────────────────────────────
|
||||
* 2018年1月17日 Administrator
|
||||
*
|
||||
* Copyright (c) 2018, TNT All Rights Reserved.
|
||||
*/
|
||||
|
||||
package com.guahao.common.util;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ClassName:JedisUntils
|
||||
* Function: TODO ADD FUNCTION
|
||||
* Reason: TODO ADD REASON
|
||||
*
|
||||
* @author gjf001
|
||||
* @version
|
||||
* @since Ver 1.1
|
||||
* @Date 2018年1月17日 下午7:20:29
|
||||
*
|
||||
* @see
|
||||
*
|
||||
*/
|
||||
public class JedisUntils {
|
||||
//私有化一个连接池
|
||||
private final static JedisPool POOL;
|
||||
|
||||
private final static String url="127.0.0.1";
|
||||
/**
|
||||
* 初始化POOL
|
||||
*/
|
||||
static{
|
||||
//创建连接池对象
|
||||
JedisPoolConfig cofig=new JedisPoolConfig();
|
||||
//获取连接池
|
||||
POOL=new JedisPool(cofig, url, 6379);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static JedisPool jedisPool = null;
|
||||
|
||||
/**
|
||||
* redis过期时间,以秒为单位*/
|
||||
|
||||
public final static int EXRP_HOUR = 60*60; //一小时
|
||||
public final static int EXRP_DAY = 60*60*24; //一天
|
||||
public final static int EXRP_MONTH = 60*60*24*30; //一个月
|
||||
|
||||
/**
|
||||
* 初始化Redis连接池*/
|
||||
|
||||
private static void initialPool(){
|
||||
try {
|
||||
JedisPoolConfig config = new JedisPoolConfig();
|
||||
jedisPool = new JedisPool(config,url, 6379, 1000);
|
||||
} catch (Exception e) {
|
||||
|
||||
System.out.println(e);
|
||||
try{
|
||||
//如果第一个IP异常,则访问第二个IP
|
||||
JedisPoolConfig config = new JedisPoolConfig();
|
||||
jedisPool = new JedisPool(config,url, 6379, 1000);
|
||||
}catch(Exception e2){
|
||||
System.out.println(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在多线程环境同步初始化*/
|
||||
|
||||
private static synchronized void poolInit() {
|
||||
if (jedisPool == null) {
|
||||
initialPool();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步获取Jedis实例
|
||||
* @return Jedis*/
|
||||
|
||||
public synchronized static Jedis getJedis() {
|
||||
if (jedisPool == null) {
|
||||
poolInit();
|
||||
}
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
if (jedisPool != null) {
|
||||
jedis = jedisPool.getResource();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// logger.error("Get jedis error : "+e);
|
||||
System.out.println(e);
|
||||
}finally{
|
||||
returnResource(jedis);
|
||||
}
|
||||
return jedis;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 释放jedis资源
|
||||
* @param jedis*/
|
||||
|
||||
public static void returnResource(final Jedis jedis) {
|
||||
if (jedis != null && jedisPool !=null) {
|
||||
jedisPool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置 String
|
||||
* @param key
|
||||
* @param value*/
|
||||
|
||||
public static void setString(String key ,String value){
|
||||
try {
|
||||
|
||||
getJedis().set(key,value);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 过期时间
|
||||
* @param key
|
||||
* @param seconds 以秒为单位
|
||||
* @param value*/
|
||||
|
||||
public static void setString(String key ,int seconds,String value){
|
||||
try {
|
||||
|
||||
getJedis().setex(key, seconds, value);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String值
|
||||
* @param key
|
||||
* @return value*/
|
||||
|
||||
public static String getString(String key){
|
||||
if(getJedis() == null || !getJedis().exists(key)){
|
||||
return null;
|
||||
}
|
||||
return getJedis().get(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
47
src/main/java/com/guahao/common/util/MD5Util.java
Normal file
47
src/main/java/com/guahao/common/util/MD5Util.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class MD5Util {
|
||||
public static void main(String[] args) {
|
||||
System.out.println(MD5Encode("123456s","UTF-8"));
|
||||
}
|
||||
|
||||
private static String byteArrayToHexString(byte b[]) {
|
||||
StringBuffer resultSb = new StringBuffer();
|
||||
for (int i = 0; i < b.length; i++)
|
||||
resultSb.append(byteToHexString(b[i]));
|
||||
|
||||
return resultSb.toString();
|
||||
}
|
||||
|
||||
private static String byteToHexString(byte b) {
|
||||
int n = b;
|
||||
if (n < 0)
|
||||
n += 256;
|
||||
int d1 = n / 16;
|
||||
int d2 = n % 16;
|
||||
return hexDigits[d1] + hexDigits[d2];
|
||||
}
|
||||
|
||||
public static String MD5Encode(String origin, String charsetname) {
|
||||
String resultString = null;
|
||||
try {
|
||||
resultString = new String(origin);
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
if (charsetname == null || "".equals(charsetname))
|
||||
resultString = byteArrayToHexString(md.digest(resultString
|
||||
.getBytes()));
|
||||
else
|
||||
resultString = byteArrayToHexString(md.digest(resultString
|
||||
.getBytes(charsetname)));
|
||||
} catch (Exception exception) {
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
||||
|
||||
}
|
||||
180
src/main/java/com/guahao/common/util/OrderNoUtils.java
Normal file
180
src/main/java/com/guahao/common/util/OrderNoUtils.java
Normal file
@@ -0,0 +1,180 @@
|
||||
package com.guahao.common.util;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
/**
|
||||
* @ClassName: OrderNoUtils
|
||||
* @Description:
|
||||
* @Author T.W
|
||||
* @Date 2023/3/31
|
||||
* @Version 1.0
|
||||
*/
|
||||
public class OrderNoUtils {
|
||||
|
||||
// ==============================Fields===========================================
|
||||
/**
|
||||
* 开始时间截 (2018-07-03)
|
||||
*/
|
||||
|
||||
private final long twepoch = 1530607760000L;
|
||||
|
||||
/**
|
||||
* 机器id所占的位数
|
||||
*/
|
||||
private final long workerIdBits = 5L;
|
||||
|
||||
/**
|
||||
* 数据标识id所占的位数
|
||||
*/
|
||||
private final long datacenterIdBits = 5L;
|
||||
|
||||
/**
|
||||
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
|
||||
*/
|
||||
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
|
||||
|
||||
/**
|
||||
* 支持的最大数据标识id,结果是31
|
||||
*/
|
||||
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
|
||||
|
||||
/**
|
||||
* 序列在id中占的位数
|
||||
*/
|
||||
private final long sequenceBits = 12L;
|
||||
|
||||
/**
|
||||
* 机器ID向左移12位
|
||||
*/
|
||||
private final long workerIdShift = sequenceBits;
|
||||
|
||||
/**
|
||||
* 数据标识id向左移17位(12+5)
|
||||
*/
|
||||
private final long datacenterIdShift = sequenceBits + workerIdBits;
|
||||
|
||||
/**
|
||||
* 时间截向左移22位(5+5+12)
|
||||
*/
|
||||
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
||||
|
||||
/**
|
||||
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
|
||||
*/
|
||||
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
|
||||
|
||||
/**
|
||||
* 工作机器ID(0~31)
|
||||
*/
|
||||
private long workerId;
|
||||
|
||||
/**
|
||||
* 数据中心ID(0~31)
|
||||
*/
|
||||
private long datacenterId;
|
||||
|
||||
/**
|
||||
* 毫秒内序列(0~4095)
|
||||
*/
|
||||
private long sequence = 0L;
|
||||
|
||||
/**
|
||||
* 上次生成ID的时间截
|
||||
*/
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
//==============================Constructors=====================================
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param workerId 工作ID (0~31)
|
||||
* @param datacenterId 数据中心ID (0~31)
|
||||
*/
|
||||
public OrderNoUtils(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;
|
||||
}
|
||||
|
||||
// ==============================Methods==========================================
|
||||
|
||||
/**
|
||||
* 获得下一个ID (该方法是线程安全的)
|
||||
*
|
||||
* @return SnowflakeId
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
long timestamp = timeGen();
|
||||
|
||||
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new RuntimeException(
|
||||
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
|
||||
}
|
||||
|
||||
//如果是同一时间生成的,则进行毫秒内序列
|
||||
if (lastTimestamp == timestamp) {
|
||||
sequence = (sequence + 1) & sequenceMask;
|
||||
//毫秒内序列溢出
|
||||
if (sequence == 0) {
|
||||
//阻塞到下一个毫秒,获得新的时间戳
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
}
|
||||
//时间戳改变,毫秒内序列重置
|
||||
else {
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
//上次生成ID的时间截
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
//移位并通过或运算拼到一起组成64位的ID
|
||||
return (((timestamp - twepoch) << timestampLeftShift)
|
||||
| (datacenterId << datacenterIdShift)
|
||||
| (workerId << workerIdShift)
|
||||
| sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞到下一个毫秒,直到获得新的时间戳
|
||||
*
|
||||
* @param lastTimestamp 上次生成ID的时间截
|
||||
* @return 当前时间戳
|
||||
*/
|
||||
protected long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回以毫秒为单位的当前时间
|
||||
*
|
||||
* @return 当前时间(毫秒)
|
||||
*/
|
||||
protected long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
//==============================Test=============================================
|
||||
|
||||
/**
|
||||
* 测试
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
OrderNoUtils idWorker = new OrderNoUtils(0, 0);
|
||||
Set set = new HashSet();
|
||||
long id = idWorker.nextId();
|
||||
System.out.println(id);
|
||||
set.add(id);
|
||||
}
|
||||
|
||||
}
|
||||
216
src/main/java/com/guahao/common/util/RsaUtils.java
Normal file
216
src/main/java/com/guahao/common/util/RsaUtils.java
Normal file
@@ -0,0 +1,216 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
/**
|
||||
* @author https://www.cnblogs.com/nihaorz/p/10690643.html
|
||||
* @description Rsa 工具类,公钥私钥生成,加解密
|
||||
* @date 2020-05-18
|
||||
**/
|
||||
public class RsaUtils {
|
||||
|
||||
private static final String SRC = "123456";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("\n");
|
||||
RsaKeyPair keyPair = generateKeyPair();
|
||||
System.out.println("公钥:" + keyPair.getPublicKey());
|
||||
System.out.println("私钥:" + keyPair.getPrivateKey());
|
||||
System.out.println("\n");
|
||||
test1(keyPair);
|
||||
System.out.println("\n");
|
||||
test2(keyPair);
|
||||
System.out.println("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密私钥解密
|
||||
*/
|
||||
private static void test1(RsaKeyPair keyPair) throws Exception {
|
||||
System.out.println("***************** 公钥加密私钥解密开始 *****************");
|
||||
String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC);
|
||||
String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
|
||||
System.out.println("加密前:" + RsaUtils.SRC);
|
||||
System.out.println("加密后:" + text1);
|
||||
System.out.println("解密后:" + text2);
|
||||
if (RsaUtils.SRC.equals(text2)) {
|
||||
System.out.println("解密字符串和原始字符串一致,解密成功");
|
||||
} else {
|
||||
System.out.println("解密字符串和原始字符串不一致,解密失败");
|
||||
}
|
||||
System.out.println("***************** 公钥加密私钥解密结束 *****************");
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥加密公钥解密
|
||||
* @throws Exception /
|
||||
*/
|
||||
private static void test2(RsaKeyPair keyPair) throws Exception {
|
||||
System.out.println("***************** 私钥加密公钥解密开始 *****************");
|
||||
String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC);
|
||||
String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
|
||||
System.out.println("加密前:" + RsaUtils.SRC);
|
||||
System.out.println("加密后:" + text1);
|
||||
System.out.println("解密后:" + text2);
|
||||
if (RsaUtils.SRC.equals(text2)) {
|
||||
System.out.println("解密字符串和原始字符串一致,解密成功");
|
||||
} else {
|
||||
System.out.println("解密字符串和原始字符串不一致,解密失败");
|
||||
}
|
||||
System.out.println("***************** 私钥加密公钥解密结束 *****************");
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥解密
|
||||
*
|
||||
* @param publicKeyText 公钥
|
||||
* @param text 待解密的信息
|
||||
* @return /
|
||||
* @throws Exception /
|
||||
*/
|
||||
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
|
||||
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||
byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
|
||||
return new String(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥加密
|
||||
*
|
||||
* @param privateKeyText 私钥
|
||||
* @param text 待加密的信息
|
||||
* @return /
|
||||
* @throws Exception /
|
||||
*/
|
||||
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||
byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
|
||||
return Base64.encodeBase64String(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
* @param privateKeyText 私钥
|
||||
* @param text 待解密的文本
|
||||
* @return /
|
||||
* @throws Exception /
|
||||
*/
|
||||
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
|
||||
return new String(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param publicKeyText 公钥
|
||||
* @param text 待加密的文本
|
||||
* @return /
|
||||
*/
|
||||
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
|
||||
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
|
||||
return Base64.encodeBase64String(result);
|
||||
}
|
||||
|
||||
private static byte[] doLongerCipherFinal(int opMode,Cipher cipher, byte[] source) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
if (opMode == Cipher.DECRYPT_MODE) {
|
||||
out.write(cipher.doFinal(source));
|
||||
} else {
|
||||
int offset = 0;
|
||||
int totalSize = source.length;
|
||||
while (totalSize - offset > 0) {
|
||||
int size = Math.min(cipher.getOutputSize(0) - 11, totalSize - offset);
|
||||
out.write(cipher.doFinal(source, offset, size));
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建RSA密钥对
|
||||
*
|
||||
* @return /
|
||||
* @throws NoSuchAlgorithmException /
|
||||
*/
|
||||
public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.generateKeyPair();
|
||||
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
|
||||
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
|
||||
return new RsaKeyPair(publicKeyString, privateKeyString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* RSA密钥对对象
|
||||
*/
|
||||
public static class RsaKeyPair {
|
||||
|
||||
private final String publicKey;
|
||||
private final String privateKey;
|
||||
|
||||
public RsaKeyPair(String publicKey, String privateKey) {
|
||||
this.publicKey = publicKey;
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 MD5
|
||||
*
|
||||
* @param data 待处理数据
|
||||
* @return MD5结果
|
||||
*/
|
||||
public static String MD5(String data) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] array = md.digest(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
281
src/main/java/com/guahao/common/util/SoapPacsXmlParser.java
Normal file
281
src/main/java/com/guahao/common/util/SoapPacsXmlParser.java
Normal file
@@ -0,0 +1,281 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* SOAP PACS XML 解析工具类 - 完全重写版本
|
||||
* 使用分步处理和正则表达式确保健壮性
|
||||
*/
|
||||
public class SoapPacsXmlParser {
|
||||
|
||||
private static final String NAMESPACE_URI = "http://server.webservice.nxwjzy.neusoft.com/";
|
||||
private static final String RETURN_XPATH = "//ns2:StudiesReportsResponse/return";
|
||||
|
||||
/**
|
||||
* 主入口:解析完整的 SOAP 响应为 JSONObject
|
||||
*/
|
||||
public static JSONObject parseSoapResponseToJson(String soapXml) throws Exception {
|
||||
if (soapXml == null || soapXml.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("SOAP XML 不能为空");
|
||||
}
|
||||
|
||||
// Step 1: 从 SOAP 中提取 <return> 内的转义 XML 字符串
|
||||
String escapedInnerXml = extractReturnContent(soapXml);
|
||||
|
||||
// Step 2: 使用完全手动解析方法
|
||||
Map<String, Object> resultMap = robustManualParse(escapedInnerXml);
|
||||
|
||||
// Step 3: 转为 JSONObject
|
||||
return new JSONObject(resultMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 SOAP 响应中提取 <return> 节点的文本内容
|
||||
*/
|
||||
public static String extractReturnContent(String soapXml) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(soapXml.getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
xpath.setNamespaceContext(new NamespaceContext() {
|
||||
@Override
|
||||
public String getNamespaceURI(String prefix) {
|
||||
return "ns2".equals(prefix) ? NAMESPACE_URI : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix(String namespaceURI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getPrefixes(String namespaceURI) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Node returnNode = (Node) xpath.compile(RETURN_XPATH).evaluate(doc, XPathConstants.NODE);
|
||||
if (returnNode == null) {
|
||||
throw new IllegalStateException("未找到 <return> 节点");
|
||||
}
|
||||
return returnNode.getTextContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 健壮的手动解析方法
|
||||
*/
|
||||
public static Map<String, Object> robustManualParse(String xml) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
|
||||
try {
|
||||
// 步骤1:解码所有 XML 实体
|
||||
String decodedXml = decodeAllXmlEntities(xml);
|
||||
|
||||
// 步骤2:提取 PacsResult 内容
|
||||
String pacsResultContent = extractPacsResultContent(decodedXml);
|
||||
|
||||
// 步骤3:解析顶级字段
|
||||
parseTopLevelFields(pacsResultContent, result);
|
||||
|
||||
// 步骤4:解析所有 exeFiles
|
||||
parseAllExeFiles(pacsResultContent, result);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 如果上述方法失败,使用最基础的解析
|
||||
return basicTextParsing(xml);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 PacsResult 内容
|
||||
*/
|
||||
private static String extractPacsResultContent(String xml) {
|
||||
// 使用正则表达式提取 <PacsResult> 和 </PacsResult> 之间的内容
|
||||
Pattern pattern = Pattern.compile("<PacsResult>(.*?)</PacsResult>", Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(xml);
|
||||
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
} else {
|
||||
// 如果没有找到 PacsResult 标签,尝试直接使用整个内容
|
||||
if (xml.contains("<successe>") && xml.contains("</PacsResult>")) {
|
||||
// 可能是缺少开始的 PacsResult 标签
|
||||
return xml.substring(xml.indexOf("<successe>"));
|
||||
}
|
||||
return xml;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析顶级字段
|
||||
*/
|
||||
private static void parseTopLevelFields(String content, Map<String, Object> result) {
|
||||
// 顶级字段列表
|
||||
String[] topLevelTags = {"successe", "msg"};
|
||||
|
||||
for (String tag : topLevelTags) {
|
||||
Pattern pattern = Pattern.compile("<" + tag + ">(.*?)</" + tag + ">", Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
if (matcher.find()) {
|
||||
result.put(tag, matcher.group(1).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析所有 exeFiles
|
||||
*/
|
||||
private static void parseAllExeFiles(String content, Map<String, Object> result) {
|
||||
List<Map<String, String>> exeFilesList = new ArrayList<>();
|
||||
|
||||
// 使用正则表达式找到所有 exeFiles 块
|
||||
Pattern exeFilesPattern = Pattern.compile("<exeFiles>(.*?)</exeFiles>", Pattern.DOTALL);
|
||||
Matcher exeFilesMatcher = exeFilesPattern.matcher(content);
|
||||
|
||||
int index = 0;
|
||||
while (exeFilesMatcher.find()) {
|
||||
index++;
|
||||
String exeFileContent = exeFilesMatcher.group(1);
|
||||
Map<String, String> fileMap = parseSingleExeFile(exeFileContent, index);
|
||||
exeFilesList.add(fileMap);
|
||||
}
|
||||
|
||||
if (!exeFilesList.isEmpty()) {
|
||||
result.put("exeFiles", exeFilesList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个 exeFile
|
||||
*/
|
||||
private static Map<String, String> parseSingleExeFile(String content, int index) {
|
||||
Map<String, String> fileMap = new LinkedHashMap<>();
|
||||
|
||||
// 定义所有可能的字段,按照它们在 XML 中出现的顺序
|
||||
String[] fieldTags = {
|
||||
"StudiesMasculine", "ReportsTechnologies", "StudiesModalities",
|
||||
"ApproveDoctorAlias", "ReportsSignatureValue", "RequestElectronic",
|
||||
"ReportsUserCertificate", "PatientsPhone", "ApproveDateTime",
|
||||
"ReportsConclusion", "ReportsDoctorAlias", "StudiesDepartmentName",
|
||||
"StudiesExamineAlias", "PatientsSex", "StudiesExaminePrice",
|
||||
"StudiesKeenness", "StudiesInstUID", "AdmissionLocation",
|
||||
"ReviewsDoctorAlias", "AdmissionID", "StudiesDateTime",
|
||||
"PatientsDOB", "RegisterDateTime", "StudiesAge",
|
||||
"ReportsOutStatus", "StudiesExamineCode", "StudiesExamine",
|
||||
"PatientsName", "PatientsCertificate", "ReviewsDateTime",
|
||||
"StudiesStatus", "ReportsDateTime", "StudiesBodiesAlias",
|
||||
"RequestDepartmentAlias", "ReportsDoctorCode", "ReportsEvidences",
|
||||
"PatientsOccupation", "AdmissionSource", "RequestClinic",
|
||||
"CriticalValues", "ReportsSignatureTSValue", "ExecuteDoctorAlias",
|
||||
"AccessionNumber", "RequestDoctorAlias", "RequestDepartmentCode",
|
||||
"PatientsID", "PatientsAddress"
|
||||
};
|
||||
|
||||
// 对每个字段进行精确提取
|
||||
for (String tag : fieldTags) {
|
||||
String value = extractFieldValue(content, tag, index);
|
||||
fileMap.put(tag, value);
|
||||
}
|
||||
|
||||
return fileMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取字段值 - 精确匹配方法
|
||||
*/
|
||||
private static String extractFieldValue(String content, String tag, int exeFileIndex) {
|
||||
// 构建精确的正则表达式
|
||||
String regex = "<" + tag + ">(.*?)</" + tag + ">";
|
||||
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
|
||||
if (matcher.find()) {
|
||||
String value = matcher.group(1).trim();
|
||||
|
||||
// 对 ReportsEvidences 特殊处理:只处理 
,不处理 < 和 >
|
||||
if ("ReportsEvidences".equals(tag)) {
|
||||
value = processOnlyCRInReportsEvidences(value);
|
||||
} else {
|
||||
// 对其他字段,正常解码所有实体
|
||||
value = decodeAllXmlEntities(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return ""; // 如果没有找到,返回空字符串
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 ReportsEvidences 中只处理 
,不处理 < 和 >
|
||||
*/
|
||||
private static String processOnlyCRInReportsEvidences(String text) {
|
||||
if (text == null) return "";
|
||||
|
||||
// 只处理 
,保持 < 和 > 不变
|
||||
return text.replace("
", "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码所有 XML 实体
|
||||
*/
|
||||
private static String decodeAllXmlEntities(String xml) {
|
||||
if (xml == null) return null;
|
||||
|
||||
return xml
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("&", "&")
|
||||
.replace(""", "\"")
|
||||
.replace("'", "'")
|
||||
.replace("
", "\n")
|
||||
.replace(" ", "\n")
|
||||
.replace(" ", "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础文本解析 - 作为最后的备用方案
|
||||
*/
|
||||
private static Map<String, Object> basicTextParsing(String xml) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
List<Map<String, String>> exeFilesList = new ArrayList<>();
|
||||
|
||||
// 完全基于文本的解析,不依赖 XML 结构
|
||||
String decoded = decodeAllXmlEntities(xml);
|
||||
|
||||
// 简单的基于文本的模式匹配
|
||||
// 这里实现一个非常基础的解析逻辑...
|
||||
|
||||
// 由于时间关系,这里只返回空结果
|
||||
// 在实际应用中,你可以在这里实现更复杂的文本解析逻辑
|
||||
|
||||
result.put("successe", "false");
|
||||
result.put("msg", "解析失败,使用备用方案");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 简单的日志方法
|
||||
private static void log(String message) {
|
||||
System.out.println("[SoapPacsXmlParser] " + message);
|
||||
}
|
||||
}
|
||||
191
src/main/java/com/guahao/common/util/SoapUtil.java
Normal file
191
src/main/java/com/guahao/common/util/SoapUtil.java
Normal file
@@ -0,0 +1,191 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import com.guahao.common.Exception.LogicException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* 访问远程SOAP Web Service 协议接口
|
||||
*/
|
||||
public class SoapUtil {
|
||||
//外网测试地址
|
||||
// private static String url = "http://222.128.90.115:8010/BtGhYytWebService.asmx";
|
||||
//测试库地址
|
||||
// private static String url = "http://192.168.150.17:8002/BtGhYytWebService.asmx";
|
||||
|
||||
//生成库地址
|
||||
// private static String url = "http://192.168.150.15:8001/BtGhYytWebService.asmx";
|
||||
|
||||
|
||||
private static String url1 = "http://192.168.103.11:8088/Controllers/SmartHospitalService.asmx"; //his正式库
|
||||
private static String url11 = "http://192.168.103.11:8089/Controllers/SmartHospitalService.asmx"; //his测试库
|
||||
private static String url2= "http://192.168.0.36:8086/WebService1.asmx";//内正式库
|
||||
private static String url22= "http://192.168.0.36:8087/WebService1.asmx";//内测试库
|
||||
// http://192.168.0.36:8086/WebService1.asmx
|
||||
private static String url3 = "http://192.168.12.23:8080/PacsData/ws/wsServer?wsdl"; //pacs
|
||||
private static String url4 = "http://172.16.21.23/webservice/n_webservice.asmx"; //体检
|
||||
private static String url_zf = "http://192.168.103.11:8088/Controllers/SmartHospitalService.asmx";
|
||||
|
||||
|
||||
/**
|
||||
* @param xml
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String soapMethod(String xml) throws Exception {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url1);
|
||||
httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
|
||||
httpPost.setEntity(new StringEntity(xml, "UTF-8"));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
return EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
throw new LogicException("HTTP请求失败,状态码:" + statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param xml
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String soapMethod2(String xml) throws Exception {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url2);
|
||||
httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
|
||||
httpPost.setEntity(new StringEntity(xml, "UTF-8"));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
return EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
throw new LogicException("HTTP请求失败,状态码:" + statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param xml
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String zfSoapMethod(String xml) throws Exception {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url_zf);
|
||||
httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
|
||||
httpPost.setEntity(new StringEntity(xml, "UTF-8"));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
return EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
throw new LogicException("HTTP请求失败,状态码:" + statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务发送请求
|
||||
*
|
||||
* @param xml
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String taskSoapMethod(String xml) throws Exception {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url1);
|
||||
httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
|
||||
httpPost.setEntity(new StringEntity(xml, "UTF-8"));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
return EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
throw new LogicException("HTTP请求失败,状态码:" + statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于pacs报告
|
||||
* @param xml
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String soapMethod3(String xml) throws Exception {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url3);
|
||||
|
||||
// 设置请求头
|
||||
httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
|
||||
httpPost.setHeader("SOAPAction", ""); // 如果服务端需要特定的SOAPAction,请填写
|
||||
|
||||
// 设置请求体
|
||||
httpPost.setEntity(new StringEntity(xml, "UTF-8"));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
return EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
// 获取更多错误信息
|
||||
String errorResponse = EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
throw new LogicException("HTTP请求失败,状态码:" + statusCode + ",响应内容:" + errorResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于体检
|
||||
* @param xml
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String soapMethod4(String xml,String title) throws Exception {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost("http://172.16.21.23/webservice/n_webservice.asmx");
|
||||
|
||||
// 添加必要的头部
|
||||
httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
|
||||
httpPost.setHeader("SOAPAction", "http://tempurl.org/"+title);
|
||||
|
||||
// 构建完整的SOAP请求
|
||||
String soapRequest = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempurl.org\">" +
|
||||
"<soapenv:Header/>" +
|
||||
"<soapenv:Body>" +
|
||||
"<tem:"+title+">" +
|
||||
"<tem:as_xml>" +
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
|
||||
"<request>" + xml + "</request>" +
|
||||
"</tem:as_xml>" +
|
||||
"</tem:"+title+">" +
|
||||
"</soapenv:Body>" +
|
||||
"</soapenv:Envelope>";
|
||||
|
||||
httpPost.setEntity(new StringEntity(soapRequest, "UTF-8"));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
return EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
throw new LogicException("HTTP请求失败,状态码:" + statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main/java/com/guahao/common/util/SortUtil.java
Normal file
25
src/main/java/com/guahao/common/util/SortUtil.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author Mr.zs
|
||||
* @date 2025/3/27
|
||||
*/
|
||||
public class SortUtil {
|
||||
public static Map<String, Object> sortByKey(Map<String, Object> map){
|
||||
//创建一个带有比较器的TreeMap
|
||||
Map<String, Object> treeMap = new TreeMap<>(String::compareTo);
|
||||
//将你的map传入treeMap
|
||||
treeMap.putAll(map);
|
||||
return treeMap;
|
||||
}
|
||||
public static Map<String, Object> sortByKeyString(Map<String, String> map){
|
||||
//创建一个带有比较器的TreeMap
|
||||
Map<String, Object> treeMap = new TreeMap<>(String::compareTo);
|
||||
//将你的map传入treeMap
|
||||
treeMap.putAll(map);
|
||||
return treeMap;
|
||||
}
|
||||
}
|
||||
22
src/main/java/com/guahao/common/util/ThrowableUtil.java
Normal file
22
src/main/java/com/guahao/common/util/ThrowableUtil.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* 异常工具 2019-01-06
|
||||
* @author Zheng Jie
|
||||
*/
|
||||
public class ThrowableUtil {
|
||||
|
||||
/**
|
||||
* 获取堆栈信息
|
||||
*/
|
||||
public static String getStackTrace(Throwable throwable){
|
||||
StringWriter sw = new StringWriter();
|
||||
try (PrintWriter pw = new PrintWriter(sw)) {
|
||||
throwable.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
552
src/main/java/com/guahao/common/util/VeDate.java
Normal file
552
src/main/java/com/guahao/common/util/VeDate.java
Normal file
@@ -0,0 +1,552 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class VeDate {
|
||||
|
||||
/**
|
||||
* 获取七天前的日期
|
||||
*
|
||||
* @return 返回时间类型 yyyy-MM-dd
|
||||
*/
|
||||
public static String getSevenDaysAgo() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.add(Calendar.DATE, -7);
|
||||
Date Date = cal.getTime();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
String dateString = formatter.format(Date);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取现在时间
|
||||
*
|
||||
* @return 返回时间类型 yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public static Date getNowDate() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String dateString = formatter.format(currentTime);
|
||||
System.out.println(dateString);
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Date currentTime_2 = formatter.parse(dateString, pos);
|
||||
return currentTime_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取现在时间
|
||||
*
|
||||
* @return返回短时间格式 yyyy-MM-dd
|
||||
*/
|
||||
public static Date getNowDateShort() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
String dateString = formatter.format(currentTime);
|
||||
ParsePosition pos = new ParsePosition(8);
|
||||
Date currentTime_2 = formatter.parse(dateString, pos);
|
||||
return currentTime_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取现在时间
|
||||
*
|
||||
* @return返回字符串格式 yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public static String getStringDate() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String dateString = formatter.format(currentTime);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取现在时间
|
||||
*
|
||||
* @return 返回短时间字符串格式yyyy-MM-dd
|
||||
*/
|
||||
public static String getStringDateShort() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
String dateString = formatter.format(currentTime);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取时间 小时:分;秒 HH:mm:ss
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getTimeShort() {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
|
||||
Date currentTime = new Date();
|
||||
String dateString = formatter.format(currentTime);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将长时间格式字符串转换为时间 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param strDate
|
||||
* @return
|
||||
*/
|
||||
public static Date strToDateLong(String strDate) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Date strtodate = formatter.parse(strDate, pos);
|
||||
return strtodate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将长时间格式时间转换为字符串 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param dateDate
|
||||
* @return
|
||||
*/
|
||||
public static String dateToStrLong(Date dateDate) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String dateString = formatter.format(dateDate);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将短时间格式时间转换为字符串 yyyy-MM-dd
|
||||
*
|
||||
* @param dateDate
|
||||
* @return
|
||||
*/
|
||||
public static String dateToStr(Date dateDate) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
String dateString = formatter.format(dateDate);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将短时间格式字符串转换为时间 yyyy-MM-dd
|
||||
*
|
||||
* @param strDate
|
||||
* @return
|
||||
*/
|
||||
public static Date strToDate(String strDate) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Date strtodate = formatter.parse(strDate, pos);
|
||||
return strtodate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到现在时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Date getNow() {
|
||||
Date currentTime = new Date();
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取一个月中的最后一天
|
||||
*
|
||||
* @param day
|
||||
* @return
|
||||
*/
|
||||
public static Date getLastDate(long day) {
|
||||
Date date = new Date();
|
||||
long date_3_hm = date.getTime() - 3600000 * 34 * day;
|
||||
Date date_3_hm_date = new Date(date_3_hm);
|
||||
return date_3_hm_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到现在时间
|
||||
*
|
||||
* @return 字符串 yyyyMMdd HHmmss
|
||||
*/
|
||||
public static String getStringToday() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd HHmmss");
|
||||
String dateString = formatter.format(currentTime);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到现在小时
|
||||
*/
|
||||
public static String getHour() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String dateString = formatter.format(currentTime);
|
||||
String hour;
|
||||
hour = dateString.substring(11, 13);
|
||||
return hour;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到现在分钟
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getTime() {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String dateString = formatter.format(currentTime);
|
||||
String min;
|
||||
min = dateString.substring(14, 16);
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户传入的时间表示格式,返回当前时间的格式 如果是yyyyMMdd,注意字母y不能大写。
|
||||
*
|
||||
* @param sformat yyyyMMddhhmmss
|
||||
* @return
|
||||
*/
|
||||
public static String getUserDate(String sformat) {
|
||||
Date currentTime = new Date();
|
||||
SimpleDateFormat formatter = new SimpleDateFormat(sformat);
|
||||
String dateString = formatter.format(currentTime);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二个小时时间间的差值,必须保证二个时间都是"HH:MM"的格式,返回字符型的分钟
|
||||
*/
|
||||
public static String getTwoHour(String st1, String st2) {
|
||||
String[] kk = null;
|
||||
String[] jj = null;
|
||||
kk = st1.split(":");
|
||||
jj = st2.split(":");
|
||||
if (Integer.parseInt(kk[0]) < Integer.parseInt(jj[0]))
|
||||
return "0";
|
||||
else {
|
||||
double y = Double.parseDouble(kk[0]) + Double.parseDouble(kk[1]) / 60;
|
||||
double u = Double.parseDouble(jj[0]) + Double.parseDouble(jj[1]) / 60;
|
||||
if ((y - u) > 0)
|
||||
return y - u + "";
|
||||
else
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到二个日期间的间隔天数
|
||||
*/
|
||||
public static String getTwoDay(String sj1, String sj2) {
|
||||
SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
long day = 0;
|
||||
try {
|
||||
Date date = myFormatter.parse(sj1);
|
||||
Date mydate = myFormatter.parse(sj2);
|
||||
day = (date.getTime() - mydate.getTime()) / (24 * 60 * 60 * 1000);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
return day + "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间前推或后推分钟,其中JJ表示分钟.
|
||||
*/
|
||||
public static String getPreTime(String sj1, String jj) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String mydate1 = "";
|
||||
try {
|
||||
Date date1 = format.parse(sj1);
|
||||
long Time = (date1.getTime() / 1000) + Integer.parseInt(jj) * 60;
|
||||
date1.setTime(Time * 1000);
|
||||
mydate1 = format.format(date1);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return mydate1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到一个时间延后或前移几天的时间,nowdate为时间,delay为前移或后延的天数
|
||||
*/
|
||||
public static String getNextDay(String nowdate, String delay) {
|
||||
try {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
||||
String mdate = "";
|
||||
Date d = strToDate(nowdate);
|
||||
long myTime = (d.getTime() / 1000) + Integer.parseInt(delay) * 24 * 60 * 60;
|
||||
d.setTime(myTime * 1000);
|
||||
mdate = format.format(d);
|
||||
return mdate;
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否润年
|
||||
*
|
||||
* @param ddate
|
||||
* @return
|
||||
*/
|
||||
public static boolean isLeapYear(String ddate) {
|
||||
|
||||
/**
|
||||
* 详细设计: 1.被400整除是闰年,否则: 2.不能被4整除则不是闰年 3.能被4整除同时不能被100整除则是闰年
|
||||
* 3.能被4整除同时能被100整除则不是闰年
|
||||
*/
|
||||
Date d = strToDate(ddate);
|
||||
GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
|
||||
gc.setTime(d);
|
||||
int year = gc.get(Calendar.YEAR);
|
||||
if ((year % 400) == 0)
|
||||
return true;
|
||||
else if ((year % 4) == 0) {
|
||||
if ((year % 100) == 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回美国时间格式 26 Apr 2006
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String getEDate(String str) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Date strtodate = formatter.parse(str, pos);
|
||||
String j = strtodate.toString();
|
||||
String[] k = j.split(" ");
|
||||
return k[2] + k[1].toUpperCase() + k[5].substring(2, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个月的最后一天
|
||||
*
|
||||
* @param dat
|
||||
* @return
|
||||
*/
|
||||
public static String getEndDateOfMonth(String dat) {// yyyy-MM-dd
|
||||
String str = dat.substring(0, 8);
|
||||
String month = dat.substring(5, 7);
|
||||
int mon = Integer.parseInt(month);
|
||||
if (mon == 1 || mon == 3 || mon == 5 || mon == 7 || mon == 8 || mon == 10 || mon == 12) {
|
||||
str += "31";
|
||||
} else if (mon == 4 || mon == 6 || mon == 9 || mon == 11) {
|
||||
str += "30";
|
||||
} else {
|
||||
if (isLeapYear(dat)) {
|
||||
str += "29";
|
||||
} else {
|
||||
str += "28";
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断二个时间是否在同一个周
|
||||
*
|
||||
* @param date1
|
||||
* @param date2
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSameWeekDates(Date date1, Date date2) {
|
||||
Calendar cal1 = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal1.setTime(date1);
|
||||
cal2.setTime(date2);
|
||||
int subYear = cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR);
|
||||
if (0 == subYear) {
|
||||
if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
|
||||
return true;
|
||||
} else if (1 == subYear && 11 == cal2.get(Calendar.MONTH)) {
|
||||
// 如果12月的最后一周横跨来年第一周的话则最后一周即算做来年的第一周
|
||||
if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
|
||||
return true;
|
||||
} else if (-1 == subYear && 11 == cal1.get(Calendar.MONTH)) {
|
||||
if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 产生周序列,即得到当前时间所在的年度是第几周
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getSeqWeek() {
|
||||
Calendar c = Calendar.getInstance(Locale.CHINA);
|
||||
String week = Integer.toString(c.get(Calendar.WEEK_OF_YEAR));
|
||||
if (week.length() == 1)
|
||||
week = "0" + week;
|
||||
String year = Integer.toString(c.get(Calendar.YEAR));
|
||||
return year + week;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个日期所在的周的星期几的日期,如要找出2002年2月3日所在周的星期一是几号
|
||||
*
|
||||
* @param sdate
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
public static String getWeek(String sdate, String num) {
|
||||
// 再转换为时间
|
||||
Date dd = VeDate.strToDate(sdate);
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(dd);
|
||||
if (num.equals("1")) // 返回星期一所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
else if (num.equals("2")) // 返回星期二所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
|
||||
else if (num.equals("3")) // 返回星期三所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
|
||||
else if (num.equals("4")) // 返回星期四所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
|
||||
else if (num.equals("5")) // 返回星期五所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
|
||||
else if (num.equals("6")) // 返回星期六所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
|
||||
else if (num.equals("0")) // 返回星期日所在的日期
|
||||
c.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
return new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据一个日期,返回是星期几的字符串
|
||||
*
|
||||
* @param sdate
|
||||
* @return
|
||||
*/
|
||||
public static String getWeek(String sdate) {
|
||||
// 再转换为时间
|
||||
Date date = VeDate.strToDate(sdate);
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(date);
|
||||
// int hour=c.get(Calendar.DAY_OF_WEEK);
|
||||
// hour中存的就是星期几了,其范围 1~7
|
||||
// 1=星期日 7=星期六,其他类推
|
||||
return new SimpleDateFormat("EEEE").format(c.getTime());
|
||||
}
|
||||
|
||||
public static String getWeekStr(String sdate) {
|
||||
String str = "";
|
||||
str = VeDate.getWeek(sdate);
|
||||
if ("1".equals(str)) {
|
||||
str = "星期日";
|
||||
} else if ("2".equals(str)) {
|
||||
str = "星期一";
|
||||
} else if ("3".equals(str)) {
|
||||
str = "星期二";
|
||||
} else if ("4".equals(str)) {
|
||||
str = "星期三";
|
||||
} else if ("5".equals(str)) {
|
||||
str = "星期四";
|
||||
} else if ("6".equals(str)) {
|
||||
str = "星期五";
|
||||
} else if ("7".equals(str)) {
|
||||
str = "星期六";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 两个时间之间的天数
|
||||
*
|
||||
* @param date1
|
||||
* @param date2
|
||||
* @return
|
||||
*/
|
||||
public static long getDays(String date1, String date2) {
|
||||
if (date1 == null || date1.equals(""))
|
||||
return 0;
|
||||
if (date2 == null || date2.equals(""))
|
||||
return 0;
|
||||
// 转换为标准时间
|
||||
SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date = null;
|
||||
Date mydate = null;
|
||||
try {
|
||||
date = myFormatter.parse(date1);
|
||||
mydate = myFormatter.parse(date2);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
long day = (date.getTime() - mydate.getTime()) / (24 * 60 * 60 * 1000);
|
||||
return day;
|
||||
}
|
||||
|
||||
/**
|
||||
* 形成如下的日历 , 根据传入的一个时间返回一个结构 星期日 星期一 星期二 星期三 星期四 星期五 星期六 下面是当月的各个时间
|
||||
* 此函数返回该日历第一行星期日所在的日期
|
||||
*
|
||||
* @param sdate
|
||||
* @return
|
||||
*/
|
||||
public static String getNowMonth(String sdate) {
|
||||
// 取该时间所在月的一号
|
||||
sdate = sdate.substring(0, 8) + "01";
|
||||
|
||||
// 得到这个月的1号是星期几
|
||||
Date date = VeDate.strToDate(sdate);
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(date);
|
||||
int u = c.get(Calendar.DAY_OF_WEEK);
|
||||
String newday = VeDate.getNextDay(sdate, (1 - u) + "");
|
||||
return newday;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库主键 生成格式为yyyymmddhhmmss+k位随机数
|
||||
*
|
||||
* @param k 表示是取几位随机数,可以自己定
|
||||
*/
|
||||
|
||||
public static String getNo(int k) {
|
||||
return getUserDate("yyyyMMddHHmmss") + getRandom(k);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个随机数
|
||||
*
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public static String getRandom(int i) {
|
||||
Random jjj = new Random();
|
||||
// int suiJiShu = jjj.nextInt(9);
|
||||
if (i == 0)
|
||||
return "";
|
||||
String jj = "";
|
||||
for (int k = 0; k < i; k++) {
|
||||
jj = jj + jjj.nextInt(9);
|
||||
}
|
||||
return jj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date
|
||||
*/
|
||||
public static boolean RightDate(String date) {
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
|
||||
;
|
||||
if (date == null)
|
||||
return false;
|
||||
if (date.length() > 10) {
|
||||
sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
|
||||
} else {
|
||||
sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
}
|
||||
try {
|
||||
sdf.parse(date);
|
||||
} catch (ParseException pe) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
59
src/main/java/com/guahao/common/util/WxPayConstants.java
Normal file
59
src/main/java/com/guahao/common/util/WxPayConstants.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import sun.net.www.http.HttpClient;
|
||||
|
||||
/**
|
||||
* 常量
|
||||
*/
|
||||
public class WxPayConstants {
|
||||
|
||||
public enum SignType {
|
||||
MD5, HMACSHA256
|
||||
}
|
||||
|
||||
public static final String DOMAIN_API = "api.mch.weixin.qq.com";
|
||||
public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
|
||||
public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
|
||||
public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
|
||||
|
||||
|
||||
public static final String FAIL = "FAIL";
|
||||
public static final String SUCCESS = "SUCCESS";
|
||||
public static final String HMACSHA256 = "HMAC-SHA256";
|
||||
public static final String MD5 = "MD5";
|
||||
|
||||
public static final String FIELD_SIGN = "sign";
|
||||
public static final String FIELD_SIGN_TYPE = "sign_type";
|
||||
|
||||
public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9";
|
||||
public static final String USER_AGENT = WXPAYSDK_VERSION +
|
||||
" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
|
||||
") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();
|
||||
|
||||
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
|
||||
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
|
||||
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
|
||||
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
|
||||
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
|
||||
public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund";
|
||||
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
|
||||
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
|
||||
public static final String REPORT_URL_SUFFIX = "/payitil/report";
|
||||
public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl";
|
||||
public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
|
||||
|
||||
// sandbox
|
||||
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
|
||||
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
|
||||
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
|
||||
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
|
||||
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
|
||||
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
|
||||
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
|
||||
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
|
||||
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
|
||||
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
|
||||
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
|
||||
|
||||
}
|
||||
|
||||
228
src/main/java/com/guahao/common/util/WxPayDUtil.java
Normal file
228
src/main/java/com/guahao/common/util/WxPayDUtil.java
Normal file
@@ -0,0 +1,228 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.github.wxpay.sdk.WXPayConstants.SignType;
|
||||
import com.github.wxpay.sdk.WXPayUtil;
|
||||
import com.guahao.h5.reserve.domain.WxResult;
|
||||
import com.guahao.common.Exception.CustomException;
|
||||
import com.guahao.common.response.ResultCodeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* @author Mr.zs
|
||||
* @date 2025/3/18
|
||||
*/
|
||||
@Slf4j
|
||||
public class WxPayDUtil {
|
||||
public static final String WX_APP_ID = "wx45acd2b4907cb8f4";
|
||||
public static final String WX_SECRET = "895b90585c4698485c07e113711eac85";
|
||||
|
||||
/**
|
||||
* 获取微信AccessToken
|
||||
* @return
|
||||
*/
|
||||
public static String getAccessToken(){
|
||||
String accessUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&" +
|
||||
"&appid=" + WX_APP_ID + "&secret=" + WX_SECRET;
|
||||
String accessTokenStr = HttpClientUtil.doGet(accessUrl, null);
|
||||
log.debug("step 1 get accesstoken:" + accessTokenStr);
|
||||
|
||||
JSONObject jsonObject = JSON.parseObject(accessTokenStr);
|
||||
String accessToken = jsonObject.get("access_token").toString();
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param data 待签名数据
|
||||
* @param key API密钥
|
||||
* @return 签名
|
||||
*/
|
||||
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
|
||||
return generateSignature(data, key, SignType.MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
|
||||
*
|
||||
* @param data 待签名数据
|
||||
* @param key API密钥
|
||||
* @param signType 签名方式
|
||||
* @return 签名
|
||||
*/
|
||||
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
|
||||
Set<String> keySet = data.keySet();
|
||||
String[] keyArray = keySet.toArray(new String[keySet.size()]);
|
||||
Arrays.sort(keyArray);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String k : keyArray) {
|
||||
if (WxPayConstants.FIELD_SIGN.equals(k)) {
|
||||
continue; // 跳过 sign 字段
|
||||
}
|
||||
String value = data.get(k);
|
||||
if (value != null && value.length() > 0) {
|
||||
sb.append(k).append("=").append(value).append("&");
|
||||
}
|
||||
}
|
||||
sb.append("key=").append(key);
|
||||
log.info("genSigStr: " + sb.toString());
|
||||
// System.out.println("genSigStr: " + sb.toString());
|
||||
try {
|
||||
String sigStr = sb.toString();
|
||||
|
||||
if (SignType.MD5.equals(signType)) {
|
||||
String sign = MD5(sigStr).toUpperCase();
|
||||
log.info("Generated Sign: " + sign); // ✅ 打印生成的 sign
|
||||
return sign;
|
||||
} else if (SignType.HMACSHA256.equals(signType)) {
|
||||
String sign = HMACSHA256(sigStr, key);
|
||||
log.info("Generated Sign (HMAC): " + sign);
|
||||
return sign;
|
||||
} else {
|
||||
throw new Exception("Invalid sign_type: " + signType);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(); // ✅ 打印堆栈
|
||||
log.error("签名生成失败", e); // ✅ 记录错误日志
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 MD5
|
||||
*
|
||||
* @param data 待处理数据
|
||||
* @return MD5结果
|
||||
*/
|
||||
public static String MD5(String data) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] array = md.digest(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 HMACSHA256
|
||||
*
|
||||
* @param data 待处理数据
|
||||
* @param key 密钥
|
||||
* @return 加密结果
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String HMACSHA256(String data, String key) throws Exception {
|
||||
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
|
||||
sha256_HMAC.init(secret_key);
|
||||
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
// 微信回调签名校验
|
||||
public static WxResult wxPaySignatureValid(String xmlStr, String wxWechartkey) {
|
||||
boolean b;
|
||||
try {
|
||||
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
|
||||
if (map.isEmpty()) {
|
||||
throw new CustomException(ResultCodeEnum.REQUEST_ERROR.getCode().toString(), "微信异步回调结果失败,xml转map为空");
|
||||
}
|
||||
log.info("微信异步回调结果map:" + JSONUtil.toJsonStr(map));
|
||||
WxResult wxResult = JSON.parseObject(JSON.toJSONString(map), WxResult.class);
|
||||
log.info("微信异步回调结果:" + JSONUtil.toJsonStr(wxResult));
|
||||
// log.info("微信异步回调结果:" + JsonUtils.beanToJson(wxResult));
|
||||
// PassbackParams passbackParams = JsonUtils.jsonToBean(URLDecoder.decode(wxResult.getAttach(), "utf-8"), PassbackParams.class);
|
||||
|
||||
// 校验签名
|
||||
b = WXPayUtil.isSignatureValid(map, wxWechartkey);
|
||||
if (!b) {
|
||||
throw new CustomException(ResultCodeEnum.REQUEST_ERROR.getCode().toString(), "微信支付回调验签失败");
|
||||
}
|
||||
log.info("微信支付回调签名验证成功......");
|
||||
|
||||
return wxResult;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("微信支付回调验签失败,原因:" + e.getMessage());
|
||||
}
|
||||
throw new CustomException(ResultCodeEnum.REQUEST_ERROR.getCode().toString(), "微信验签失败");
|
||||
}
|
||||
/**
|
||||
* 微信单号制作
|
||||
*/
|
||||
public static String makeNo(String type) {
|
||||
return type+DateUtils.getTimesStamp()+ RandomUtil.randomNumbers(8);
|
||||
}
|
||||
/**
|
||||
* 元单位变成分单位
|
||||
*/
|
||||
public static String yuan2fen(BigDecimal money) {
|
||||
return money.multiply(new BigDecimal(100)).toPlainString();
|
||||
}
|
||||
public static String yuan2fen(String money) {
|
||||
return parseToBigDecimal( money).multiply(new BigDecimal(100)).toPlainString();
|
||||
}
|
||||
public static BigDecimal yuan2fenBigDecimal(BigDecimal money) {
|
||||
if (money == null) {
|
||||
throw new IllegalArgumentException("金额不能为空");
|
||||
}
|
||||
if (money.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new IllegalArgumentException("金额不能为负数");
|
||||
}
|
||||
|
||||
// RoundingMode.HALF_UP (四舍五入)
|
||||
BigDecimal scaledAmount = money.setScale(2, RoundingMode.HALF_UP);
|
||||
|
||||
// 转换为分(乘以100)
|
||||
BigDecimal fenAmount = scaledAmount.multiply(new BigDecimal("100"));
|
||||
|
||||
// 确保结果为整数(没有小数部分)
|
||||
return fenAmount.setScale(0, RoundingMode.UNNECESSARY);
|
||||
}
|
||||
public static BigDecimal yuan2fenBig(String money) {
|
||||
return parseToBigDecimal( money).multiply(new BigDecimal(100));
|
||||
}
|
||||
/**
|
||||
* 将string类型变为BigDecimal类型
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal parseToBigDecimal(String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(str.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("无法解析金额: " + str, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String yuanToFee(String yuan) {
|
||||
return new BigDecimal(yuan)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.setScale(0, RoundingMode.HALF_UP)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
289
src/main/java/com/guahao/common/util/WxPayUtil.java
Normal file
289
src/main/java/com/guahao/common/util/WxPayUtil.java
Normal file
@@ -0,0 +1,289 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.github.wxpay.sdk.WXPayUtil;
|
||||
import com.guahao.common.Exception.LogicException;
|
||||
import com.guahao.common.config.WxPayConfig;
|
||||
import com.guahao.h5.reserve.service.Reserve8Service;
|
||||
import com.guahao.common.util.WxPayDUtil;
|
||||
import com.guahao.h5.reserve.vo.WxPayVo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@Component
|
||||
public class WxPayUtil {
|
||||
private Logger log = LoggerFactory.getLogger(WxPayUtil.class);
|
||||
|
||||
@Autowired
|
||||
private WxPayConfig wxPayConfig;
|
||||
/**
|
||||
* 关闭微信支付订单
|
||||
* @param wxPayVo 支付参数对象
|
||||
* @return 关单结果
|
||||
*/
|
||||
public Map<String, String> closeOrder(WxPayVo wxPayVo) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
String outTradeNo = wxPayVo.getOutTradeNo();
|
||||
log.info("开始关单: {}",outTradeNo);
|
||||
// log.info("wxpayconfig:"+wxPayConfig.getAppId());
|
||||
try {
|
||||
// 构建请求参数
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("appid", wxPayConfig.getAppId());
|
||||
data.put("mch_id", wxPayConfig.getMchId());
|
||||
data.put("out_trade_no", outTradeNo);
|
||||
data.put("nonce_str", WXPayUtil.generateNonceStr());
|
||||
log.info("请求参数: {}", data);
|
||||
// 生成签名
|
||||
String sign = WXPayUtil.generateSignature(data, wxPayConfig.getApiKey());
|
||||
data.put("sign", sign);
|
||||
log.info("请求参数: {}", data);
|
||||
// 转换为XML
|
||||
String xmlParam = mapToXml(data);
|
||||
log.info("关单请求XML: {}", xmlParam);
|
||||
|
||||
URL url = new URL("https://api.mch.weixin.qq.com/pay/closeorder");
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 设置请求参数
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; WeChat Pay SDK)");
|
||||
connection.setDoOutput(true);
|
||||
connection.setDoInput(true);
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
// 写入请求体
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
os.write(xmlParam.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
int code = connection.getResponseCode();
|
||||
log.info("关单HTTP状态码: {}", code);
|
||||
|
||||
// 读取响应
|
||||
StringBuilder response = new StringBuilder();
|
||||
try (InputStream is = code == 200 ? connection.getInputStream() : connection.getErrorStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
String responseXml = response.toString();
|
||||
log.info("关单响应XML: {}", responseXml);
|
||||
|
||||
// 解析响应
|
||||
Map<String, String> resultMap = xmlToMap(responseXml);
|
||||
|
||||
// ✅ 关键:必须同时判断 return_code 和 result_code
|
||||
if ("SUCCESS".equals(resultMap.get("return_code"))
|
||||
&& "SUCCESS".equals(resultMap.get("result_code"))) {
|
||||
log.info("✅ 微信关单成功: {}", outTradeNo);
|
||||
return resultMap; // 返回 SUCCESS
|
||||
} else {
|
||||
// 业务失败
|
||||
String errorMsg = resultMap.get("return_msg");
|
||||
String errCodeDes = resultMap.get("err_code_des");
|
||||
log.error(" 微信关单失败: {} | {} | {}", outTradeNo, errorMsg, errCodeDes);
|
||||
result.put("return_code", "FAIL");
|
||||
result.put("return_msg", "关单失败: " + errorMsg + ", 详情: " + errCodeDes);
|
||||
return result;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// 正确写法:打印异常类名 + 消息 + 堆栈
|
||||
String errorMsg = e.getMessage();
|
||||
log.error(" 调用微信关单接口异常 [订单号: {}] [异常类型: {}] [异常消息: {}]",
|
||||
outTradeNo,
|
||||
e.getClass().getSimpleName(),
|
||||
errorMsg == null ? "null" : errorMsg,
|
||||
e); // e 放最后,用于输出堆栈
|
||||
|
||||
result.put("return_code", "FAIL");
|
||||
result.put("return_msg", "网络异常: " + (errorMsg == null ? e.getClass().getSimpleName() : errorMsg));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付统一下单
|
||||
*
|
||||
* @param wxPayVo 支付参数
|
||||
* @return 微信支付响应结果
|
||||
*/
|
||||
public Map<String, String> unifiedOrder(WxPayVo wxPayVo) throws Exception {
|
||||
// 构造统一下单请求参数
|
||||
Map<String, String> paramMap = new HashMap<>();
|
||||
paramMap.put("appid", wxPayConfig.getAppId());
|
||||
paramMap.put("mch_id", wxPayConfig.getMchId());
|
||||
paramMap.put("nonce_str", wxPayVo.getNonceStr());
|
||||
paramMap.put("body", wxPayVo.getBody());
|
||||
paramMap.put("out_trade_no", wxPayVo.getOutTradeNo());
|
||||
paramMap.put("total_fee", wxPayVo.getTotalFee().toString());
|
||||
paramMap.put("spbill_create_ip", wxPayVo.getSpbillCreateIp());
|
||||
paramMap.put("notify_url", "https://nxwj.btlsoln.com/nxgzh/pay/wxpay/notify");
|
||||
paramMap.put("trade_type", "JSAPI");
|
||||
paramMap.put("openid", wxPayVo.getOpenid());
|
||||
|
||||
// log.info("进入1generateSignature");
|
||||
log.info("签名参数: " + JSONUtil.toJsonStr(paramMap));
|
||||
// 生成签名
|
||||
String sign = WxPayDUtil.generateSignature(paramMap, wxPayConfig.getApiKey());
|
||||
paramMap.put("sign", sign);
|
||||
|
||||
// 转换为XML格式
|
||||
String xmlParam = mapToXml(paramMap);
|
||||
log.info("xmlParam: " + xmlParam);
|
||||
|
||||
HttpURLConnection connection = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
// 1. 创建 URL 对象
|
||||
URL url = new URL("https://api.mch.weixin.qq.com/pay/unifiedorder");
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 2. 设置请求参数
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; WeChat Pay SDK)");
|
||||
connection.setDoOutput(true);
|
||||
connection.setDoInput(true);
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
// 3. 写入请求体(XML)
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
byte[] input = xmlParam.getBytes("UTF-8");
|
||||
os.write(input, 0, input.length);
|
||||
}
|
||||
|
||||
// 4. 获取响应码
|
||||
int code = connection.getResponseCode();
|
||||
log.info("code:" + code);
|
||||
|
||||
if (code == 200) {
|
||||
// 5. 读取响应内容
|
||||
StringBuilder response = new StringBuilder();
|
||||
try (InputStream is = connection.getInputStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
String responseXml = response.toString();
|
||||
log.info("微信支付响应XML: " + responseXml);
|
||||
|
||||
// 解析返回结果
|
||||
Map<String, String> resultMap = xmlToMap(responseXml);
|
||||
|
||||
// 检查业务结果
|
||||
if ("SUCCESS".equals(resultMap.get("return_code")) &&
|
||||
"SUCCESS".equals(resultMap.get("result_code"))) {
|
||||
|
||||
// 构造返回给前端的js-sdk参数
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("appId", wxPayConfig.getAppId());
|
||||
result.put("timeStamp", DateUtils.getTimesStamp());
|
||||
result.put("nonceStr", wxPayVo.getNonceStr());
|
||||
result.put("package", "prepay_id=" + resultMap.get("prepay_id"));
|
||||
result.put("signType", "MD5");
|
||||
// log.info("进入2generateSignature");
|
||||
result.put("paySign", WxPayDUtil.generateSignature(result, wxPayConfig.getApiKey()));
|
||||
result.put("outTradeNo", wxPayVo.getOutTradeNo());
|
||||
return result; // 包含prepay_id等信息
|
||||
} else {
|
||||
String returnMsg = resultMap.get("return_msg");
|
||||
String errCodeDes = resultMap.get("err_code_des");
|
||||
throw new LogicException("微信支付统一下单失败:" + returnMsg + (errCodeDes != null ? ", 详细: " + errCodeDes : ""));
|
||||
}
|
||||
} else {
|
||||
// 读取错误流(如 4xx/5xx)
|
||||
try (InputStream es = connection.getErrorStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(es, "UTF-8"))) {
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
errorMsg.append(line);
|
||||
}
|
||||
log.error("HTTP错误响应: " + errorMsg);
|
||||
throw new LogicException("微信支付统一下单失败:HTTP状态码 " + code + ", 响应: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("调用微信统一下单接口异常", e);
|
||||
throw new RuntimeException("网络请求失败: " + e.getMessage(), e);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect(); // 释放连接
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map转XML
|
||||
*/
|
||||
public String mapToXml(Map<String, String> paramMap) {
|
||||
try {
|
||||
return WXPayUtil.mapToXml(paramMap);
|
||||
} catch (Exception e) {
|
||||
// 如果WXPayUtil不可用,使用备用实现
|
||||
return mapToXmlFallback(paramMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 备用的Map转XML实现
|
||||
*/
|
||||
private String mapToXmlFallback(Map<String, String> paramMap) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<xml>");
|
||||
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
// 对值进行XML转义
|
||||
String escapedValue = escapeXml(value);
|
||||
sb.append("<").append(key).append(">");
|
||||
sb.append("<![CDATA[").append(escapedValue).append("]]>");
|
||||
sb.append("</").append(key).append(">");
|
||||
}
|
||||
sb.append("</xml>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* XML转义
|
||||
*/
|
||||
private String escapeXml(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
return str.replace("]]>", "]]]]><![CDATA[>");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析微信支付返回的XML
|
||||
*/
|
||||
public Map<String, String> xmlToMap(String xmlStr) throws Exception {
|
||||
return WXPayUtil.xmlToMap(xmlStr);
|
||||
}
|
||||
|
||||
}
|
||||
159
src/main/java/com/guahao/common/util/WxPayV2ApiUtil-README.md
Normal file
159
src/main/java/com/guahao/common/util/WxPayV2ApiUtil-README.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# 微信支付V2 JSAPI 整合工具类使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
`WxPayV2ApiUtil` 是一个整合的微信支付工具类,旨在将分散在项目中的微信支付相关功能集中管理,使代码更加规整、易于维护和使用。
|
||||
|
||||
## 主要优势
|
||||
|
||||
1. **功能集中化**:将所有微信支付相关功能整合到一个类中
|
||||
2. **结构清晰**:按照功能模块对方法进行分类组织
|
||||
3. **命名规范**:使用更具描述性的方法名
|
||||
4. **代码复用**:避免重复代码,提高开发效率
|
||||
5. **易于扩展**:统一的结构便于未来添加新功能
|
||||
|
||||
## 功能模块分类
|
||||
|
||||
### 1. 核心API方法
|
||||
|
||||
这些是直接与微信支付接口交互的方法:
|
||||
|
||||
- `unifiedOrderJSAPI(WxPayVo wxPayVo)`:发起JSAPI支付统一下单请求
|
||||
- `closeOrder(String outTradeNo)`:关闭未支付的订单
|
||||
- `queryOrder(String outTradeNo)`:查询订单状态
|
||||
|
||||
### 2. 签名与验证
|
||||
|
||||
处理微信支付的签名生成和验证:
|
||||
|
||||
- `generateSignature(Map<String, String> data, String key)`:生成签名
|
||||
- `generateSignature(Map<String, String> data, String key, SignType signType)`:指定签名方式生成签名
|
||||
- `verifyNotifySignature(String xmlStr, String apiKey)`:验证微信回调签名
|
||||
|
||||
### 3. 数据转换
|
||||
|
||||
处理XML和Map之间的转换:
|
||||
|
||||
- `mapToXml(Map<String, String> paramMap)`:将Map转换为XML格式
|
||||
- `xmlToMap(String xmlStr)`:将XML字符串解析为Map
|
||||
|
||||
### 4. 金额处理
|
||||
|
||||
处理金额单位转换:
|
||||
|
||||
- `yuan2fen(BigDecimal money)`:元转分(字符串)
|
||||
- `yuan2fen(String money)`:元字符串转分字符串
|
||||
- `yuan2fenBigDecimal(BigDecimal money)`:元转分(BigDecimal)
|
||||
- `yuanToFee(String yuan)`:元转微信支付金额(保留整数)
|
||||
|
||||
### 5. 订单号生成
|
||||
|
||||
- `generateOrderNo(String type, String patientid)`:生成微信支付订单号
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 1. 发起JSAPI支付
|
||||
|
||||
```java
|
||||
// 1. 注入工具类
|
||||
@Autowired
|
||||
private WxPayV2ApiUtil wxPayV2ApiUtil;
|
||||
|
||||
// 2. 创建支付参数对象
|
||||
WxPayVo wxPayVo = new WxPayVo();
|
||||
wxPayVo.setAppId("wx45acd2b4907cb8f4"); // 可从配置中获取
|
||||
wxPayVo.setMchId("1726317574"); // 可从配置中获取
|
||||
wxPayVo.setOpenid("用户的openid");
|
||||
wxPayVo.setNonceStr(WXPayUtil.generateNonceStr());
|
||||
wxPayVo.setBody("商品描述");
|
||||
wxPayVo.setOutTradeNo(WxPayV2ApiUtil.generateOrderNo("TL", "患者ID"));
|
||||
wxPayVo.setTotalFee(new BigDecimal("1")); // 单位:元
|
||||
wxPayVo.setSpbillCreateIp("127.0.0.1");
|
||||
|
||||
// 3. 调用统一下单接口
|
||||
Map<String, String> payParams = wxPayV2ApiUtil.unifiedOrderJSAPI(wxPayVo);
|
||||
|
||||
// 4. 将payParams返回给前端,用于调起微信支付
|
||||
```
|
||||
|
||||
### 2. 处理微信支付回调
|
||||
|
||||
```java
|
||||
@PostMapping("/wxpay/notify")
|
||||
public String wxPayNotify(@RequestBody String xmlStr) {
|
||||
try {
|
||||
// 验证回调签名
|
||||
WxResult wxResult = WxPayV2ApiUtil.verifyNotifySignature(xmlStr, wxPayConfig.getApiKey());
|
||||
|
||||
// 检查支付结果
|
||||
if ("SUCCESS".equals(wxResult.getReturn_code()) && "SUCCESS".equals(wxResult.getResult_code())) {
|
||||
// 处理业务逻辑
|
||||
String outTradeNo = wxResult.getOut_trade_no();
|
||||
// 处理订单...
|
||||
}
|
||||
|
||||
// 构建成功响应
|
||||
Map<String, String> resultMap = new HashMap<>();
|
||||
resultMap.put("return_code", "SUCCESS");
|
||||
resultMap.put("return_msg", "");
|
||||
return WXPayUtil.mapToXml(resultMap);
|
||||
} catch (Exception e) {
|
||||
log.error("微信支付回调处理异常", e);
|
||||
// 返回失败响应
|
||||
return WxPayV2ApiUtil.buildFailResponse("处理异常");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 关闭订单
|
||||
|
||||
```java
|
||||
// 关闭指定订单
|
||||
Map<String, String> result = wxPayV2ApiUtil.closeOrder("商户订单号");
|
||||
if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
|
||||
// 关单成功
|
||||
log.info("订单关闭成功");
|
||||
} else {
|
||||
// 关单失败
|
||||
log.error("订单关闭失败: {}", result.get("return_msg"));
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 查询订单状态
|
||||
|
||||
```java
|
||||
try {
|
||||
// 查询订单状态
|
||||
Map<String, String> orderInfo = wxPayV2ApiUtil.queryOrder("商户订单号");
|
||||
String tradeState = orderInfo.get("trade_state");
|
||||
|
||||
if ("SUCCESS".equals(tradeState)) {
|
||||
// 订单已支付
|
||||
} else if ("NOTPAY".equals(tradeState)) {
|
||||
// 订单未支付
|
||||
} else if ("CLOSED".equals(tradeState)) {
|
||||
// 订单已关闭
|
||||
}
|
||||
// 其他状态处理...
|
||||
} catch (Exception e) {
|
||||
log.error("查询订单状态失败", e);
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保在使用前已正确配置`WxPayConfig`中的参数(appId, mchId, apiKey等)
|
||||
2. 所有与微信支付相关的敏感信息(如API密钥)应妥善保管,避免泄露
|
||||
3. 处理回调通知时,应先验证签名,再处理业务逻辑
|
||||
4. 建议使用分布式锁避免重复处理回调通知
|
||||
5. 涉及金额计算时,建议使用BigDecimal类型以避免精度问题
|
||||
|
||||
## 替换原有实现建议
|
||||
|
||||
为了逐步过渡到新的工具类,建议按以下步骤进行:
|
||||
|
||||
1. 在新功能开发中优先使用`WxPayV2ApiUtil`
|
||||
2. 对现有代码进行重构,逐步替换为新的工具类方法
|
||||
3. 测试确保功能正常后,可考虑废弃原有的`WxPayUtil`和`WxPayDUtil`
|
||||
|
||||
通过使用这个整合的工具类,可以使您的微信支付代码更加规范、易于维护,提高开发效率。
|
||||
602
src/main/java/com/guahao/common/util/WxPayV2ApiUtil.java
Normal file
602
src/main/java/com/guahao/common/util/WxPayV2ApiUtil.java
Normal file
@@ -0,0 +1,602 @@
|
||||
package com.guahao.common.util;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.github.wxpay.sdk.WXPayConstants.SignType;
|
||||
import com.github.wxpay.sdk.WXPayUtil;
|
||||
import com.guahao.common.Exception.CustomException;
|
||||
import com.guahao.common.Exception.LogicException;
|
||||
import com.guahao.common.config.WxPayConfig;
|
||||
import com.guahao.h5.reserve.domain.WxResult;
|
||||
import com.guahao.h5.reserve.vo.WxPayVo;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 微信支付V2 JSAPI 整合工具类
|
||||
* 整合微信支付相关的所有功能,包括:
|
||||
* 1. 统一下单接口
|
||||
* 2. 订单查询接口
|
||||
* 3. 关单接口
|
||||
* 4. 退款接口
|
||||
* 5. 签名生成与验证
|
||||
* 6. 金额单位转换
|
||||
* 7. 订单号生成
|
||||
*/
|
||||
@Component
|
||||
public class WxPayV2ApiUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(WxPayV2ApiUtil.class);
|
||||
|
||||
@Autowired
|
||||
private WxPayConfig wxPayConfig;
|
||||
|
||||
// ============================ 核心API方法 ============================
|
||||
|
||||
/**
|
||||
* 微信支付统一下单接口 - JSAPI支付专用
|
||||
* @param wxPayVo 支付参数对象
|
||||
* @return 微信支付响应结果,包含前端调起支付所需的参数
|
||||
*/
|
||||
public Map<String, String> unifiedOrderJSAPI(WxPayVo wxPayVo) throws Exception {
|
||||
// 构造统一下单请求参数
|
||||
Map<String, String> paramMap = new HashMap<>();
|
||||
paramMap.put("appid", wxPayConfig.getAppId());
|
||||
paramMap.put("mch_id", wxPayConfig.getMchId());
|
||||
paramMap.put("nonce_str", wxPayVo.getNonceStr());
|
||||
paramMap.put("body", wxPayVo.getBody());
|
||||
paramMap.put("out_trade_no", wxPayVo.getOutTradeNo());
|
||||
paramMap.put("total_fee", wxPayVo.getTotalFee().toString());
|
||||
paramMap.put("spbill_create_ip", wxPayVo.getSpbillCreateIp());
|
||||
paramMap.put("notify_url", "https://nxwj.btlsoln.com/nxgzh/pay/wxpay/notify");
|
||||
paramMap.put("trade_type", "JSAPI");
|
||||
paramMap.put("openid", wxPayVo.getOpenid());
|
||||
|
||||
log.info("签名参数: {}", JSONUtil.toJsonStr(paramMap));
|
||||
// 生成签名
|
||||
String sign = generateSignature(paramMap, wxPayConfig.getApiKey());
|
||||
paramMap.put("sign", sign);
|
||||
|
||||
// 转换为XML格式
|
||||
String xmlParam = mapToXml(paramMap);
|
||||
log.info("xmlParam: {}", xmlParam);
|
||||
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
// 创建URL对象并发送请求
|
||||
URL url = new URL(WxPayConstants.DOMAIN_API + WxPayConstants.UNIFIEDORDER_URL_SUFFIX);
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 设置请求参数
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
connection.setRequestProperty("User-Agent", WxPayConstants.USER_AGENT);
|
||||
connection.setDoOutput(true);
|
||||
connection.setDoInput(true);
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
// 写入请求体(XML)
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
byte[] input = xmlParam.getBytes("UTF-8");
|
||||
os.write(input, 0, input.length);
|
||||
}
|
||||
|
||||
// 获取响应码
|
||||
int code = connection.getResponseCode();
|
||||
log.info("HTTP状态码: {}", code);
|
||||
|
||||
if (code == 200) {
|
||||
// 读取响应内容
|
||||
StringBuilder response = new StringBuilder();
|
||||
try (InputStream is = connection.getInputStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
String responseXml = response.toString();
|
||||
log.info("微信支付响应XML: {}", responseXml);
|
||||
|
||||
// 解析返回结果
|
||||
Map<String, String> resultMap = xmlToMap(responseXml);
|
||||
|
||||
// 检查业务结果
|
||||
if ("SUCCESS".equals(resultMap.get("return_code")) &&
|
||||
"SUCCESS".equals(resultMap.get("result_code"))) {
|
||||
|
||||
// 构造返回给前端的js-sdk参数
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("appId", wxPayConfig.getAppId());
|
||||
result.put("timeStamp", DateUtils.getTimesStamp());
|
||||
result.put("nonceStr", wxPayVo.getNonceStr());
|
||||
result.put("package", "prepay_id=" + resultMap.get("prepay_id"));
|
||||
result.put("signType", "MD5");
|
||||
result.put("paySign", generateSignature(result, wxPayConfig.getApiKey()));
|
||||
result.put("outTradeNo", wxPayVo.getOutTradeNo());
|
||||
return result; // 包含prepay_id等信息
|
||||
} else {
|
||||
String returnMsg = resultMap.get("return_msg");
|
||||
String errCodeDes = resultMap.get("err_code_des");
|
||||
throw new LogicException("微信支付统一下单失败:" + returnMsg + (errCodeDes != null ? ", 详细: " + errCodeDes : ""));
|
||||
}
|
||||
} else {
|
||||
// 读取错误流(如 4xx/5xx)
|
||||
try (InputStream es = connection.getErrorStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(es, "UTF-8"))) {
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
errorMsg.append(line);
|
||||
}
|
||||
log.error("HTTP错误响应: {}", errorMsg);
|
||||
throw new LogicException("微信支付统一下单失败:HTTP状态码 " + code + ", 响应: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("调用微信统一下单接口异常", e);
|
||||
throw new RuntimeException("网络请求失败: " + e.getMessage(), e);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect(); // 释放连接
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭微信支付订单
|
||||
* @param outTradeNo 商户订单号
|
||||
* @return 关单结果
|
||||
*/
|
||||
public Map<String, String> closeOrder(String outTradeNo) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
log.info("开始关单: {}", outTradeNo);
|
||||
try {
|
||||
// 构建请求参数
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("appid", wxPayConfig.getAppId());
|
||||
data.put("mch_id", wxPayConfig.getMchId());
|
||||
data.put("out_trade_no", outTradeNo);
|
||||
data.put("nonce_str", WXPayUtil.generateNonceStr());
|
||||
log.info("请求参数: {}", data);
|
||||
// 生成签名
|
||||
String sign = generateSignature(data, wxPayConfig.getApiKey());
|
||||
data.put("sign", sign);
|
||||
log.info("请求参数: {}", data);
|
||||
// 转换为XML
|
||||
String xmlParam = mapToXml(data);
|
||||
log.info("关单请求XML: {}", xmlParam);
|
||||
|
||||
URL url = new URL(WxPayConstants.DOMAIN_API + WxPayConstants.CLOSEORDER_URL_SUFFIX);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 设置请求参数
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
connection.setRequestProperty("User-Agent", WxPayConstants.USER_AGENT);
|
||||
connection.setDoOutput(true);
|
||||
connection.setDoInput(true);
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
// 写入请求体
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
os.write(xmlParam.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
int code = connection.getResponseCode();
|
||||
log.info("关单HTTP状态码: {}", code);
|
||||
|
||||
// 读取响应
|
||||
StringBuilder response = new StringBuilder();
|
||||
try (InputStream is = code == 200 ? connection.getInputStream() : connection.getErrorStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
String responseXml = response.toString();
|
||||
log.info("关单响应XML: {}", responseXml);
|
||||
|
||||
// 解析响应
|
||||
Map<String, String> resultMap = xmlToMap(responseXml);
|
||||
|
||||
// 关键:必须同时判断 return_code 和 result_code
|
||||
if ("SUCCESS".equals(resultMap.get("return_code"))
|
||||
&& "SUCCESS".equals(resultMap.get("result_code"))) {
|
||||
log.info("✅ 微信关单成功: {}", outTradeNo);
|
||||
return resultMap; // 返回 SUCCESS
|
||||
} else {
|
||||
// 业务失败
|
||||
String errorMsg = resultMap.get("return_msg");
|
||||
String errCodeDes = resultMap.get("err_code_des");
|
||||
log.error(" 微信关单失败: {} | {} | {}", outTradeNo, errorMsg, errCodeDes);
|
||||
result.put("return_code", "FAIL");
|
||||
result.put("return_msg", "关单失败: " + errorMsg + ", 详情: " + errCodeDes);
|
||||
return result;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// 正确写法:打印异常类名 + 消息 + 堆栈
|
||||
String errorMsg = e.getMessage();
|
||||
log.error(" 调用微信关单接口异常 [订单号: {}] [异常类型: {}] [异常消息: {}]",
|
||||
outTradeNo,
|
||||
e.getClass().getSimpleName(),
|
||||
errorMsg == null ? "null" : errorMsg,
|
||||
e); // e 放最后,用于输出堆栈
|
||||
|
||||
result.put("return_code", "FAIL");
|
||||
result.put("return_msg", "网络异常: " + (errorMsg == null ? e.getClass().getSimpleName() : errorMsg));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询微信支付订单状态
|
||||
* @param outTradeNo 商户订单号
|
||||
* @return 订单查询结果
|
||||
*/
|
||||
public Map<String, String> queryOrder(String outTradeNo) throws Exception {
|
||||
log.info("查询微信支付订单: {}", outTradeNo);
|
||||
|
||||
// 构建请求参数
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("appid", wxPayConfig.getAppId());
|
||||
data.put("mch_id", wxPayConfig.getMchId());
|
||||
data.put("out_trade_no", outTradeNo);
|
||||
data.put("nonce_str", WXPayUtil.generateNonceStr());
|
||||
|
||||
// 生成签名
|
||||
String sign = generateSignature(data, wxPayConfig.getApiKey());
|
||||
data.put("sign", sign);
|
||||
|
||||
// 转换为XML
|
||||
String xmlParam = mapToXml(data);
|
||||
log.info("查询订单请求XML: {}", xmlParam);
|
||||
|
||||
// 发送请求
|
||||
URL url = new URL(WxPayConstants.DOMAIN_API + WxPayConstants.ORDERQUERY_URL_SUFFIX);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 设置请求参数
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
connection.setRequestProperty("User-Agent", WxPayConstants.USER_AGENT);
|
||||
connection.setDoOutput(true);
|
||||
connection.setDoInput(true);
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
// 写入请求体
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
os.write(xmlParam.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
int code = connection.getResponseCode();
|
||||
log.info("查询订单HTTP状态码: {}", code);
|
||||
|
||||
// 读取响应
|
||||
StringBuilder response = new StringBuilder();
|
||||
try (InputStream is = code == 200 ? connection.getInputStream() : connection.getErrorStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
String responseXml = response.toString();
|
||||
log.info("查询订单响应XML: {}", responseXml);
|
||||
|
||||
// 解析响应
|
||||
Map<String, String> resultMap = xmlToMap(responseXml);
|
||||
|
||||
// 检查通信状态
|
||||
if (!"SUCCESS".equals(resultMap.get("return_code"))) {
|
||||
String returnMsg = resultMap.get("return_msg");
|
||||
throw new CustomException("500", "通信失败:" + returnMsg);
|
||||
}
|
||||
|
||||
// 检查业务结果
|
||||
if (!"SUCCESS".equals(resultMap.get("result_code"))) {
|
||||
String errCode = resultMap.get("err_code");
|
||||
String errCodeDes = resultMap.get("err_code_des");
|
||||
throw new CustomException(errCode, "查询失败:" + errCodeDes);
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// ============================ 工具方法 ============================
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param data 待签名数据
|
||||
* @param key API密钥
|
||||
* @return 签名
|
||||
*/
|
||||
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
|
||||
return generateSignature(data, key, SignType.MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
|
||||
* @param data 待签名数据
|
||||
* @param key API密钥
|
||||
* @param signType 签名方式
|
||||
* @return 签名
|
||||
*/
|
||||
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
|
||||
Set<String> keySet = data.keySet();
|
||||
String[] keyArray = keySet.toArray(new String[keySet.size()]);
|
||||
Arrays.sort(keyArray);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String k : keyArray) {
|
||||
if (WxPayConstants.FIELD_SIGN.equals(k)) {
|
||||
continue; // 跳过 sign 字段
|
||||
}
|
||||
String value = data.get(k);
|
||||
if (value != null && value.length() > 0) {
|
||||
sb.append(k).append("=").append(value).append("&");
|
||||
}
|
||||
}
|
||||
sb.append("key=").append(key);
|
||||
log.info("genSigStr: {}", sb.toString());
|
||||
|
||||
try {
|
||||
String sigStr = sb.toString();
|
||||
|
||||
if (SignType.MD5.equals(signType)) {
|
||||
String sign = MD5(sigStr).toUpperCase();
|
||||
log.info("Generated Sign: {}", sign);
|
||||
return sign;
|
||||
} else if (SignType.HMACSHA256.equals(signType)) {
|
||||
String sign = HMACSHA256(sigStr, key);
|
||||
log.info("Generated Sign (HMAC): {}", sign);
|
||||
return sign;
|
||||
} else {
|
||||
throw new Exception("Invalid sign_type: " + signType);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("签名生成失败", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 MD5
|
||||
* @param data 待处理数据
|
||||
* @return MD5结果
|
||||
*/
|
||||
public static String MD5(String data) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] array = md.digest(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 HMACSHA256
|
||||
* @param data 待处理数据
|
||||
* @param key 密钥
|
||||
* @return 加密结果
|
||||
*/
|
||||
public static String HMACSHA256(String data, String key) throws Exception {
|
||||
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
|
||||
sha256_HMAC.init(secret_key);
|
||||
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信回调签名校验
|
||||
* @param xmlStr 微信回调XML数据
|
||||
* @param apiKey API密钥
|
||||
* @return 解析后的回调结果对象
|
||||
*/
|
||||
public static WxResult verifyNotifySignature(String xmlStr, String apiKey) {
|
||||
try {
|
||||
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
|
||||
if (map.isEmpty()) {
|
||||
throw new CustomException("400", "微信异步回调结果失败,xml转map为空");
|
||||
}
|
||||
log.info("微信异步回调结果map:{}", JSONUtil.toJsonStr(map));
|
||||
|
||||
// 校验签名
|
||||
boolean isValid = WXPayUtil.isSignatureValid(map, apiKey);
|
||||
if (!isValid) {
|
||||
throw new CustomException("400", "微信支付回调验签失败");
|
||||
}
|
||||
log.info("微信支付回调签名验证成功......");
|
||||
|
||||
// 转换为对象
|
||||
WxResult wxResult = new WxResult();
|
||||
// 使用反射或其他方式将map转换为对象
|
||||
// 这里简化处理,实际项目中可能需要更复杂的转换逻辑
|
||||
wxResult.setReturn_code(map.get("return_code"));
|
||||
wxResult.setResult_code(map.get("result_code"));
|
||||
wxResult.setOut_trade_no(map.get("out_trade_no"));
|
||||
wxResult.setTransaction_id(map.get("transaction_id"));
|
||||
wxResult.setTotal_fee(map.get("total_fee"));
|
||||
wxResult.setTime_end(map.get("time_end"));
|
||||
|
||||
return wxResult;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("微信支付回调验签失败,原因:{}", e.getMessage());
|
||||
throw new CustomException("500", "微信验签失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ 数据转换方法 ============================
|
||||
|
||||
/**
|
||||
* Map转XML
|
||||
*/
|
||||
public String mapToXml(Map<String, String> paramMap) {
|
||||
try {
|
||||
return WXPayUtil.mapToXml(paramMap);
|
||||
} catch (Exception e) {
|
||||
// 如果WXPayUtil不可用,使用备用实现
|
||||
return mapToXmlFallback(paramMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 备用的Map转XML实现
|
||||
*/
|
||||
private String mapToXmlFallback(Map<String, String> paramMap) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<xml>");
|
||||
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
// 对值进行XML转义
|
||||
String escapedValue = escapeXml(value);
|
||||
sb.append("<").append(key).append(">").append("<![CDATA[")
|
||||
.append(escapedValue).append("]]>")
|
||||
.append("</").append(key).append(">");
|
||||
}
|
||||
sb.append("</xml>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* XML转义
|
||||
*/
|
||||
private String escapeXml(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
return str.replace("]]>", "]]]]><![CDATA[>");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析微信支付返回的XML
|
||||
*/
|
||||
public Map<String, String> xmlToMap(String xmlStr) throws Exception {
|
||||
return WXPayUtil.xmlToMap(xmlStr);
|
||||
}
|
||||
|
||||
// ============================ 金额处理方法 ============================
|
||||
|
||||
/**
|
||||
* 元单位变成分单位
|
||||
*/
|
||||
public static String yuan2fen(BigDecimal money) {
|
||||
return money.multiply(new BigDecimal(100)).toPlainString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 元单位字符串变成分单位字符串
|
||||
*/
|
||||
public static String yuan2fen(String money) {
|
||||
return parseToBigDecimal(money).multiply(new BigDecimal(100)).toPlainString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 元单位变成分单位(BigDecimal)
|
||||
*/
|
||||
public static BigDecimal yuan2fenBigDecimal(BigDecimal money) {
|
||||
if (money == null) {
|
||||
throw new IllegalArgumentException("金额不能为空");
|
||||
}
|
||||
if (money.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new IllegalArgumentException("金额不能为负数");
|
||||
}
|
||||
|
||||
// 截断到两位小数(不四舍五入)
|
||||
BigDecimal scaledAmount = money.setScale(2, RoundingMode.DOWN);
|
||||
|
||||
// 转换为分(乘以100)
|
||||
BigDecimal fenAmount = scaledAmount.multiply(new BigDecimal("100"));
|
||||
|
||||
// 确保结果为整数(没有小数部分)
|
||||
return fenAmount.setScale(0, RoundingMode.UNNECESSARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元单位字符串变成分单位(BigDecimal)
|
||||
*/
|
||||
public static BigDecimal yuan2fenBig(String money) {
|
||||
return parseToBigDecimal(money).multiply(new BigDecimal(100));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将string类型变为BigDecimal类型
|
||||
*/
|
||||
public static BigDecimal parseToBigDecimal(String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(str.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("无法解析金额: " + str, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 元转微信支付金额(保留整数,四舍五入)
|
||||
*/
|
||||
public static String yuanToFee(String yuan) {
|
||||
return new BigDecimal(yuan)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.setScale(0, RoundingMode.HALF_UP)
|
||||
.toString();
|
||||
}
|
||||
|
||||
// ============================ 订单号生成 ============================
|
||||
|
||||
/**
|
||||
* 微信单号制作
|
||||
* @param type 订单类型前缀
|
||||
* @param patientid 患者ID
|
||||
* @return 生成的订单号
|
||||
*/
|
||||
public static String generateOrderNo(String type, String patientid) {
|
||||
return type + DateUtils.getTimesStamp() + patientid + cn.hutool.core.util.RandomUtil.randomNumbers(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建微信支付失败响应
|
||||
* @param message 失败消息
|
||||
* @return XML格式的失败响应
|
||||
*/
|
||||
public static String buildFailResponse(String message) {
|
||||
try {
|
||||
Map<String, String> resultMap = new HashMap<>();
|
||||
resultMap.put("return_code", "FAIL");
|
||||
resultMap.put("return_msg", message);
|
||||
return WXPayUtil.mapToXml(resultMap);
|
||||
} catch (Exception e) {
|
||||
log.error("构建失败响应异常", e);
|
||||
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[" + message + "]]></return_msg></xml>";
|
||||
}
|
||||
}
|
||||
}
|
||||
2219
src/main/java/com/guahao/common/util/XmlUtil.java
Normal file
2219
src/main/java/com/guahao/common/util/XmlUtil.java
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user