This commit is contained in:
Lemon
2024-08-06 15:27:37 +08:00
20 changed files with 222 additions and 44 deletions

View File

@@ -141,7 +141,6 @@ public class PayController extends BaseController {
} else if (StringUtils.equals(dto.getRequestSource(), AdapayPayChannelEnum.ALIPAY_LITE.getValue())) {
dto.setAlipayAppId(appId);
}
map = orderService.payOrderV2(dto);
}
response = new RestApiResponse<>(map);

View File

@@ -1090,6 +1090,7 @@ public class OrderService {
private void paymentSucceeded(String data) throws JsonProcessingException {
// 验签成功 保存到回调记录表中
JSONObject jsonObject = JSON.parseObject(data);
log.info("adapay支付成功回调:{}", jsonObject.toJSONString());
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -1120,6 +1121,9 @@ public class OrderService {
// 支付金额
BigDecimal amount = adapayCallbackRecord.getPayAmt();
// 支付渠道
adapayCallbackRecord.setPayChannel(jsonObject.getString("pay_channel"));
// 保存到数据库
adapayCallbackRecordService.saveAdapayCallbackRecord(adapayCallbackRecord);

View File

@@ -1080,8 +1080,8 @@ public class SpringBootTestController {
*/
@Test
public void createPaymentReverseRequestTest() {
String paymentId = "002212024061717243610648707836701741056";
BigDecimal refundAmount = new BigDecimal("46.49");
String paymentId = "002212024080614142410666779361873702912";
BigDecimal refundAmount = new BigDecimal("0.64");
String memberId = null;
// 延迟分账未确认调撤销调撤销接口退款

View File

@@ -0,0 +1,38 @@
package com.jsowell.common;
import com.jsowell.common.util.BytesUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 友电电单车充电桩协议工具类
*/
@Slf4j
public class YouDianUtils {
/**
* 校验方法
* 整个数据包中的每个字节不包括校验字段本身将它们的数值累加起来。然后取累加和的低2字节16位作为校验字段的值
*/
public static boolean validateChecksum(byte[] bytes) {
if (bytes.length < 2) {
return false; // 校验字段长度不足时返回 false
}
// 计算累加和
int sum = 0;
for (int i = 0; i < bytes.length - 2; i++) {
sum += (bytes[i] & 0xFF); // 将每个字节视为无符号值进行累加
}
// 取累加和的低 2 字节16 位)
int calculatedChecksum = sum & 0xFFFF;
// 读取校验字段的值
byte[] checksumBytes = {bytes[bytes.length - 2], bytes[bytes.length - 1]};
int receivedChecksum = BytesUtil.bytesToIntLittle(checksumBytes);
// 比较计算的校验值和接收到的校验值
log.info("计算的校验值:{}, 接收到的校验值:{}", calculatedChecksum, receivedChecksum);
return calculatedChecksum == receivedChecksum;
}
}

View File

@@ -1,8 +1,29 @@
package com.jsowell.common.enums.adapay;
public enum AdapayPayChannelEnum {
WX_LITE("wx_lite", "微信小程序支付"),
alipay("alipay", "支付宝 App 支付"),
alipay_qr("alipay_qr", "支付宝正扫"),
alipay_wap("alipay_wap", "支付宝 H5 支付"),
ALIPAY_LITE("alipay_lite", "支付宝小程序支付"),
alipay_pub("alipay_pub", "支付宝生活号支付"),
alipay_scan("alipay_scan", "支付宝反扫"),
wx_pub("wx_pub", "微信公众号支付"),
WX_LITE("wx_lite", "微信小程序支付"),
wx_scan("wx_scan", "微信反扫"),
union("union", "银联云闪付 App"),
union_qr("union_qr", "银联云闪付正扫"),
union_wap("union_wap", "银联云闪付 H5 支付"),
union_scan("union_scan", "银联云闪付反扫"),
union_online("union_online", "银联 H5 支付"),
union_checkout("union_checkout", "银联统一收银台支付"),
fast_pay("fast_pay", "快捷支付"),
b2c("b2c", "个人网银支付"),
b2b("b2b", "企业网银支付"),
;
private String value;
private String label;

View File

@@ -13,7 +13,7 @@ import java.util.List;
public class MessageDecode extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
log.info("MessageDecode.decode");
// log.info("MessageDecode.decode");
// 检查是否有足够的字节可以读取
if (in.readableBytes() < 14) { // 最小长度包头3 + 长度2 + 物理ID4 + 消息ID2 + 命令1 + 校验2
return;

View File

@@ -17,7 +17,7 @@ public class StartAndLengthFieldFrameDecoder extends ByteToMessageDecoder {
public StartAndLengthFieldFrameDecoder() {}
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
log.info("StartAndLengthFieldFrameDecoder.decode");
// log.info("StartAndLengthFieldFrameDecoder.decode");
// 记录包头开始的index
int beginReader;

View File

@@ -6,6 +6,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -24,37 +26,64 @@ public class ChargingPileMessage {
private byte[] data; // 数据 (n字节)
private int checksum; // 校验 (2字节)
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.JSON_STYLE)
.append("header", header)
.append("length", length)
.append("physicalId", physicalId)
.append("messageId", messageId)
.append("command", command)
.append("data", data)
.append("checksum", checksum)
.toString();
}
// 从字节数组解析消息
public static ChargingPileMessage parseMessage(byte[] messageBytes) {
log.info("parseMessage:{}", BytesUtil.binary(messageBytes, 16));
ChargingPileMessage message = new ChargingPileMessage();
// log.info("parseMessage:{}", BytesUtil.binary(messageBytes, 16));
// 读取包头
byte[] headerBytes = Arrays.copyOfRange(messageBytes, 0, 3);
System.out.println(new String(headerBytes, StandardCharsets.UTF_8));
String header = new String(headerBytes, StandardCharsets.UTF_8);
message.setHeader(header);
// System.out.println("包头:" + header);
// 读取长度
byte[] lengthBytes = {messageBytes[3], messageBytes[4]};
System.out.println(BytesUtil.bcd2StrLittle(lengthBytes));
int length = BytesUtil.bytesToIntLittle(lengthBytes);
message.setLength(length);
// System.out.println("长度:" + length);
// 读取物理ID
byte[] physicalIdBytes = Arrays.copyOfRange(messageBytes, 5, 9);
System.out.println(BytesUtil.bcd2StrLittle(physicalIdBytes));
int physicalId = BytesUtil.bytesToIntLittle(physicalIdBytes);
message.setPhysicalId(physicalId);
// System.out.println("物理ID:" + physicalId);
// 读取消息ID
byte[] messageIdBytes = {messageBytes[9], messageBytes[10]};
System.out.println(BytesUtil.bcd2StrLittle(messageIdBytes));
int messageId = BytesUtil.bytesToIntLittle(messageIdBytes);
message.setMessageId(messageId);
// System.out.println("消息ID:" + messageId);
// 读取命令
byte command = messageBytes[11];
System.out.println(BytesUtil.bcd2StrLittle(new byte[] {command}));
message.setChecksum(command);
// System.out.println("命令:" + BytesUtil.bcd2StrLittle(new byte[] {command}));
// 读取数据
byte[] data = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2);
System.out.println(BytesUtil.bcd2StrLittle(data));
byte[] dataBytes = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2);
message.setData(dataBytes);
String data = BytesUtil.bcd2StrLittle(dataBytes);
// System.out.println("数据:" + data);
// 读取校验
byte[] checksumBytes = {messageBytes[messageBytes.length - 2], messageBytes[messageBytes.length - 1]};
System.out.println(BytesUtil.bcd2StrLittle(checksumBytes));
return null;
int checksum = BytesUtil.bytesToIntLittle(checksumBytes);
message.setChecksum(checksum);
// System.out.println("校验:" + checksum);
log.info("报文:{}, parseMessage:{}", BytesUtil.binary(messageBytes, 16), message.toString());
return message;
}
}

View File

@@ -5,7 +5,9 @@ import com.google.common.primitives.Bytes;
import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
import com.jsowell.common.enums.ykc.ChargingFailedReasonEnum;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.YKCOperateFactory;
import com.jsowell.pile.dto.ReservationChargingStartupResult;
@@ -72,35 +74,34 @@ public class ReservationChargingStartupResultHandler extends AbstractHandler{
length = 1;
byte[] startupResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String startupResult = BytesUtil.bcd2Str(startupResultByteArr);
String startupResultMsg = StringUtils.equals(startupResult, "00") ? "失败" : "成功";
// 失败原因
startIndex += length;
length = 1;
byte[] failReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String failReason = BytesUtil.bcd2Str(failReasonByteArr);
String failReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failReason, 16));
log.info("[===预约充电启动结果上送===]交易流水号:{}, 桩编号:{}, 枪号:{}, vin:{}, 启动结果:{}, 失败原因:{}",
transactionCode, pileSn, connectorCode, vinCode, startupResult, failReason);
transactionCode, pileSn, connectorCode, vinCode, startupResultMsg, failReasonMsg);
ReservationChargingStartupResult chargingStartupResult = ReservationChargingStartupResult.builder()
.transactionCode(transactionCode)
.pileSn(pileSn)
.connectorCode(connectorCode)
.vinCode(vinCode)
.startupResult(startupResult)
.failReason(failReason)
.startupResult(startupResultMsg)
.failReason(failReasonMsg)
.build();
pileBasicInfoService.startupResult(chargingStartupResult);
/*
应答
确认结果 0x00 成功 0x01 失败
*/
byte[] confirmResultBytes = Constants.zeroByteArray;
byte[] concatMsgBody = Bytes.concat(transactionCodeByteArr, pileSnByteArr, connectorCodeByteArr, confirmResultBytes);
return getResult(ykcDataProtocol, concatMsgBody);
}
}

View File

@@ -84,6 +84,9 @@ public class AdapayCallbackRecord implements Serializable {
private BigDecimal payAmt;
/**
* 支付渠道
*/
private String payChannel;
private BigDecimal realAmt;

View File

@@ -2,6 +2,7 @@ package com.jsowell.pile.mapper;
import java.util.List;
import com.jsowell.pile.domain.PileMemberRelation;
import com.jsowell.pile.vo.uniapp.customer.MemberVO;
import org.springframework.stereotype.Repository;
/**
@@ -71,4 +72,6 @@ public interface PileMemberRelationMapper
int deleteRelationByIds(List<Integer> ids);
List<MemberVO> selectMemberList(String pileSn);
}

View File

@@ -1,6 +1,7 @@
package com.jsowell.pile.service;
import com.jsowell.pile.domain.PileMemberRelation;
import com.jsowell.pile.vo.uniapp.customer.MemberVO;
import java.util.List;
@@ -78,4 +79,5 @@ public interface PileMemberRelationService
*/
List<PileMemberRelation> selectPileMemberRelationByPileSn(String pileSn);
List<MemberVO> selectMemberList(String pileSn);
}

View File

@@ -34,6 +34,8 @@ public interface PileReservationInfoService {
List<PileReservationInfo> getReservationsByMemberIdAndPileSn(String memberId, String pileSn);
PileReservationInfo selectByPileConnectorCode(String pileConnectorCode);
/**
* 启动预约
* @param dto

View File

@@ -14,10 +14,7 @@ import com.jsowell.common.enums.lianlian.LianLianPileStatusEnum;
import com.jsowell.common.enums.ykc.*;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.*;
import com.jsowell.pile.domain.PileBasicInfo;
import com.jsowell.pile.domain.PileConnectorInfo;
import com.jsowell.pile.domain.PileModelInfo;
import com.jsowell.pile.domain.PileSimInfo;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.mapper.PileBasicInfoMapper;
import com.jsowell.pile.service.*;
@@ -28,9 +25,11 @@ import com.jsowell.pile.thirdparty.EquipmentInfo;
import com.jsowell.pile.thirdparty.ZDLConnectorInfo;
import com.jsowell.pile.thirdparty.ZDLEquipmentInfo;
import com.jsowell.pile.util.UserUtils;
import com.jsowell.pile.vo.PileReservationInfoVO;
import com.jsowell.pile.vo.base.MerchantInfoVO;
import com.jsowell.pile.vo.base.PileInfoVO;
import com.jsowell.pile.vo.uniapp.customer.GroundLockInfoVO;
import com.jsowell.pile.vo.uniapp.customer.MemberVO;
import com.jsowell.pile.vo.uniapp.customer.PersonalPileInfoVO;
import com.jsowell.pile.vo.uniapp.customer.PileConnectorDetailVO;
import com.jsowell.pile.vo.web.*;
@@ -44,6 +43,8 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Time;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -93,6 +94,9 @@ public class PileBasicInfoServiceImpl implements PileBasicInfoService {
@Autowired
private OrderBasicInfoService orderBasicInfoService;
@Autowired
private PileMemberRelationService pileMemberRelationService;
/**
* 查询设备管理
*
@@ -1226,7 +1230,20 @@ public class PileBasicInfoServiceImpl implements PileBasicInfoService {
// 创建订单
orderBasicInfoService.createReservationOrder(chargingStartupResult);
// 小程序通知
wxAppletRemoteService.reservationStartupResultSendMsg(chargingStartupResult);
// 查预约信息
String pileConnectorCode = chargingStartupResult.getPileSn() + chargingStartupResult.getConnectorCode();
PileReservationInfo pileReservationInfo = pileReservationInfoService.selectByPileConnectorCode(pileConnectorCode);
LocalDateTime[] localDateTimes = DateUtils.convertStartAndEndTime(pileReservationInfo.getStartTime().toString(), pileReservationInfo.getEndTime().toString());
// 发小程序通知消息
String startTime = DateUtils.formatDateTime(localDateTimes[0]);
String endTime = DateUtils.formatDateTime(localDateTimes[1]);
String startUpResult = chargingStartupResult.getStartupResult();
String failReason = chargingStartupResult.getFailReason();
// 根据pileSn 查询使用者列表
List<MemberVO> memberVOS = pileMemberRelationService.selectMemberList(chargingStartupResult.getPileSn());
for (MemberVO memberVO : memberVOS) {
wxAppletRemoteService.reservationStartupResultSendMsg(memberVO.getOpenId(), startTime, endTime, startUpResult, failReason);
}
}
}

View File

@@ -7,6 +7,7 @@ import com.jsowell.pile.domain.PileMemberRelation;
import com.jsowell.pile.mapper.PileMemberRelationMapper;
import com.jsowell.pile.service.PileConnectorInfoService;
import com.jsowell.pile.service.PileMemberRelationService;
import com.jsowell.pile.vo.uniapp.customer.MemberVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -118,4 +119,8 @@ public class PileMemberRelationServiceImpl implements PileMemberRelationService
return selectPileMemberRelationList(pileMemberRelation);
}
public List<MemberVO> selectMemberList(String pileSn) {
return pileMemberRelationMapper.selectMemberList(pileSn);
}
}

View File

@@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.Date;
import java.sql.Time;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -127,6 +126,7 @@ public class PileReservationInfoServiceImpl implements PileReservationInfoServic
return pileReservationInfoMapper.findByMemberIdAndPileSn(memberId, pileSn);
}
@Override
public PileReservationInfo selectByPileConnectorCode(String pileConnectorCode) {
return pileReservationInfoMapper.selectByPileConnectorCode(pileConnectorCode);
}

View File

@@ -25,6 +25,11 @@ public class MemberVO {
*/
private String memberId;
/**
* 微信openId
*/
private String openId;
/**
* 会员所属一级运营商
*/

View File

@@ -30,6 +30,8 @@ public class AppletTemplateMessageSendDTO implements Serializable {
private StopChargingMessage stopChargingMessage;
private StartUpResultMessage startUpResultMessage;
@Data
public static class StartChargingMessage {
@@ -100,4 +102,33 @@ public class AppletTemplateMessageSendDTO implements Serializable {
*/
private String chargingTime;
}
@Data
public static class StartUpResultMessage {
/**
* 开始时间
* time2
*/
private String startTime;
/**
* 预计结束时间
* time3
*/
private String endTime;
/**
* 启动结果
* phrase20
*/
private String startUpResult;
/**
* 失败原因
* thing21
*/
private String failReason;
}
}

View File

@@ -234,7 +234,7 @@ public class WxAppletRemoteService {
// 枪口编号
startChargingMessage.setPileConnectorCode(sendMessageVO.getPileSn() + "" + sendMessageVO.getConnectorCode() + "枪口");
return uniAppSendMsg(msgInfo);
return uniAppSendMsg(msgInfo); // 开始充电
}
/**
@@ -269,23 +269,25 @@ public class WxAppletRemoteService {
Date chargeStopTime = DateUtils.parseDate(sendMessageVO.getChargeStopTime(), DateUtils.YYYY_MM_DD_HH_MM_SS);
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);
return uniAppSendMsg(msgInfo); // 停止充电
}
/**
* 预约充电结果小程序服务通知
*/
public Map<String, String> reservationStartupResultSendMsg(ReservationChargingStartupResult chargingStartupResult) {
public Map<String, String> reservationStartupResultSendMsg(String openId, String startTime, String endTime, String startUpResult, String failReason) {
AppletTemplateMessageSendDTO msgInfo = new AppletTemplateMessageSendDTO();
return uniAppSendMsg(msgInfo);
msgInfo.setType("2"); // 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); // 预约充电结果
}
/**
@@ -301,7 +303,6 @@ public class WxAppletRemoteService {
// 开始充电
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())); // 开始时间
@@ -313,15 +314,23 @@ public class WxAppletRemoteService {
// 结束充电
String templateId = stopChargingTmpId;
dto.setTemplate_id(templateId);
// dto.setPage("跳转的页面");
Map<String, Object> 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);
} else if (StringUtils.equals("3", type)) {
// 结束充电
String templateId = stopChargingTmpId;
dto.setTemplate_id(templateId);
Map<String, Object> map = new HashMap<>();
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())); // 失败原因
}
// 调用下面的发送消息接口
return uniformMessageSend(dto);

View File

@@ -92,4 +92,13 @@
#{id}
</foreach>
</delete>
<select id="selectMemberList" resultType="com.jsowell.pile.vo.uniapp.customer.MemberVO">
select
t1.member_id as memberId,
t2.open_id as openId
from pile_member_relation t1
left join member_basic_info t2 on t2.member_id = t1.member_id and t2.del_flag = '0'
where t1.pile_sn = #{pileSn,jdbcType=VARCHAR}
</select>
</mapper>