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.google.common.collect.Maps; 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.YKCUtils; import com.jsowell.common.util.http.HttpUtils; import com.jsowell.pile.domain.MemberBasicInfo; import com.jsowell.pile.domain.OrderBasicInfo; import com.jsowell.pile.service.MemberBasicInfoService; import com.jsowell.pile.service.OrderBasicInfoService; import com.jsowell.pile.service.PileBillingTemplateService; import com.jsowell.pile.vo.uniapp.customer.CurrentTimePriceDetails; import com.jsowell.pile.vo.uniapp.customer.SendMessageVO; import com.jsowell.pile.vo.web.BillingTemplateVO; 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.math.BigDecimal; import java.text.ParseException; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; @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 OrderBasicInfoService orderBasicInfoService; @Autowired private PileBillingTemplateService pileBillingTemplateService; @Autowired private MemberBasicInfoService memberBasicInfoService; @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; @Value("${weixin.sendMsg.startupResultTmpId}") private String startupResultTmpId; /** * 获取accessToken * * @return */ public String getAccessToken() { //查询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 memberId 会员id * @param orderCode 订单编号 * @return */ public Map startChargingSendMsg(String memberId, String orderCode) { MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMemberId(memberId); if (memberBasicInfo == null) { throw new BusinessException("99999", "开始充电发送消息 error, 查询openid为空"); } WechatSendMsgDTO sendMsgDTO = new WechatSendMsgDTO(); sendMsgDTO.setOpenId(memberBasicInfo.getOpenId()); sendMsgDTO.setOrderCode(orderCode); return this.startChargingSendMsg(sendMsgDTO); } public Map startChargingSendMsg(OrderBasicInfo orderBasicInfo) { MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMemberId(orderBasicInfo.getMemberId()); if (memberBasicInfo == null) { throw new BusinessException("99999", "开始充电发送消息 error, 查询openid为空"); } WechatSendMsgDTO sendMsgDTO = new WechatSendMsgDTO(); sendMsgDTO.setOpenId(memberBasicInfo.getOpenId()); sendMsgDTO.setOrderCode(orderBasicInfo.getOrderCode()); return this.startChargingSendMsg(sendMsgDTO); } /** * 开始充电发送消息 * @param dto * @return */ private Map startChargingSendMsg(WechatSendMsgDTO dto) { if (StringUtils.isBlank(dto.getOpenId()) || StringUtils.isBlank(dto.getOrderCode())) { return null; } AppletTemplateMessageSendDTO msgInfo = new AppletTemplateMessageSendDTO(); msgInfo.setType(Constants.ONE); // 1-开始充电推送消息 msgInfo.setTouser(dto.getOpenId()); // 接收者的openId // 通过orderCode查询到充电站点和开始时间并set SendMessageVO sendMessageVO = orderBasicInfoService.selectOrderInfoByOrderCode(dto.getOrderCode()); if (Objects.isNull(sendMessageVO)) { log.error("开始充电发送消息, 通过orderCode:{}, 查询SendMessageVO为null", dto.getOrderCode()); return null; } 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()); // 站点名称 // 订单编号 String orderCode = sendMessageVO.getOrderCode(); startChargingMessage.setOrderCode(orderCode); // 查询订单信息 OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode); if (orderBasicInfo == null) { return null; } String pileSn = orderBasicInfo.getPileSn(); Boolean result = YKCUtils.checkEVPileSn(pileSn); // 收费标准 CurrentTimePriceDetails currentTimePriceDetails = new CurrentTimePriceDetails(); if (result) { // 查询电动汽车收费标准 currentTimePriceDetails = pileBillingTemplateService.getCurrentTimePriceDetails(sendMessageVO.getStationId()); }else { // 查询电单车收费标准 currentTimePriceDetails = pileBillingTemplateService.getCurrentTimePriceDetailsForEBike(sendMessageVO.getStationId()); } // 收费标准 // CurrentTimePriceDetails currentTimePriceDetails = pileBillingTemplateService.getCurrentTimePriceDetailsByPileType(sendMessageVO.getStationId(),sendMessageVO.getPileSn()); 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()); if (Objects.isNull(sendMessageVO)) { log.error("停止充电发送消息, 通过orderCode:{}, 查询SendMessageVO为null", dto.getOrderCode()); return null; } msgInfo.setType("2"); // 2-结束充电推送消息 msgInfo.setTouser(sendMessageVO.getOpenId()); // 封装对象并调用发送消息的方法 AppletTemplateMessageSendDTO.StopChargingMessage stopChargingMessage = new AppletTemplateMessageSendDTO.StopChargingMessage(); msgInfo.setStopChargingMessage(stopChargingMessage); BigDecimal orderAmount = new BigDecimal(sendMessageVO.getOrderAmount()); BigDecimal discountAmount = new BigDecimal(sendMessageVO.getDiscountAmount()); stopChargingMessage.setChargingAmount(String.valueOf(orderAmount.subtract(discountAmount))); // 订单金额 2024.03.05改为 实付金额 stopChargingMessage.setEndReason(sendMessageVO.getStopReason()); // 结束原因 stopChargingMessage.setOrderCode(sendMessageVO.getOrderCode()); // 订单号 stopChargingMessage.setChargingDegree(sendMessageVO.getChargingDegree()); // 充电度数 String chargingTime = ""; if (StringUtils.isNotBlank(sendMessageVO.getChargeStartTime()) && StringUtils.isNotBlank(sendMessageVO.getChargeStopTime())) { 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); chargingTime = DateUtils.getDatePoor(chargeStopTime, chargeStartTime); } stopChargingMessage.setChargingTime(chargingTime); // 充电时长 return uniAppSendMsg(msgInfo); // 停止充电 } /** * 预约充电结果小程序服务通知 */ public Map reservationStartupResultSendMsg(String openId, String startTime, String endTime, String startUpResult, String failReason) { AppletTemplateMessageSendDTO msgInfo = new AppletTemplateMessageSendDTO(); msgInfo.setType(Constants.THREE); // 2-结束充电推送消息 msgInfo.setTouser(openId); // 封装对象并调用发送消息的方法 AppletTemplateMessageSendDTO.StartUpResultMessage startUpResultMessage = new AppletTemplateMessageSendDTO.StartUpResultMessage(); startUpResultMessage.setStartTime(startTime); startUpResultMessage.setEndTime(endTime); startUpResultMessage.setStartUpResult(startUpResult); startUpResultMessage.setFailReason(failReason); msgInfo.setStartUpResultMessage(startUpResultMessage); return uniAppSendMsg(msgInfo); // 预约充电结果 } /** * 小程序发送消息方法/小程序通知/服务通知 * @param dto * @return */ private Map uniAppSendMsg(AppletTemplateMessageSendDTO dto) { // 判断是什么场景调用此方法(1-开始充电推送消息;2-充电结束推送消息) String type = dto.getType(); // 根据不同的场景set不同的对象 Map map = Maps.newHashMap(); String templateId = null; if (StringUtils.equals("1", type)) { // 开始充电 templateId = startChargingTmpId; 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())); // 收费标准 } else if (StringUtils.equals("2", type)) { // 结束充电 templateId = stopChargingTmpId; map.put("character_string5", ImmutableMap.of("value", dto.getStopChargingMessage().getOrderCode())); // 充电金额 map.put("amount17", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingAmount())); // 充电金额 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())); // 充电度数 } else if (StringUtils.equals("3", type)) { // 预约充电启动通知 templateId = stopChargingTmpId; map.put("time2", ImmutableMap.of("value", dto.getStartUpResultMessage().getStartTime())); // 开始时间 map.put("time3", ImmutableMap.of("value", dto.getStartUpResultMessage().getEndTime())); // 结束时间 map.put("phrase20", ImmutableMap.of("value", dto.getStartUpResultMessage().getStartUpResult())); // 启动结果 map.put("thing21", ImmutableMap.of("value", dto.getStartUpResultMessage().getFailReason())); // 失败原因 } dto.setTemplate_id(templateId); 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; } }