Files
jsowell-charger-web/jsowell-pile/src/main/java/com/jsowell/wxpay/service/WxAppletRemoteService.java
2025-12-15 10:06:15 +08:00

394 lines
16 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<String, Object> 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();
// 通过tokencode前端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<String, Object> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> uniAppSendMsg(AppletTemplateMessageSendDTO dto) {
// 判断是什么场景调用此方法1-开始充电推送消息2-充电结束推送消息)
String type = dto.getType();
// 根据不同的场景set不同的对象
Map<String, Object> 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<String, String> 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<String, Object> 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<String, String> resultMap = new HashMap<>();
resultMap.put("code", errcode);
resultMap.put("message", errmsg);
return resultMap;
}
}