package com.jsowell.wxpay.service; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.jsowell.common.constant.CacheConstants; import com.jsowell.common.constant.Constants; import com.jsowell.common.core.redis.RedisCache; import com.jsowell.common.enums.ykc.ReturnCodeEnum; import com.jsowell.common.exception.BusinessException; import com.jsowell.common.util.DateUtils; import com.jsowell.common.util.StringUtils; import com.jsowell.common.util.http.HttpUtils; import com.jsowell.pile.service.IOrderBasicInfoService; import com.jsowell.pile.service.IPileBillingTemplateService; import com.jsowell.pile.vo.uniapp.CurrentTimePriceDetails; import com.jsowell.pile.vo.uniapp.SendMessageVO; import com.jsowell.wxpay.config.WeixinLoginProperties; import com.jsowell.wxpay.dto.AppletTemplateMessageSendDTO; import com.jsowell.wxpay.dto.WechatSendMsgDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; import java.text.ParseException; import java.util.Date; import java.util.HashMap; import java.util.Map; @Service public class WxAppletRemoteService { private Logger log = LoggerFactory.getLogger(WxAppletRemoteService.class); private static final String WX_APPLET_URl = "https://api.weixin.qq.com/cgi-bin"; private static final String GET_USER_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"; @Autowired private ObjectMapper objectMapper; @Autowired private RedisCache redisCache; @Autowired private IOrderBasicInfoService orderBasicInfoService; @Autowired private IPileBillingTemplateService pileBillingTemplateService; @Value("${weixin.login.appid}") private String appid; @Value("${weixin.login.appsecret}") private String secret; @Value("${weixin.sendMsg.startChargingTmpId}") private String startChargingTmpId; @Value("${weixin.sendMsg.stopChargingTmpId}") private String stopChargingTmpId; /** * 获取accessToken * * @return */ public String getAccessToken() { // String appid = Constants.APP_ID; // String secret = Constants.APP_SECRET; // 这里我是从配置文件中取得appid和appsecret // appid = properties.getAppId(); // secret = properties.getAppSecret(); //查询token是否存在 String redisKey = CacheConstants.ACCESS_TOKEN + appid; // 使用缓存先查询AccessToken是否存在 String accessToken = redisCache.getCacheObject(redisKey); // 存在直接返回,不存在重新获取AccessToken if (!Strings.isNullOrEmpty(accessToken)) { return accessToken; } // 获取AccessToken的url String grantType = "client_credential"; // https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid= String url = WX_APPLET_URl + "/token?grant_type=" + grantType + "&appid=" + appid + "&secret=" + secret; // 获取到AccessToken String token = HttpUtils.sendGet(url); Map map = null; try { map = objectMapper.readValue(token, Map.class); } catch (IOException e) { log.error("小程序异常通知-获取AccessToken-转化异常", e); } String access_token = String.valueOf(map.get("access_token")); // 把AccessToken存入缓存中,并设置过期时间,因为access_token的过期时间是两小时,我们缓存的时间一定要小于两小时, redisCache.setCacheObject(redisKey, access_token, 300); if (map.get("errcode") != null || map.get("errmsg") != null) { String errcode = String.valueOf(map.get("errcode")); String errmsg = String.valueOf(map.get("errmsg")); if (!errcode.equals("0")) { log.error("获取token失败:code=" + errcode + "msg=" + errmsg); return null; } } return access_token; } /** * 获取手机号 * * @param code * @return */ public String getMobileNumberByCode(String code) { if (StringUtils.isBlank(code)) { throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); } // 请求获取令牌 String access_token = getAccessToken(); // 通过token,code(前端getPhoneNum按钮返回的code,而不是登录的code)发送post请求到官方获取到手机号码 String postUrl = GET_USER_PHONE_NUMBER_URL + "?access_token=" + access_token; JSONObject paramJson = new JSONObject(); paramJson.put("code", code); String postResult = HttpUtils.sendPost(postUrl, paramJson.toJSONString()); JSONObject postResultJson = JSONObject.parseObject(postResult); String errCode = postResultJson.getString("errcode"); if (!StringUtils.equals(errCode, Constants.ZERO)) { // errCode不为0,表示有错误 String errMsg = postResultJson.getString("errmsg"); log.info("发送Post请求失败,错误消息:{}", errMsg); throw new BusinessException(errCode, errMsg); } JSONObject phoneInfoJson = (JSONObject) postResultJson.get("phone_info"); return phoneInfoJson.getString("phoneNumber"); } /** * 获取openId * * @param code * @return */ public String getOpenIdByCode(String code) { String baseAccessTokenUrl = WeixinLoginProperties.WX_OPEN_GATEWAY + "?appid=%s" + "&secret=%s" + "&js_code=%s" + "&grant_type=authorization_code"; log.info("appid:{},appscrect:{}", WeixinLoginProperties.WX_OPEN_APP_ID, WeixinLoginProperties.WX_OPEN_APP_SECRET); String accessTokenUrl = String.format(baseAccessTokenUrl, WeixinLoginProperties.WX_OPEN_APP_ID, WeixinLoginProperties.WX_OPEN_APP_SECRET, code); //2:执行请求,获取微信请求返回得数据 String result = HttpUtils.sendGet(accessTokenUrl); // 3: 对微信返回得数据进行转换 Map resultMap = JSONObject.parseObject(result, HashMap.class); log.info("微信返回的日志信息是:code:{},resultMap:{}", code, resultMap); if (resultMap.get("errcode") != null) { throw new BusinessException("22006", "微信登录出错!"); } // 4: 解析微信用户得唯一凭证openid String openid = (String) resultMap.get("openid"); if (StringUtils.isBlank(openid)) { throw new BusinessException("22009", "登录失败,尝试刷新重新扫码登录!!!"); } // 5:封装返回 return openid; } /** * 开始充电发送消息 * @param dto * @return */ public Map startChargingSendMsg(WechatSendMsgDTO dto) { // 通过code查询openId并set String openId = getOpenIdByCode(dto.getCode()); if (StringUtils.isBlank(openId)) { return null; } AppletTemplateMessageSendDTO msgInfo = new AppletTemplateMessageSendDTO(); msgInfo.setType("1"); // 1-开始充电推送消息 msgInfo.setTouser(openId); // 通过orderCode查询到充电站点和开始时间并set String orderCode = dto.getOrderCode(); SendMessageVO sendMessageVO = orderBasicInfoService.selectOrderInfoByOrderCode(orderCode); AppletTemplateMessageSendDTO.StartChargingMessage startChargingMessage = new AppletTemplateMessageSendDTO.StartChargingMessage(); msgInfo.setStartChargingMessage(startChargingMessage); if (StringUtils.isBlank(sendMessageVO.getChargeStartTime())) { startChargingMessage.setStartTime(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm")); }else { startChargingMessage.setStartTime(sendMessageVO.getChargeStartTime()); // 开始时间 } startChargingMessage.setStationName(sendMessageVO.getStationName()); // 站点名称 // 订单编号 startChargingMessage.setOrderCode(sendMessageVO.getOrderCode()); // 收费标准 CurrentTimePriceDetails currentTimePriceDetails = pileBillingTemplateService.getCurrentTimePriceDetails(sendMessageVO.getStationId()); startChargingMessage.setTotalPrice(currentTimePriceDetails.getTotalPrice() + " 元/度"); // 枪口编号 startChargingMessage.setPileConnectorCode(sendMessageVO.getPileSn() + "桩" + sendMessageVO.getConnectorCode() + "枪口"); return uniAppSendMsg(msgInfo); } /** * 停止充电发送消息 * @param dto * @return */ public Map stopChargingSendMsg(WechatSendMsgDTO dto) throws ParseException { // 通过订单号查询订单金额 AppletTemplateMessageSendDTO msgInfo = new AppletTemplateMessageSendDTO(); SendMessageVO sendMessageVO = orderBasicInfoService.selectOrderInfoByOrderCode(dto.getOrderCode()); msgInfo.setType("2"); // 2-结束充电推送消息 msgInfo.setTouser(sendMessageVO.getOpenId()); // 封装对象并调用发送消息的方法 AppletTemplateMessageSendDTO.StopChargingMessage stopChargingMessage = new AppletTemplateMessageSendDTO.StopChargingMessage(); msgInfo.setStopChargingMessage(stopChargingMessage); stopChargingMessage.setChargingAmount(sendMessageVO.getOrderAmount()); // 订单金额 stopChargingMessage.setEndReason(sendMessageVO.getStopReason()); // 结束原因 stopChargingMessage.setOrderCode(sendMessageVO.getOrderCode()); // 订单号 stopChargingMessage.setChargingDegree(sendMessageVO.getChargingDegree()); // 充电度数 Date chargeStartTime = DateUtils.parseDate(sendMessageVO.getChargeStartTime(), DateUtils.YYYY_MM_DD_HH_MM_SS); Date chargeStopTime = DateUtils.parseDate(sendMessageVO.getChargeStopTime(), DateUtils.YYYY_MM_DD_HH_MM_SS); String chargingTime = DateUtils.getDatePoor(chargeStopTime, chargeStartTime); stopChargingMessage.setChargingTime(chargingTime); // 充电时长 // if (StringUtils.isBlank(sendMessageVO.getChargeStopTime())) { // stopChargingMessage.setEndTime(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm")); // }else { // stopChargingMessage.setEndTime(sendMessageVO.getChargeStopTime()); // } return uniAppSendMsg(msgInfo); } /** * 小程序发送消息方法 * @param dto * @return */ private Map uniAppSendMsg(AppletTemplateMessageSendDTO dto) { // 判断是什么场景调用此方法(1-开始充电推送消息;2-充电结束推送消息) String type = dto.getType(); // 根据不同的场景set不同的对象 if (StringUtils.equals("1", type)) { // 开始充电 String templateId = startChargingTmpId; dto.setTemplate_id(templateId); // dto.setPage("跳转的页面"); Map map = new HashMap<>(); map.put("thing5", ImmutableMap.of("value", dto.getStartChargingMessage().getStationName())); // 充电站名称 map.put("time2", ImmutableMap.of("value", dto.getStartChargingMessage().getStartTime())); // 开始时间 map.put("character_string11", ImmutableMap.of("value", dto.getStartChargingMessage().getOrderCode())); // 订单编号 map.put("thing7", ImmutableMap.of("value", dto.getStartChargingMessage().getPileConnectorCode())); // 充电插座 map.put("thing13", ImmutableMap.of("value", dto.getStartChargingMessage().getTotalPrice())); // 收费标准 dto.setData(map); } else if (StringUtils.equals("2", type)) { // 结束充电 String templateId = stopChargingTmpId; dto.setTemplate_id(templateId); // dto.setPage("跳转的页面"); Map map = new HashMap<>(); map.put("character_string5", ImmutableMap.of("value", dto.getStopChargingMessage().getOrderCode())); // 充电金额 map.put("amount17", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingAmount())); // 充电金额 // map.put("time3", ImmutableMap.of("value", dto.getStopChargingMessage().getEndTime())); // 结束时间 map.put("thing7", ImmutableMap.of("value", dto.getStopChargingMessage().getEndReason())); // 结束原因 map.put("thing22", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingTime())); // 充电时长 map.put("number13", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingDegree())); // 充电度数 dto.setData(map); } // 调用下面的发送消息接口 return uniformMessageSend(dto); } /** * 同一消息发送接口 * AppletTemplateMessageSendDTO 是一个传输类 */ public Map uniformMessageSend(AppletTemplateMessageSendDTO data) { String token = getAccessToken(); // 调用发型接口 String url = WX_APPLET_URl + "/message/subscribe/send?access_token=" + token; String returnData = HttpUtils.sendPost(url, JSON.toJSONString(data)); Map map = null; try { map = objectMapper.readValue(returnData, Map.class); } catch (IOException e) { log.error("小程序异常通知-同一消息发送-转化异常", e); } String errcode = String.valueOf(map.get("errcode")); String errmsg = String.valueOf(map.get("errmsg")); if (!errcode.equals(Constants.ZERO)) { log.error("消息发送失败:code=" + errcode + "msg=" + errmsg); } Map resultMap = new HashMap<>(); resultMap.put("code", errcode); resultMap.put("message", errmsg); return resultMap; } }