package com.ylx.massage.utils; /** * @author jianlong * @date 2024-06-04 10:13 */ import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSONObject; import com.ylx.common.config.WechatAccountConfig; import com.ylx.common.constant.MassageConstants; import com.ylx.common.core.redis.RedisCache; import com.ylx.common.exception.ServiceException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.annotation.Resource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 微信登录工具类 */ @Component @Slf4j public class WeChatUtil { private final static String ERROR_CODE = "errcode"; @Resource private WechatAccountConfig wxPayProperties; @Autowired private RedisCache redisCache; /** * 获取微信授权code * * @param state state信息 * @return 返回微信授权code的URL */ public String getCode(String state) { try { StringBuffer url = new StringBuffer(); url.append(wxPayProperties.getGetCodeUrl()) .append("?appid=") .append(wxPayProperties.getMpAppId()) .append("&redirect_uri=") .append(URLEncoder.encode(wxPayProperties.getRedirectUrl(), "UTF-8")) // .append(URLEncoder.encode("https://test.baoxianzhanggui.com/fragrance/#/pages/my/wxLogin", "UTF-8")) .append("&response_type=code&scope=snsapi_userinfo&state=") .append("STATE") .append("#wechat_redirect"); return url.toString(); } catch (UnsupportedEncodingException e) { throw new ServiceException("URL格式化异常"); } } /** * 获取微信AccessToken * * @param code 用户code * @return 返回包含微信AccessToken的Map */ public Map getAccessToken(String code) { StringBuffer url = new StringBuffer(); url.append(wxPayProperties.getAccessTokenUrl()) .append("?appid=").append(wxPayProperties.getMpAppId()) .append("&secret=").append(wxPayProperties.getMpAppSecret()) .append("&code=").append(code) .append("&grant_type=authorization_code"); String rs = HttpUtil.get(url.toString()); Map map = JSONObject.parseObject(rs, Map.class); if (null == map.get(ERROR_CODE)) { return map; } else { log.error("getAccessToken-获取access_token出错:{}", map); throw new ServiceException("获取access_token出错"); } } /** * 获取微信AccessToken * * @return 返回包含微信AccessToken的Map */ public String getToken() { if (redisCache.getCacheObject(MassageConstants.ACCESS_TOKEN_KEY + wxPayProperties.getMpAppId()) != null) { return redisCache.getCacheObject(MassageConstants.ACCESS_TOKEN_KEY + wxPayProperties.getMpAppId()); } StringBuffer url = new StringBuffer(); url.append("https://api.weixin.qq.com/cgi-bin/token") .append("?grant_type=").append("client_credential") .append("&appid=").append(wxPayProperties.getMpAppId()) .append("&secret=").append(wxPayProperties.getMpAppSecret()); String rs = HttpUtil.get(url.toString()); Map map = JSONObject.parseObject(rs, Map.class); if (null == map.get(ERROR_CODE)) { redisCache.setCacheObject(MassageConstants.ACCESS_TOKEN_KEY + wxPayProperties.getMpAppId(), map.get("access_token").toString(), 1, TimeUnit.HOURS); return map.get("access_token").toString(); } else { log.error("getToken-获取access_token出错:{}", map); throw new ServiceException("getToken-获取access_token出错"); } } /** * 获取jsticke */ public String getJsapiTicket(String token) { //todo 缓存公众号全局token凭证 StringBuffer url = new StringBuffer(); url.append("https://api.weixin.qq.com/cgi-bin/ticket/getticket") .append("?access_token=").append(token) .append("&type=").append("jsapi"); String rs = HttpUtil.get(url.toString()); Map map = JSONObject.parseObject(rs, Map.class); if ("ok".equals(map.get("errmsg"))) { return map.get("ticket").toString(); } else { log.error("获取jsticke出错:{}", map); throw new ServiceException("获取jsticke出错"); } } /** * 刷新AccessToken * * @param refreshToken 用户刷新token * @return 返回包含刷新后的微信AccessToken的Map */ public Map refreshToken(String refreshToken) { StringBuffer url = new StringBuffer(); url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token") .append("?appid=").append(wxPayProperties.getMpAppId()) .append("&grant_type=refresh_token&refresh_token=").append(refreshToken); String rs = HttpUtil.get(url.toString()); Map map = JSONObject.parseObject(rs, Map.class); if (null == map.get(ERROR_CODE)) { return map; } else { log.error("刷新access_token出错:{}", map); throw new ServiceException("刷新access_token出错"); } } /** * 获取用户信息 * * @param accessToken 微信AccessToken * @param openid 用户的openid * @return 返回包含用户信息的JSON字符串 */ public String getUserInfo(String accessToken, String openid) { StringBuffer url = new StringBuffer(); url.append("https://api.weixin.qq.com/sns/userinfo") .append("?access_token=").append(accessToken) .append("&openid=").append(openid) .append("&lang=zh_CN"); return HttpUtil.get(url.toString()); } /** * 生成公众号码ticket * * @param token access_token * @return 返回包含微信公众号码ticket的Map */ public Map getTicket(String token, String openId) { String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + token; String param = "{\"action_name\": \"QR_LIMIT_STR_SCENE\",\"action_info\": {\"scene\": {\"scene_str\": \"" + openId + "\"}}}"; String rs = HttpUtil.post(url, param); Map map = JSONObject.parseObject(rs, Map.class); // String ticket = map.get("ticket").toString(); // // String encode = null; // try { // encode = URLEncoder.encode(ticket, "UTF-8"); // } catch (UnsupportedEncodingException e) { // throw new RuntimeException(e); // } // // String url1 = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+ encode; // // String s = HttpUtil.get(url1); // // // // // Map rsmap = JSONObject.parseObject(s, Map.class); // return rsmap; return map; } /** * 生成公众号码ticket * * @param token access_token * @return 返回包含微信公众号码ticket的Map */ public Map menuUtil(String token) { String url = " https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + token; String param = "{\"button\":[{\"type\":\"view\",\"name\":\"立即下单\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/\"},{\"name\":\"更多\",\"sub_button\":[{\"type\":\"view\",\"name\":\"搜索\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/\"},{\"type\":\"click\",\"name\":\"赞一下我们\",\"key\":\"V1001_GOOD\"}]}]}"; String rs = HttpUtil.post(url, param); Map map = JSONObject.parseObject(rs, Map.class); return map; } /** * 公众号消息通知 * * @param token access_token * @return 返回包含微信公众号码ticket的Map */ public Map notification(String openid, cn.hutool.json.JSONObject data) { String token = this.getToken(); String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + token; cn.hutool.json.JSONObject param1 = JSONUtil.createObj(); param1.put("touser", openid); param1.put("template_id", wxPayProperties.getTemplateId1()); param1.put("url", "https://test.baoxianzhanggui.com/fragrance/"); param1.put("data", data); String result = HttpUtil.post(url, param1.toString()); System.out.println(result); log.info("notification-消息通知请求参数:{}", param1.toString()); Map map = JSONObject.parseObject(result, Map.class); log.info("notification-消息通知返回参数:{}", map); return map; } /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public static Map xmlToMap(String strXML) throws Exception { try { Map data = new HashMap<>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { // do nothing } return data; } catch (Exception ex) { log.error("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML); throw ex; } } /** * 将Map转换为XML格式的字符串 * * @param data Map类型数据 * @return XML格式的字符串 * @throws Exception */ public static String mapToXml(Map data) throws Exception { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key : data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { } return output; } }