微信支付接口与回调接口

微信支付:

/**
	 * 调用统一下单接口,并组装生成支付所需参数对象.
	 *
	 * @param orderInfoVO 统一下单请求参数
	 */
	@PostMapping("/unifiedOrder")
	public R unifiedOrder(HttpServletRequest request, @RequestBody DeviceOrdersEntity orderInfoVO) {
		return orderInfoService.unifiedOrder(request, orderInfoVO);
	}


import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.xhsoft.device.entity.DeviceOrdersEntity;
import com.xhsoft.device.feign.IDeviceOrdersClient;
import com.xhsoft.mp.config.PayConfig;
import com.xhsoft.mp.entity.OrderInfo;
import com.xhsoft.mp.entity.WechatUser;
import com.xhsoft.mp.service.OrderInfoService;
import com.xhsoft.mp.util.MoneyUtils;
import com.xhsoft.mp.util.WeChatPayUrl;
import com.xhsoft.mp.util.WxChatPayCommonUtil;
import com.xhsoft.mp.vo.OrderInfoVO;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import org.springblade.common.constant.HeaderConstant;
import org.springblade.core.social.props.SocialProperties;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.ObjectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
@Slf4j
@Service
public class OrderInfoServiceImpl implements OrderInfoService {

	@Resource
	private SocialProperties socialProperties;

	@Resource
	private IDeviceOrdersClient deviceOrdersClient;

	@Resource
	private WechatUserServiceImpl wechatUserService;

	@Autowired
	private PayConfig payConfig;
	@Override
	public R unifiedOrder(HttpServletRequest request, DeviceOrdersEntity orderInfoVO) {
		String openid = request.getHeader(HeaderConstant.TOKEN);
		WechatUser user = wechatUserService.getOne(Wrappers.lambdaQuery(WechatUser.class).eq(WechatUser::getOpenid,openid));
		if (user == null) {
			return R.fail("用户信息不存在");
		}


		AuthConfig config = socialProperties.getOauth().get(AuthDefaultSource.WECHAT_MP);
		String clientId = config.getClientId(); // 小程序id



		//根据typeId查询支付金额  根据自己的业务逻辑自行处理
//		SysFunctionType sysFunctionType = sysFunctionTypeMapper.selectFunctionTypeById(orderInfoVO.getTypeId());
		//通过customId 查询用户信息  根据自己的业务逻辑自行处理
//		SysCustom sysCustom = sysCustomMapper.selectSysCustomById(orderInfoVO.getCustomId());

		//根据自己的业务逻辑自行处理 OrderInfo为我自己业务中的实体类
		DeviceOrdersEntity orderInfo = new DeviceOrdersEntity();
		// 下单商品id
		orderInfo.setGoodsId(orderInfoVO.getGoodsId());
		// 生成雪花id
		orderInfo.setId(IdUtil.getSnowflake(0,0).nextId());
		// 下单用户
		orderInfo.setOpenid(openid);
		orderInfo.setUserId(user.getId());
		orderInfo.setAmount(orderInfoVO.getAmount());// 订单金额
		orderInfo.setPayStatus(0);// 0 未支付 1 已支付
		//支付类型
		orderInfo.setPayMethod(1);// 1 微信支付 2 支付宝支付
		// 单价 折扣 购买天数 需要用户传来 后台与之匹配
		orderInfo.setUnitPrice(orderInfoVO.getUnitPrice());
		orderInfo.setDiscount(orderInfoVO.getDiscount());
		orderInfo.setOrderDays(orderInfoVO.getOrderDays());

//		orderInfo.setPaymentPrice(sysFunctionType.getPrice());
		String body = "测试支付下单商品";
		body = body.length() > 40 ? body.substring(0,39) : body;
		//更新编号防止不同终端微信报重复订单号
		orderInfo.setOrderNumber(IdUtil.getSnowflake(0,0).nextIdStr());
		Map<String, String> req = new HashMap<>();
		// JSAPI支付必须传openid]
		req.put("openid", openid);
		//公众号
		req.put("appid", clientId);
		// 商户号
		req.put("mch_id", payConfig.getMchId());
		// 32位随机字符串
		req.put("nonce_str", WXPayUtil.generateNonceStr());
		// 商品描述
		req.put("body", body);
		// 商户订单号
		req.put("out_trade_no", orderInfo.getOrderNumber());
		// 标价金额(分)
		req.put("total_fee", String.valueOf(MoneyUtils.yuanToFen(String.valueOf(orderInfo.getAmount()))));
		// 终端IP
		req.put("spbill_create_ip", request.getRemoteAddr());
		// 回调地址+携带的返回参数  domain 为配置的域名[不可为ip地址]
		req.put("notify_url", payConfig.getDomain()+"/api/vdp-mp/mp/py/notify-order-wx"+"/"+orderInfo.getId());
		// 交易类型
		req.put("trade_type", "JSAPI"); //JSAPI 是微信公众号支付或小程序支付的一种交易类型 //NATIVE 交易类型主要用于线下扫码支付场景
		try {
			// 签名
			req.put("sign", WXPayUtil.generateSignature(req, payConfig.getMchKey(), WXPayConstants.SignType.MD5));
			String xmlBody = WXPayUtil.generateSignedXml(req, payConfig.getMchKey());
			System.err.println(String.format("微信支付预下单请求 xml 格式:\n%s", xmlBody));
			String result = WxChatPayCommonUtil.httpsRequest(WeChatPayUrl.Uifiedorder, "POST", xmlBody);
			// TODO: 2025/3/7 保存订单信息
			log.info("保存订单信息");
			// 保存订单信息
			R submitR = deviceOrdersClient.submit(orderInfo);
			if (submitR.isSuccess()) {
				log.info("保存订单信息成功");
			} else {
				log.info("保存订单信息失败");
			}


			System.err.println(String.format("%s", result));
			Map<String, String> WxResultMap = WXPayUtil.xmlToMap(result);
			WxResultMap.put("orderNo",orderInfo.getOrderNumber());
			if (ObjectUtil.isNotEmpty(WxResultMap.get("return_code")) && WxResultMap.get("return_code").equals("SUCCESS")) {
				if (WxResultMap.get("result_code").equals("SUCCESS")) {
					System.out.println("预下单成功");
					System.out.println("QrCode:"+WxResultMap.get("code_url"));
					return R.data(WxResultMap);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		return R.fail("下单失败");
	}

	@Override
	public Map<String, Object> WeChatPayCallback(String xmlData, String apiKey) {
		Map<String, Object> ResultMap = new HashMap<String, Object>();
		//解析到微信返回过来的xml数据
		try {
			//xml转Map
			Map<String, String> WxResultMap = WXPayUtil.xmlToMap(xmlData);
			//验证签名
			boolean SignStatus = WXPayUtil.isSignatureValid(WxResultMap, apiKey);
			if (SignStatus) {
				//验证成功
				//要返回给微信的xml数据
				String returnWeChat = WxChatPayCommonUtil.setReturnXml("SUCCESS", "OK");
				ResultMap.put("Verify", "YES");
				ResultMap.put("returnWeChat", returnWeChat);
				ResultMap.put("data", WxResultMap);
			} else {
				//验证失败(表示可能接口被他人调用  需要留意)
				ResultMap.put("Verify", "NO");
				ResultMap.put("msg", "验签失败。");
			}
			return ResultMap;
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}



}

回调接口:

	/**
	 * 支付回调(微信)
	 *
	 * @param xmlData  微信XML数据
	 * @param orderId  订单id
	 * @return 返回支付结果
	 */
	@PostMapping("/notify-order-wx/{orderId}")
	public String notifyOrderWx(HttpServletRequest request, HttpServletResponse response,
								@RequestBody String xmlData,
								@PathVariable("orderId") Long orderId
								) throws IOException {
		AuthConfig config = socialProperties.getOauth().get(AuthDefaultSource.WECHAT_MP);



		log.info("支付回调(微信):" + xmlData);
		if( orderId == null ){
			System.out.println("验签失败");
			response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
		}
		String resXml = "";
		try {
			//通过商家id查询支付配置
//			PayConfig payConfig = payConfigService.selectPayConfigByTenantId(tenantId,"1");
			// todo 不确定后面的传递的是什么 apiV2 密钥
			Map<String, Object> ResultMap = orderInfoService.WeChatPayCallback(xmlData, payConfig.getMchKey());
			Map<String, String> WxResultMapData = new HashMap<>();
			if (ResultMap.get("Verify").equals("YES")) {
				//验签成功
				System.out.println("验签成功");
				WxResultMapData = (Map<String, String>) ResultMap.get("data");
				System.out.println("WxResultMapData:" + JSON.toJSONString(WxResultMapData));
				log.info("收到微信回调:{}", JSON.toJSONString(WxResultMapData));
				log.info("查询订单");
				// TODO: 2025/3/7 查询订单
				R<DeviceOrdersVO> deviceOrdersVOR = deviceOrdersClient.detailById(orderId);
				if (!deviceOrdersVOR.isSuccess() || deviceOrdersVOR.getData() == null) {
					log.info("查询订单失败");
					log.info("无法变更订单状态,通知微信验签失败");
					return WxPayNotifyResponse.fail("失败");
				}
				System.out.println("通知微信验签成功");
				//自信业务逻辑处理
				log.info("自己的业务处理");
//				todo 自己的业务处理
				DeviceOrdersVO ordersVO = deviceOrdersVOR.getData();
				// 验证金额
				BigDecimal payAmount = new BigDecimal(WxResultMapData.get("total_fee"));
				BigDecimal amount = ordersVO.getAmount();
				if(payAmount == null || amount == null || amount.compareTo(payAmount) != 0) {
					log.error("微信支付回调时,支付金额{}与待支付金额{}不符",payAmount,amount);
					return WxPayNotifyResponse.fail("支付金额不符");
				}
				ordersVO.setPayStatus(1);// 支付状态 已支付
				ordersVO.setPayTime(new Date());// 支付时间
				R updateR = deviceOrdersClient.update(ordersVO);
				if (!updateR.isSuccess()) {
					log.info("无法变更订单状态,通知微信验签失败");
					return WxPayNotifyResponse.fail("失败");
				}else {
					log.info("变更订单状态成功");
				}
//				orderInfoService.notifyOrder(orderInfo,tenantId,orderId,typeId,WxResultMapData);
				resXml = String.valueOf(ResultMap.get("returnWeChat"));
			} else if (ResultMap.get("Verify").equals("NO")) {
				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[" + WxResultMapData.get("err_code_des") + "]]></return_msg>" + "</xml> ";
			}
		}catch (Exception e) {
			throw new RuntimeException(e);
		}finally {
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
			out.write(resXml.getBytes());
			out.flush();
			out.close();
		}
		return WxPayNotifyResponse.success("成功");
	}

具体说明:

一些java相关的工具类:



import cn.hutool.core.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class MessageUtil {
	/**
	 * 解析微信公众号回调xml
	 * @param request 请求
	 * @return
	 */
	public static Map<String,Object> parseRequest(HttpServletRequest request){

		InputStream inputStream = null;
		Map<String,Object> map = new HashMap<>();
		SAXReader reader = new SAXReader();
		Document document;
		try {
			inputStream = request.getInputStream();
			//读取输入流获取文档对象
			document = reader.read(inputStream); // 切记 必须为接口配置xss白名单 否则会解析xml错误
			//根据文档对象获取根节点
			Element rootElement = document.getRootElement();
			//获取根所有子节点
			List<Element> elements = rootElement.elements();
			for (Element e:elements) {
				map.put(e.getName(),e.getStringValue());
			}
		} catch (IOException | DocumentException e) {
			log.error("处理微信公众号请求异常:", e);
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException ioe) {
					log.error("关闭inputStream异常:", ioe);
				}
			}
		}
		return map;
	}
}





import java.math.BigDecimal;

/**
 * 钱 工具类
 *
 * Created by YouHan on 2019-06-28 09:12:00
 * Copyright © 2019 YouHan All rights reserved.
 */
public class MoneyUtils {
	public static final String YUAN = "元";
	public static final String FEN = "分";

	/**
	 * 元转分
	 *
	 * @param s
	 * @return java.lang.Integer
	 * @date 2020/9/10 9:03
	 * @author YouHan
	 */
	public static Integer yuanToFen(String s) {
		if (!YouNumberUtil.isNumber(s)) {
			return 0;
		}

		return new BigDecimal(s).multiply(new BigDecimal(100)).intValue();
	}

	/**
	 * 处理分
	 *
	 * @param s
	 * @return java.lang.Integer
	 * @author YouHan
	 * @date 2022/7/23
	 */
	public static Integer handleFen(String s) {
		if (!YouNumberUtil.isNumber(s)) {
			return 0;
		}

		return new BigDecimal(s).intValue();
	}

	/**
	 * 分转元
	 *      可以为正负小数(这里保留2位小数)
	 *
	 * @param s
	 * @return java.lang.String
	 * @date 2020/9/10 9:01
	 * @author YouHan
	 */
	public static String fenToYuan(String s) {
		if (!YouNumberUtil.isNumber(s) || "0".equals(s) || "-0".equals(s)) {
			return "0.00";
		}

		return new BigDecimal(s)
			.divide(new BigDecimal(100))
			.setScale(2, BigDecimal.ROUND_DOWN)
			.toString();
	}

	/**
	 * 处理元
	 *      可以为正负小数(这里保留2位小数)
	 *
	 * @param s
	 * @return java.lang.String
	 * @author YouHan
	 * @date 2022/7/23
	 */
	public static String handleYuan(String s) {
		if (!YouNumberUtil.isNumber(s) || "0".equals(s) || "-0".equals(s)) {
			return "0.00";
		}

		return new BigDecimal(s)
			.setScale(2, BigDecimal.ROUND_DOWN)
			.toString();
	}

}



/**
 * 微信支付接口地址
 *
 */
public class WeChatPayUrl {
	//统一下单预下单接口url
	public static final String Uifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	//订单状态查询接口URL
	public static final String Orderquery = "https://api.mch.weixin.qq.com/pay/orderquery";
	//订单申请退款
	public static final String Refund = "https://api.mch.weixin.qq.com/secapi/pay/refund";
	//付款码 支付
	public static final String MicroPay = "https://api.mch.weixin.qq.com/pay/micropay";
	//微信网页授权 获取“code”请求地址
	public static final String GainCodeUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";

}
package com.xhsoft.mp.util;



import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;

/**
 * 微信支付工具类
 *
 */
public class WxChatPayCommonUtil {
	/**
	 * 发送 http 请求
	 * @param requestUrl    请求路径
	 * @param requestMethod 请求方式(GET/POST/PUT/DELETE/...)
	 * @param outputStr     请求参数体
	 * @return 结果信息
	 */
	public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
		try {
			URL url = new URL(requestUrl);
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			// 设置请求方式(GET/POST)
			conn.setRequestMethod(requestMethod);
			conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
			// 当outputStr不为null时向输出流写数据
			if (null != outputStr) {
				OutputStream outputStream = conn.getOutputStream();
				// 注意编码格式
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}
			// 从输入流读取返回内容
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 释放资源
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			conn.disconnect();
			return buffer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取ip
	 * @param request 请求
	 * @return ip 地址
	 */
	public static String getIp(HttpServletRequest request) {
		if (request == null) {
			return "";
		}
		String ip = request.getHeader("X-Requested-For");
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("X-Forwarded-For");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	/**
	 * 从流中读取微信返回的xml数据
	 * @param httpServletRequest
	 * @return
	 * @throws IOException
	 */
	public static String readXmlFromStream(HttpServletRequest httpServletRequest) throws IOException {
		InputStream inputStream = httpServletRequest.getInputStream();
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
		final StringBuffer sb = new StringBuffer();
		String line = null;
		try {
			while ((line = bufferedReader.readLine()) != null) {
				sb.append(line);
			}
		} finally {
			bufferedReader.close();
			inputStream.close();
		}

		return sb.toString();
	}

	/**
	 * 设置返回给微信服务器的xml信息
	 * @param returnCode
	 * @param returnMsg
	 * @return
	 */
	public static String setReturnXml(String returnCode, String returnMsg) {
		return "<xml><return_code><![CDATA[" + returnCode + "]]></return_code><return_msg><![CDATA[" + returnMsg + "]]></return_msg></xml>";
	}

}





import org.apache.commons.lang3.StringUtils;

import java.util.concurrent.ThreadLocalRandom;

/**
 * 数字 client
 *
 * Created by YouHan on 2020-09-09 13:28:40
 * Copyright © 2021 YouHan All rights reserved.
 */
public class YouNumberUtil {
	/**
	 * 整数正则表达式
	 */
	public static final String INTEGER_REGEX = "^[-\\+]?[\\d]*$";

	/**
	 * 数字正则表达式
	 */
	public static final String NUMBER_REGEX = "^-?\\d+(\\.\\d+)?$";

	/**
	 * 是否是整数
	 *
	 * @param s
	 * @return java.lang.Boolean
	 * @date 2020/9/12 8:38
	 * @author YouHan
	 */
	public static boolean isInteger(String s) {
		if (StringUtils.isBlank(s)) {
			return false;
		}

		return s.matches(INTEGER_REGEX);
	}

	/**
	 * 判断给定字符串是否为十六进制数
	 *
	 * @param value 值
	 * @return 是否为十六进制
	 */
	public static boolean isHex(String value) {
		final int index = (value.startsWith("-") ? 1 : 0);
		if (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index)) {
			try {
				Long.decode(value);
			} catch (NumberFormatException e) {
				return false;
			}
			return true;
		}

		return false;
	}

	/**
	 * 是否是数字(包括小数)
	 *
	 * @param s
	 * @return java.lang.Boolean
	 * @date 2020/9/9 14:01
	 * @author YouHan
	 */
	public static boolean isNumber(String s) {
		if (StringUtils.isBlank(s)) {
			return false;
		}

		return s.matches(NUMBER_REGEX);
	}

	/**
	 * 十进制转十六进制
	 *
	 * @param n 十进制数
	 * @return java.lang.String
	 * @date 2019/4/8 09:22
	 * @author YouHan
	 */
	public static String intToHex(Integer n) {
		if (null == n) {
			return null;
		}

		return String.format("%X", n);
	}

	/**
	 * 十进制转十六进制
	 *
	 * @param n 十进制数
	 * @return java.lang.String
	 * @date 2019/4/8 09:22
	 * @author YouHan
	 */
	public static String longToHex(Long n) {
		if (null == n) {
			return null;
		}

		return String.format("%X", n);
	}

	/**
	 * 十进制转十六进制
	 *
	 * @param n
	 * @param length
	 * @return java.lang.String
	 * @date 2019-08-12 09:56
	 * @author YouHan
	 */
	public static String intToHexPrefix(Integer n, Integer length) {
		if (null == n) {
			return null;
		}
		if (null == length || length <= 0) {
			return null;
		}

		String result = String.format("%X", n);
		if (result.length() < length) {
			result = YouStringUtil.appendPrefixContent(result, "0", length - result.length());
		}

		return result;
	}

	/**
	 * 十进制转十六进制
	 *
	 * @param n
	 * @param length
	 * @return java.lang.String
	 * @date 2019-08-12 09:56
	 * @author YouHan
	 */
	public static String longToHexPrefix(Long n, Integer length) {
		if (null == n) {
			return null;
		}
		if (null == length || length <= 0) {
			return null;
		}

		String result = String.format("%X", n);
		if (result.length() < length) {
			result = YouStringUtil.appendPrefixContent(result, "0", length - result.length());
		}

		return result;
	}

	/**
	 * 十进制转十六进制
	 *
	 * @param n 十进制数
	 * @return java.lang.String
	 * @date 2019/4/8 09:22
	 * @author YouHan
	 */
	public static String intToHexSuffix(Integer n, Integer length) {
		if (null == n) {
			return null;
		}
		if (null == length || length <= 0) {
			return null;
		}

		String result = String.format("%X", n);
		if (result.length() < length) {
			result = YouStringUtil.appendSuffixContent(result, "0", length - result.length());
		}

		return result;
	}

	/**
	 * 十进制转十六进制
	 *
	 * @param n 十进制数
	 * @return java.lang.String
	 * @date 2019/4/8 09:22
	 * @author YouHan
	 */
	public static String longToHexSuffix(Long n, Integer length) {
		if (null == n) {
			return null;
		}
		if (null == length || length <= 0) {
			return null;
		}

		String result = String.format("%X", n);
		if (result.length() < length) {
			result = YouStringUtil.appendSuffixContent(result, "0", length - result.length());
		}

		return result;
	}

	/**
	 * 十六进制转十进制
	 *
	 * @param hex
	 * @return java.lang.Integer
	 * @date 2019/4/8 09:49
	 * @author YouHan
	 */
	public static Integer hexToInt(String hex) {
		Long n = hexToLong(hex);
		if (null == n) {
			return null;
		}

		// 超出整数最大值,不予处理
		if (Integer.MAX_VALUE < n) {
			return null;
		}

		return Integer.valueOf(String.valueOf(n));
	}

	/**
	 * 十六进制转十进制
	 *
	 * @param hex
	 * @return java.lang.Integer
	 * @date 2019/4/8 09:49
	 * @author YouHan
	 */
	public static Long hexToLong(String hex) {
		if (StringUtils.isBlank(hex)) {
			return null;
		}

		// 去除前缀为 0 的 十六进制
		if (hex.length() > 1 && hex.startsWith("0")) {
			hex = hex.substring(1);
		}

		return Long.valueOf(hex, 16);
	}

	/**
	 * 字符串转十六进制
	 *
	 * @param s
	 * @return
	 */
	public static String stringToHex(String s) {
		char[] chars = "0123456789ABCDEF".toCharArray();
		StringBuilder sb = new StringBuilder("");
		byte[] bs = s.getBytes();
		int bit;
		for (int i = 0; i < bs.length; i++) {
			bit = (bs[i] & 0x0f0) >> 4;
			sb.append(chars[bit]);
			bit = bs[i] & 0x0f;
			sb.append(chars[bit]);
		}
		return sb.toString().trim();
	}

	/**
	 * 十六进制转字符串
	 *
	 * @param s
	 * @return
	 */
	public static String hexToString(String s) {
		String str = "0123456789ABCDEF";
		char[] hexs = s.toCharArray();
		byte[] bytes = new byte[s.length() / 2];
		int n;
		for (int i = 0; i < bytes.length; i++) {
			n = str.indexOf(hexs[2 * i]) * 16;
			n += str.indexOf(hexs[2 * i + 1]);
			bytes[i] = (byte) (n & 0xff);
		}

		return new String(bytes);
	}

	/**
	 * 去除末尾多余的 0
	 *
	 * @param s
	 * @return java.lang.String
	 * @author YouHan
	 * @date 2021/7/2 15:39
	 */
	public static String stripTrailingZeros(String s) {
		if (StringUtils.isBlank(s)) {
			return "0";
		}

		if (!isNumber(s)) {
			return "0";
		}

		if (!s.contains(".")) {
			return s;
		}

		while (s.endsWith("0")) {
			s = s.substring(0, s.length() - 1);
		}
		if (s.endsWith(".")) {
			s = s.substring(0, s.length() - 1);
		}

		return s;
	}

	/**
	 * int 转 String
	 * 1024以内高效,超出后,正常转换
	 */
	static int cacheSize = 1024;
	static String[] caches = new String[cacheSize];

	static {
		for (int i = 0; i < cacheSize; i++) {
			caches[i] = String.valueOf(i);
		}
	}

	public static String int2String(int data) {
		if (data < cacheSize) {
			return caches[data];
		}
		return String.valueOf(data);
	}

	/**
	 * 获取几位的 int 随机数
	 *
	 * @param length
	 * @return int
	 * @author YouHan
	 * @date 2021/12/19
	 */
	public static int getRandomInt(int length) {
		return (int) ((Math.random() * 9 + 1) * 10 * length);
	}

	/**
	 * 获取几位的 long 随机数
	 *
	 * @param length
	 * @return long
	 * @author YouHan
	 * @date 2021/12/19
	 */
	public static long getRandomLong(long length) {
		return (long) ((Math.random() * 9 + 1) * 10 * length);
	}

	/**
	 * 获取随机数
	 *
	 * @param
	 * @return java.client.concurrent.ThreadLocalRandom
	 * @author YouHan
	 * @date 2021/6/3 10:29
	 */
	public static ThreadLocalRandom getRandom() {
		return ThreadLocalRandom.current();
	}

	/**
	 * 获取缓存穿透时间(单位秒),最长不超过 5 分钟
	 *
	 * @param
	 * @return java.lang.Long
	 * @date 2021/4/26 9:50
	 * @author YouHan
	 */
	public static Long getCachePenetrationTime() {
		return Long.valueOf(int2String(getRandom().nextInt(300)));
	}

	/**
	 * 获取数据库缓存时间(单位秒),最长不超过 1 小时
	 *
	 * @param
	 * @return java.lang.Long
	 * @date 2021/4/26 9:50
	 * @author YouHan
	 */
	public static Long getDBCacheTime() {
		return Long.valueOf(int2String(getRandom().nextInt(3600)));
	}

	/**
	 * 十六进制高低位转换
	 *
	 * @param hexString
	 * @return java.lang.String
	 * @author YouHan
	 * @date 2021/12/11
	 */
	public static String hexHighLowPositionConvert(String hexString) {
		if (StringUtils.isBlank(hexString) || hexString.length() % 2 != 0) {
			return null;
		}

		StringBuilder result = new StringBuilder();
		for (int i = hexString.length() - 2; i >= 0; i = i - 2) {
			result.append(hexString.substring(i, i + 2));
		}

		return result.toString();
	}

}




import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * String client
 *
 * Created by YouHan on 2019-09-11 08:57:56
 * Copyright © 2019 YouHan All rights reserved.
 */
public class YouStringUtil {

	/**
	 * 下划线
	 */
	public static final Pattern LINE = Pattern.compile("_(\\w)");

	/**
	 * 驼峰
	 */
	public static final Pattern HUMP = Pattern.compile("[A-Z]");

	/**
	 * 添加内容
	 *
	 * @param content
	 * @param length
	 * @return java.lang.String
	 * @author YouHan
	 * @date 2021/6/17 9:59
	 */
	public static String appendContent(String content, int length) {
		if (length <= 0) {
			return "";
		}

		StringBuilder sb = new StringBuilder();

		for (int i = 0; i < length; i ++) {
			sb.append(content);
		}

		return sb.toString();
	}

	/**
	 * 添加前缀内容
	 *
	 * @param s
	 * @param content
	 * @param length
	 * @return java.lang.String
	 * @date 2019-08-12 09:53
	 * @author YouHan
	 */
	public static String appendPrefixContent(String s, String content, int length) {

		if (length <= 0) {
			return null;
		}

		StringBuilder sb = new StringBuilder(s);
		for (int i = 0; i < length; i ++) {
			sb.append(content, 0, content.length());
		}

		return sb.toString();
	}

	/**
	 * 添加后缀内容
	 *
	 * @param s
	 * @param content
	 * @param length
	 * @return java.lang.String
	 * @date 2019-08-12 09:56
	 * @author YouHan
	 */
	public static String appendSuffixContent(String s, String content, int length) {
		if (length <= 0) {
			return null;
		}

		StringBuilder sb = new StringBuilder(s);

		for (int i = 0; i < length; i ++) {
			sb.append(content);
		}

		return sb.toString();
	}

	/**
	 * Set 转 String
	 *
	 * @param stringSet
	 * @return java.lang.String
	 * @author YouHan
	 * @date 2021/6/3 9:26
	 */
	public static String setToString(Set<String> stringSet) {
		return setToString(stringSet, null);
	}

	/**
	 * Set 转 String
	 *
	 * @param stringSet
	 * @param regex
	 * @return java.lang.String
	 * @date 2021/6/3 9:21
	 * @author YouHan
	 */
	public static String setToString(Set<String> stringSet, String regex) {
		// 参数校验
		if (CollectionUtils.isEmpty(stringSet)) {
			return null;
		}
		if (StringUtils.isBlank(regex)) {
			regex = ",";
		}

		// List to String
		StringBuilder sb = new StringBuilder(stringSet.size());
		for (String s : stringSet) {
			sb.append(s).append(regex);
		}

		// 返回结果
		return sb.substring(0, sb.length() - 1);
	}

	/**
	 *  字符串列表转字符串
	 *
	 * @author YouHan
	 * @generatedDate: 2018/10/9 17:25
	 * @param stringList 要转换的字符串列表
	 * @return
	 */
	public static String listToString(List<String> stringList) {
		return listToString(stringList, null);
	}

	/**
	 *  字符串列表转字符串
	 *
	 * @author YouHan
	 * @generatedDate: 2018/10/9 17:25
	 * @param stringList 要转换的字符串列表
	 * @return
	 */
	public static String listToString(List<String> stringList, String regex) {
		// 参数校验
		if (CollectionUtils.isEmpty(stringList)) {
			return null;
		}
		if (StringUtils.isBlank(regex)) {
			regex = ",";
		}

		// List to String
		StringBuilder sb = new StringBuilder(stringList.size());
		for (String s : stringList) {
			sb.append(s).append(regex);
		}

		// 返回结果
		return sb.substring(0, sb.length() - 1);
	}

	/**
	 * 字符串转列表
	 *
	 * @param s
	 * @return java.client.List<java.lang.String>
	 * @date 2019-09-11 09:11
	 * @author YouHan
	 */
	public static List<String> stringToList(String s) {
		/**
		 * 参数校验
		 */
		if (StringUtils.isBlank(s)) {
			return null;
		}

		return stringToList(s, null);
	}

	/**
	 * 字符串转列表
	 *
	 * @param s
	 * @param regex 分割规则,默认为逗号
	 * @return java.client.List<java.lang.String>
	 * @date 2019-09-11 09:11
	 * @author YouHan
	 */
	public static List<String> stringToList(String s, String regex) {
		/**
		 * 参数校验
		 */
		if (StringUtils.isBlank(s)) {
			return null;
		}

		/**
		 * 默认逗号隔开
		 */
		if (StringUtils.isBlank(regex)) {
			regex = ",";
		}

		/**
		 * 去除首尾空格
		 */
		String blankString = " ";
		while (s.startsWith(blankString)) {
			s = s.substring(1);
		}
		while (s.endsWith(blankString)) {
			s = s.substring(0, s.length() -1);
		}

		/**
		 * 返回结果列表
		 */
		List<String> resultList = new ArrayList<>();

		/**
		 * 只有单个元素
		 */
		if (!s.contains(regex)) {
			resultList.add(s);
			return resultList;
		}

		String[] strings = s.split(regex);
		for (String e : strings) {
			resultList.add(e);
		}

		return resultList;
	}

	/**
	 * 过滤逗号
	 * @param s
	 * @return
	 */
	public static String filterCommaString(String s) {
		// 数据为空校验
		if (StringUtils.isEmpty(s)) {
			return null;
		}

		// 去除 并列逗号
		s = s.replace(",,", ",");

		// 去除 首逗号
		if (s.startsWith(",")) {
			s = s.substring(1, s.length() - 1);
		}

		// 去除 尾逗号
		if (s.endsWith(",")) {
			s = s.substring(0, s.length() - 1);
		}

		return s;
	}

	/**
	 * 是否包含中文(包括中文标点符号和空格)
	 *
	 * @param s
	 * @return boolean
	 * @date 2020/9/9 13:30
	 * @author YouHan
	 */
	public static Boolean isContainChinese(String s) {
		/**
		 * 参数校验
		 */
		if (StringUtils.isBlank(s)) {
			return false;
		}

		if (s.contains(" ")) {
			return true;
		}

		/**
		 * 中文正则表达式
		 */
		String regex = "[\u4e00-\u9fa5]";

		if (s.matches(regex)) {
			return Boolean.TRUE;
		}

		/**
		 * 中文标点符号处理
		 */
		char[] chars = s.toCharArray();
		for (char c : chars) {
			if (isChinesePunctuation(c)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * 过滤中文(包括标点符号和空格)
	 *
	 * @param s
	 * @return java.lang.String
	 * @date 2020/9/9 14:08
	 * @author YouHan
	 */
	public static String filterChinese(String s) {
		/**
		 * 参数校验
		 */
		if (StringUtils.isBlank(s)) {
			return "";
		}

		s = s.replace(" ", "");

		if (!isContainChinese(s)) {
			return s;
		}

		/**
		 * 过滤中文字符
		 */
		char[] chars = s.toCharArray();
		StringBuilder sb = new StringBuilder(chars.length);
		for (char c : chars) {
			if (isContainChinese(String.valueOf(c))) {
				continue;
			}
			sb.append(c);
		}

		return sb.toString();
	}

	/**
	 * 判断是否为中文标点符号
	 *
	 * @param c
	 * @return java.lang.Boolean
	 * @date 2020/9/9 13:43
	 * @author YouHan
	 */
	public static boolean isChinesePunctuation(char c) {
		Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
		if (ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
			|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
			|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
			|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS
			|| ub == Character.UnicodeBlock.VERTICAL_FORMS) {
			return true;
		}

		return false;
	}

	/**
	 * 获取 UUID
	 *
	 * @param
	 * @return java.lang.String
	 * @date 2021/4/9 14:08
	 * @author YouHan
	 */
	public static String getUUID() {
		return UUID.randomUUID().toString().replace("-", "");
	}

	/**
	 * 安全比较(可防止时序攻击 timing attack)
	 */
	public static boolean safeEqual(String a, String b) {
		if (StringUtils.isBlank(a) || StringUtils.isBlank(b)) {
			return false;
		}
		if (a.length() != b.length()) {
			return false;
		}
		int equal = 0;
		for (int i = 0; i < a.length(); i++) {
			equal |= a.charAt(i) ^ b.charAt(i);
		}
		return equal == 0;
	}

	/**
	 * 驼峰转下划线
	 *
	 * @param s
	 * @return java.lang.String
	 * @date 2021/5/6 22:20
	 * @author YouHan
	 */
	public static String humpToLine(String s) {
		Matcher matcher = HUMP.matcher(s);
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
		}
		if (sb.toString().startsWith("_")) {
			sb.deleteCharAt(0);
		}

		matcher.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 下划线转驼峰
	 *
	 * @param s
	 * @return java.lang.String
	 * @date 2021/5/6 22:21
	 * @author YouHan
	 */
	public static String lineToHump(String s) {
		s = s.toLowerCase();
		Matcher matcher = LINE.matcher(s);
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
		}
		matcher.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 生成加密的内容
	 *
	 * @param s
	 * @return java.lang.String
	 * @author YouHan
	 * @date 2021/6/17 10:10
	 */
	public static String hide(String s) {
		/**
		 * 1
		 * 1*
		 * 1**
		 * 1***
		 * 1***5
		 * 12***6
		 * 12***67
		 * 123***78
		 * 123***789
		 * 123****890
		 * 123*****8901
		 */
		if (s.isEmpty() || s.length() == 1) {
			return s;
		}

		if (s.length() == 2) {
			return s.substring(0, 1) + "*";
		}

		if (s.length() == 3 || s.length() == 4) {
			return s.substring(0, 1) + appendContent("*", s.length() - 1);
		}

		if (s.length() == 5) {
			return s.substring(0, 1) + "***" + s.substring(4);
		}

		if (s.length() == 6 || s.length() == 7) {
			return s.substring(0, 2) + appendContent("*", 3) + s.substring(5);
		}

		if (s.length() == 8) {
			return s.substring(0, 3) + "***" + s.substring(6);
		}

		return s.substring(0, 3) + appendContent("*", s.length() - 6) + s.substring(s.length() - 3);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值