2023-03-04 16:29:55 +08:00
|
|
|
|
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.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;
|
2023-03-18 15:32:29 +08:00
|
|
|
|
import com.jsowell.pile.service.IPileBillingTemplateService;
|
|
|
|
|
|
import com.jsowell.pile.vo.uniapp.CurrentTimePriceDetails;
|
2023-03-04 16:29:55 +08:00
|
|
|
|
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;
|
2023-03-18 15:32:29 +08:00
|
|
|
|
import java.text.ParseException;
|
|
|
|
|
|
import java.util.Date;
|
2023-03-04 16:29:55 +08:00
|
|
|
|
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;
|
|
|
|
|
|
|
2023-03-18 15:32:29 +08:00
|
|
|
|
@Autowired
|
|
|
|
|
|
private IPileBillingTemplateService pileBillingTemplateService;
|
|
|
|
|
|
|
2023-03-04 16:29:55 +08:00
|
|
|
|
@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 = "AccessToken_" + 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();
|
|
|
|
|
|
// 通过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<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 dto
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, String> 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()); // 站点名称
|
2023-03-18 15:32:29 +08:00
|
|
|
|
|
|
|
|
|
|
// 订单编号
|
|
|
|
|
|
startChargingMessage.setOrderCode(sendMessageVO.getOrderCode());
|
|
|
|
|
|
// 收费标准
|
|
|
|
|
|
CurrentTimePriceDetails currentTimePriceDetails = pileBillingTemplateService.getCurrentTimePriceDetails(sendMessageVO.getStationId());
|
|
|
|
|
|
startChargingMessage.setTotalPrice(currentTimePriceDetails.getTotalPrice() + " 元/度");
|
|
|
|
|
|
// 枪口编号
|
|
|
|
|
|
startChargingMessage.setPileConnectorCode(sendMessageVO.getPileSn() + "桩" + sendMessageVO.getConnectorCode() + "枪口");
|
|
|
|
|
|
|
2023-03-04 16:29:55 +08:00
|
|
|
|
return uniAppSendMsg(msgInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 停止充电发送消息
|
|
|
|
|
|
* @param dto
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
2023-03-18 15:32:29 +08:00
|
|
|
|
public Map<String, String> stopChargingSendMsg(WechatSendMsgDTO dto) throws ParseException {
|
2023-03-04 16:29:55 +08:00
|
|
|
|
// 通过订单号查询订单金额
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2023-03-18 15:32:29 +08:00
|
|
|
|
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());
|
|
|
|
|
|
// }
|
2023-03-04 16:29:55 +08:00
|
|
|
|
return uniAppSendMsg(msgInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 小程序发送消息方法
|
|
|
|
|
|
* @param dto
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
private Map<String, String> 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<String, Object> map = new HashMap<>();
|
|
|
|
|
|
map.put("thing5", ImmutableMap.of("value", dto.getStartChargingMessage().getStationName())); // 充电站名称
|
|
|
|
|
|
map.put("time2", ImmutableMap.of("value", dto.getStartChargingMessage().getStartTime())); // 开始时间
|
2023-03-18 15:32:29 +08:00
|
|
|
|
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())); // 收费标准
|
2023-03-04 16:29:55 +08:00
|
|
|
|
dto.setData(map);
|
|
|
|
|
|
} else if (StringUtils.equals("2", type)) {
|
|
|
|
|
|
// 结束充电
|
|
|
|
|
|
String templateId = stopChargingTmpId;
|
|
|
|
|
|
dto.setTemplate_id(templateId);
|
|
|
|
|
|
// dto.setPage("跳转的页面");
|
|
|
|
|
|
Map<String, Object> map = new HashMap<>();
|
2023-03-18 15:32:29 +08:00
|
|
|
|
map.put("character_string5", ImmutableMap.of("value", dto.getStopChargingMessage().getOrderCode())); // 充电金额
|
2023-03-04 16:29:55 +08:00
|
|
|
|
map.put("amount17", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingAmount())); // 充电金额
|
2023-03-18 15:32:29 +08:00
|
|
|
|
// map.put("time3", ImmutableMap.of("value", dto.getStopChargingMessage().getEndTime())); // 结束时间
|
2023-03-04 16:29:55 +08:00
|
|
|
|
map.put("thing7", ImmutableMap.of("value", dto.getStopChargingMessage().getEndReason())); // 结束原因
|
2023-03-18 15:32:29 +08:00
|
|
|
|
map.put("thing22", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingTime())); // 充电时长
|
|
|
|
|
|
map.put("number13", ImmutableMap.of("value", dto.getStopChargingMessage().getChargingDegree())); // 充电度数
|
2023-03-04 16:29:55 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|