From eba790b3e043a217bf89ab6d818b3149511b5766 Mon Sep 17 00:00:00 2001
From: 18650502300 <18650502300@163.com>
Date: Tue, 28 May 2024 16:55:38 +0800
Subject: [PATCH] =?UTF-8?q?1.=E4=B8=8D=E5=90=8C=E5=9C=BA=E6=99=AF=E4=B8=8B?=
=?UTF-8?q?=E5=8F=91=E9=80=81=E4=B8=8D=E5=90=8C=E7=9A=84=E8=AF=AD=E9=9F=B3?=
=?UTF-8?q?=E5=91=BD=E4=BB=A4=202.=E6=8E=A8=E9=80=81=E7=BB=8F=E5=BA=A6?=
=?UTF-8?q?=E7=BA=AC=E5=BA=A6=E6=95=B0=E6=8D=AE=E5=88=B0=E5=90=8E=E5=8F=B0?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/controller/iot/domain/BodyObj.java | 29 +++
.../web/controller/iot/domain/DataPonit.java | 31 +++
.../web/controller/iot/domain/LogEntry.java | 39 ++++
.../iot/receive/ReceiveController.java | 191 ++++++++++++++++++
.../ruoyi/web/controller/iot/util/Util.java | 141 +++++++++++++
.../controller/system/AsUserController.java | 16 ++
.../src/main/resources/application.yml | 4 +-
.../com/ruoyi/common/config/WxPayConfig.java | 16 ++
.../ruoyi/common/constant/IotConstants.java | 64 +++++-
.../com/ruoyi/common/utils/CommonUtil.java | 40 ++++
.../framework/config/SecurityConfig.java | 1 +
.../ruoyi/system/domain/EtOperatingArea.java | 2 +
.../ruoyi/system/mapper/AsDeviceMapper.java | 2 +-
.../system/service/IAsDeviceService.java | 13 +-
.../ruoyi/system/service/IWxPayService.java | 29 ++-
.../service/impl/AsDeviceServiceImpl.java | 95 ++++++++-
.../service/impl/CallbackServiceImpl.java | 42 +---
.../service/impl/EtOrderServiceImpl.java | 52 +----
.../system/service/impl/WxPayService.java | 102 +++++-----
.../java/com/ruoyi/system/task/EtTask.java | 24 ++-
20 files changed, 773 insertions(+), 160 deletions(-)
create mode 100644 electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/BodyObj.java
create mode 100644 electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/DataPonit.java
create mode 100644 electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/LogEntry.java
create mode 100644 electripper-admin/src/main/java/com/ruoyi/web/controller/iot/receive/ReceiveController.java
create mode 100644 electripper-admin/src/main/java/com/ruoyi/web/controller/iot/util/Util.java
diff --git a/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/BodyObj.java b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/BodyObj.java
new file mode 100644
index 0000000..a42a45a
--- /dev/null
+++ b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/BodyObj.java
@@ -0,0 +1,29 @@
+package com.ruoyi.web.controller.iot.domain;
+
+import lombok.Data;
+
+/**
+ * onenet接收到的body对象
+ *
+ * @author ruoyi
+ */
+
+@Data
+public class BodyObj {
+
+ /** 设备推送数据,包括设备的生命周期,数据点,物模型属性、事件、服务等 */
+ private Object msg;
+
+ /** 用于计算签名字符的随机串 */
+ private String nonce;
+
+ /** 加密签名,用以校验推送客户端身份合法性,校验方法见实例验证 */
+ private String signature;
+
+ /** 推送时间戳(毫秒) */
+ private Long time;
+
+ /** 消息ID */
+ private String id;
+
+}
diff --git a/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/DataPonit.java b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/DataPonit.java
new file mode 100644
index 0000000..1aeeebc
--- /dev/null
+++ b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/DataPonit.java
@@ -0,0 +1,31 @@
+package com.ruoyi.web.controller.iot.domain;
+
+import lombok.Data;
+
+/**
+ * 数据点数据
+ *
+ * @author ruoyi
+ */
+
+@Data
+public class DataPonit {
+
+ /** 设备名称 */
+ private String dev_name;
+
+ /** 设备上报的时间戳 */
+ private Long at;
+
+ /** 产品id */
+ private String pid;
+
+ /** 固定值:1 */
+ private Integer type;
+
+ /** 数据点id */
+ private String ds_id;
+
+ /** 消息值 */
+ private String value;
+}
diff --git a/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/LogEntry.java b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/LogEntry.java
new file mode 100644
index 0000000..b04456e
--- /dev/null
+++ b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/domain/LogEntry.java
@@ -0,0 +1,39 @@
+package com.ruoyi.web.controller.iot.domain;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * onenet接收到的车辆定位日志对象
+ * */
+@Data
+public class LogEntry {
+
+ //mac号
+ @JsonProperty("dev_name")
+ private String devName;
+
+ private long at;
+
+ private String pid;
+
+ private int type;
+
+ @JsonProperty("ds_id")
+ private String dsId;
+
+ private LocationValue value;
+
+ @Data
+ public class LocationValue {
+ private String lon;//经度
+
+ private String lat;//纬度
+
+ private Integer status;//电动车状态 0断电,1上电运行 2轮动抱死 3超出区域断电(远程下发了qlose)
+
+ private Integer bat;//电池电压 "bat":571 ==> 57.1V
+
+ private Integer csq;//信号强度
+ }
+}
diff --git a/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/receive/ReceiveController.java b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/receive/ReceiveController.java
new file mode 100644
index 0000000..5fcc438
--- /dev/null
+++ b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/receive/ReceiveController.java
@@ -0,0 +1,191 @@
+package com.ruoyi.web.controller.iot.receive;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.constant.IotConstants;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.CommonUtil;
+import com.ruoyi.common.utils.onenet.Token;
+import com.ruoyi.system.domain.AsDevice;
+import com.ruoyi.system.domain.EtModel;
+import com.ruoyi.system.domain.EtOperatingArea;
+import com.ruoyi.system.mapper.AsDeviceMapper;
+import com.ruoyi.system.service.IAsDeviceService;
+import com.ruoyi.system.service.IEtModelService;
+import com.ruoyi.system.service.IEtOperatingAreaService;
+import com.ruoyi.web.controller.iot.domain.BodyObj;
+import com.ruoyi.web.controller.iot.domain.LogEntry;
+import com.ruoyi.web.controller.iot.util.Util;
+import lombok.SneakyThrows;
+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.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * 接收硬件参数
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/common")
+public class ReceiveController {
+
+ private static final Logger log = LoggerFactory.getLogger(ReceiveController.class);
+
+ @Value(value = "${iot.token}")
+ private String token;
+
+
+ @Resource
+ private IAsDeviceService asDeviceService;
+
+ @Autowired
+ private IEtModelService etModelService;
+
+ @Resource
+ private IEtOperatingAreaService etOperatingAreaService;
+
+ /**
+ * 功能描述:第三方平台数据接收。
+ *
注:
+ * - 1.OneNet平台为了保证数据不丢失,有重发机制,如果重复数据对业务有影响,数据接收端需要对重复数据进行排除重复处理。
+ * - 2.OneNet每一次post数据请求后,等待客户端的响应都设有时限,在规定时限内没有收到响应会认为发送失败。
+ * 接收程序接收到数据时,尽量先缓存起来,再做业务逻辑处理。
+ *
+ * @param body 数据消息
+ * @return 任意字符串。OneNet平台接收到http 200的响应,才会认为数据推送成功,否则会重发。
+ */
+ @RequestMapping(value = "/receive",method = RequestMethod.POST)
+ @ResponseBody
+ @SneakyThrows
+ @Transactional
+ public String receive(@RequestBody String body){
+
+ log.info("receive方法接收到参数: body String --- " +body);
+ /************************************************
+ * 解析数据推送请求,非加密模式。
+ * 如果是明文模式使用以下代码
+ **************************************************/
+ /*************明文模式 start****************/
+ BodyObj obj = Util.resolveBody(body, false);
+ log.info("receive方法解析对象: body Object --- " + JSON.toJSONString(obj));
+ if (obj != null){
+ boolean dataRight = Util.checkSignature(obj, token);
+ if (dataRight){
+ log.info("receive方法验证签名正确: content" + JSON.toJSONString(obj));
+ String msg = (String)obj.getMsg();
+ log.info("receive方法-获取到消息体: msg---" +msg);
+ LogEntry logEntry = JSONObject.parseObject(msg, LogEntry.class);
+ log.info("logEntry转换后的对象: logEntry---【{}】" , JSON.toJSONString(logEntry));
+ LogEntry.LocationValue value = logEntry.getValue();
+ if(IotConstants.ONENET_LOCATION.equals(logEntry.getDsId()) && ObjectUtil.isNotNull(value)){
+ /**如果是定位日志则,获取到车辆mac,找到对应车辆
+ * 1.更新车辆定位、计算续航里程
+ * 2.判断是否在禁行区内,如果在,根据配置‘禁行区内断电配置’进行断电,
+ * 3.超出运营区外断电
+ * 4.行程线路添加,更新订单中的trip_route字段
+ * 5.低电量不能骑行,如果电量低则声音播报
+ * */
+ /** 1.更新车辆定位、电压;计算续航里程 */
+ AsDevice device = asDeviceService.selectAsDeviceByMac(logEntry.getDevName());
+ EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(device.getAreaId());
+ if(ObjectUtil.isNotNull(device)){
+ device.setLatitude(value.getLon());
+ device.setLongitude(value.getLat());
+ Integer bat = value.getBat();
+ double voltage = (double) bat / 10;//电压
+ device.setVoltage(String.valueOf(voltage));//电压
+ // 根据电压计算续航里程
+ EtModel model = etModelService.selectEtModelByModelId(device.getModelId());
+ Integer remainingMileage = CommonUtil.getRemainingMileage(device.getVoltage(), model.getFullVoltage(), model.getLowVoltage(), model.getFullEndurance());
+ device.setRemainingMileage(remainingMileage);
+ int i = asDeviceService.updateAsDeviceBySn(device);
+ if(i>0){
+ log.info("更新定位成功:" +logEntry.getDevName());
+ /** 2. 判断是否在禁行区内
+ * 如果在, 根据配置‘禁行区内断电配置’进行断电
+ **/
+ boolean noRidingArea = asDeviceService.isNoRidingArea(device.getSn(), device.getAreaId());
+ if(noRidingArea){
+ String noRidingOutage = area.getNoRidingOutage();
+ //发送播报指令
+ asDeviceService.sendCommand(device.getMac(), Token.getToken(),IotConstants.COMMAND_PLAY3,"禁行区内播报");
+ if(noRidingOutage.equals("1")){//禁行区内断电
+ //发送断电命令
+ asDeviceService.sendCommand(device.getMac(), Token.getToken(),IotConstants.COMMAND_QLOSE,"禁行区内断电");
+ log.info("禁行区内发送断电命令:" +logEntry.getDevName());
+ }
+ }
+ /** 3.超出运营区外断电*/
+ boolean isAreaZone = asDeviceService.isAreaZone(device.getSn(), area);
+ if(isAreaZone){
+ String areaOutOutage = area.getAreaOutOutage();
+ //发送超出营运区播报指令
+ asDeviceService.sendCommand(device.getMac(), Token.getToken(),IotConstants.COMMAND_PLAY3,"超出营运区播报");
+ if(areaOutOutage.equals("1")){//超出营运区断电
+ //发送断电命令
+ asDeviceService.sendCommand(device.getMac(), Token.getToken(),IotConstants.COMMAND_QLOSE,"超出营运区断电");
+ log.info("超出营运区发送断电命令:" +logEntry.getDevName());
+ }
+ }
+ /** todo 4.行程线路添加,更新订单中的trip_route字段 */
+
+
+ /** 5.低电量不能骑行,如果电量低则声音播报 */
+ Integer electricQuantity = CommonUtil.getElectricQuantity(device.getVoltage(), model.getFullVoltage(), model.getLowVoltage());
+ if(electricQuantity <=model.getLowBatteryReminder()){
+ //发送低电量播报指令
+ asDeviceService.sendCommand(device.getMac(), Token.getToken(),IotConstants.COMMAND_PLAY6,"低电量播报");
+ log.info("低电量播报:" +logEntry.getDevName());
+ }
+ }else{
+ log.info("更新定位失败:" +logEntry.getDevName());
+ }
+ }else{
+ log.info("未找到车辆对象:" +logEntry.getDevName());
+ }
+
+ }
+ }else {
+ log.info("receive方法验证签名错误: signature error");
+ }
+
+ }else {
+ log.info("receive方法参数为空: body empty error");
+ }
+ /*************明文模式 end****************/
+ return "ok";
+ }
+
+ /**
+ * 功能说明: URL&Token验证接口。如果验证成功返回msg的值,否则返回其他值。
+ * @param msg 验证消息
+ * @param nonce 随机串
+ * @param signature 签名
+ * @return msg值
+ */
+
+ @RequestMapping(value = "/receive", method = RequestMethod.GET)
+ @ResponseBody
+ public String check(@RequestParam(value = "msg") String msg,
+ @RequestParam(value = "nonce") String nonce,
+ @RequestParam(value = "signature") String signature) throws UnsupportedEncodingException {
+
+ log.info("check方法接收到参数:: msg:{} nonce{} signature:{}",msg,nonce,signature);
+ if (Util.checkToken(msg,nonce,signature,token)){
+ log.info("校验成功",msg,nonce,signature);
+ return msg;
+ }else {
+ log.info("校验失败",msg,nonce,signature);
+ return "error";
+ }
+
+ }
+}
diff --git a/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/util/Util.java b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/util/Util.java
new file mode 100644
index 0000000..6616b97
--- /dev/null
+++ b/electripper-admin/src/main/java/com/ruoyi/web/controller/iot/util/Util.java
@@ -0,0 +1,141 @@
+package com.ruoyi.web.controller.iot.util;
+
+import cn.hutool.json.JSONObject;
+import com.ruoyi.web.controller.iot.domain.BodyObj;
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.*;
+
+
+/**
+ * 功能描述: OneNet数据推送接收程序工具类。
+ *
+ * Created by Roy on 2017/5/17.
+ *
+ */
+public class Util {
+
+ private static Logger logger = LoggerFactory.getLogger(Util.class);
+
+ private static MessageDigest mdInst;
+
+ static {
+ try {
+ mdInst = MessageDigest.getInstance("MD5");
+ Security.addProvider(new BouncyCastleProvider());
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 功能描述:在OneNet平台配置数据接收地址时,平台会发送URL&token验证请求
+ * 使用此功能函数验证token
+ * @param msg 请求参数 的值
+ * @param nonce 请求参数 的值
+ * @param signature 请求参数 的值
+ * @param token OneNet平台配置页面token的值
+ * @return token检验成功返回true;token校验失败返回false
+ */
+ public static boolean checkToken(String msg,String nonce,String signature, String token) throws UnsupportedEncodingException {
+
+ byte[] paramB = new byte[token.length() + 8 + msg.length()];
+ System.arraycopy(token.getBytes(), 0, paramB, 0, token.length());
+ System.arraycopy(nonce.getBytes(), 0, paramB, token.length(), 8);
+ System.arraycopy(msg.getBytes(), 0, paramB, token.length() + 8, msg.length());
+ String sig = com.sun.org.apache.xerces.internal.impl.dv.util.Base64.encode(mdInst.digest(paramB));
+ logger.info("url&token validation: result {}, detail receive:{} calculate:{}", sig.equals(signature.replace(' ','+')),signature,sig);
+ return sig.equals(signature.replace(' ','+'));
+ }
+
+ /**
+ * 功能描述: 检查接收数据的信息摘要是否正确。
+ * 方法非线程安全。
+ * @param obj 消息体对象
+ * @param token OneNet平台配置页面token的值
+ * @return
+ */
+ public static boolean checkSignature(BodyObj obj, String token) {
+ //计算接受到的消息的摘要
+ //token长度 + 8B随机字符串长度 + 消息长度
+ byte[] signature = new byte[token.length() + 8 + obj.getMsg().toString().length()];
+ System.arraycopy(token.getBytes(), 0, signature, 0, token.length());
+ System.arraycopy(obj.getNonce().getBytes(), 0, signature, token.length(), 8);
+ System.arraycopy(obj.getMsg().toString().getBytes(), 0, signature, token.length() + 8, obj.getMsg().toString().length());
+ String calSig = Base64.encodeBase64String(mdInst.digest(signature));
+ logger.info("check signature: result:{} receive sig:{},calculate sig: {}",calSig.equals(obj.getSignature()),obj.getSignature(),calSig);
+ return calSig.equals(obj.getSignature());
+ }
+
+ /**
+ * 功能描述 解密消息
+ * @param obj 消息体对象
+ * @param encodeKey OneNet平台第三方平台配置页面为用户生成的AES的BASE64编码格式秘钥
+ * @return
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ * @throws InvalidKeyException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ */
+ public static String decryptMsg(BodyObj obj, String encodeKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+ byte[] encMsg = Base64.decodeBase64(obj.getMsg().toString());
+ byte[] aeskey = Base64.decodeBase64(encodeKey + "=");
+ SecretKey secretKey = new SecretKeySpec(aeskey, 0, 32, "AES");
+ Cipher cipher = null;
+ cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(aeskey, 0, 16));
+ byte[] allmsg = cipher.doFinal(encMsg);
+ byte[] msgLenBytes = new byte[4];
+ System.arraycopy(allmsg, 16, msgLenBytes, 0, 4);
+ int msgLen = getMsgLen(msgLenBytes);
+ byte[] msg = new byte[msgLen];
+ System.arraycopy(allmsg, 20, msg, 0, msgLen);
+ return new String(msg);
+ }
+
+ /**
+ * 功能描述 解析数据推送请求,生成code>BodyObj消息对象
+ * @param body 数据推送请求body部分
+ * @param encrypted 表征是否为加密消息
+ * @return 生成的BodyObj
消息对象
+ */
+ public static BodyObj resolveBody(String body, boolean encrypted) {
+ JSONObject jsonMsg = new JSONObject(body);
+ BodyObj obj = new BodyObj();
+ obj.setNonce(jsonMsg.getStr("nonce"));
+ obj.setSignature(jsonMsg.getStr("signature"));
+ if (encrypted) {
+ if (!jsonMsg.containsKey("enc_msg")) {
+ return null;
+ }
+ obj.setMsg(jsonMsg.getStr("enc_msg"));
+ } else {
+ if (!jsonMsg.containsKey("msg")) {
+ return null;
+ }
+ obj.setMsg(jsonMsg.get("msg"));
+ }
+ return obj;
+ }
+
+ private static int getMsgLen(byte[] arrays) {
+ int len = 0;
+ len += (arrays[0] & 0xFF) << 24;
+ len += (arrays[1] & 0xFF) << 16;
+ len += (arrays[2] & 0xFF) << 8;
+ len += (arrays[3] & 0xFF);
+ return len;
+ }
+
+
+}
diff --git a/electripper-admin/src/main/java/com/ruoyi/web/controller/system/AsUserController.java b/electripper-admin/src/main/java/com/ruoyi/web/controller/system/AsUserController.java
index dea52e4..802e3c8 100644
--- a/electripper-admin/src/main/java/com/ruoyi/web/controller/system/AsUserController.java
+++ b/electripper-admin/src/main/java/com/ruoyi/web/controller/system/AsUserController.java
@@ -4,13 +4,17 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.AsUser;
+import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.service.IAsUserService;
+import com.ruoyi.system.service.ISysUserService;
import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -31,6 +35,9 @@ public class AsUserController extends BaseController
@Resource
private IAsUserService asUserService;
+ @Autowired
+ private ISysUserService sysUserService;
+
/**
* 获取用户列表
*/
@@ -131,6 +138,15 @@ public class AsUserController extends BaseController
@PutMapping("/bandSystemUser")
public AjaxResult bandSystemUser(@RequestBody AsUser user)
{
+ Long sysUserId = user.getSysUserId();
+ SysUser sysUser = sysUserService.selectUserById(sysUserId);
+ if(sysUser.getUserName().equals(SpringUtils.getRequiredProperty("et.repairAdmin"))){
+ user.setRole("2");
+ }else if(sysUser.getUserName().equals(SpringUtils.getRequiredProperty("et.operateAdmin"))){
+ user.setRole("3");
+ }else{
+ user.setRole("1");
+ }
return toAjax(asUserService.bandSystemUser(user));
}
diff --git a/electripper-admin/src/main/resources/application.yml b/electripper-admin/src/main/resources/application.yml
index b779bc3..5314e02 100644
--- a/electripper-admin/src/main/resources/application.yml
+++ b/electripper-admin/src/main/resources/application.yml
@@ -222,7 +222,7 @@ iot:
# token过期时间
daysToExpire: 100
# 推送消息token
- token: tVpNdGKrAFHfKZNgpIWQfZukrcYHNfFM
+ token: JZWgouXXNcgTbxCyRCLKbQkKQMhyUrfL
geo:
# 高德地图key web服务 手续费
key: 834f1f029671d84272554528311ff0f1
@@ -231,3 +231,5 @@ et:
handlingCharge: 4
verifyUrl: https://zidv2.market.alicloudapi.com/idcheck/Post
appcode: 32b6c6445b1a42ed862dd4202392c47d
+ repairAdmin: wx
+ operateAdmin: root
diff --git a/electripper-common/src/main/java/com/ruoyi/common/config/WxPayConfig.java b/electripper-common/src/main/java/com/ruoyi/common/config/WxPayConfig.java
index adbc2c7..fc5f09f 100644
--- a/electripper-common/src/main/java/com/ruoyi/common/config/WxPayConfig.java
+++ b/electripper-common/src/main/java/com/ruoyi/common/config/WxPayConfig.java
@@ -4,9 +4,11 @@ import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
+import com.wechat.pay.java.service.brandprofitsharing.BrandProfitSharingService;
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
+import com.wechat.pay.java.service.profitsharing.ProfitsharingService;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
@@ -122,4 +124,18 @@ public class WxPayConfig {
return new RefundService.Builder().config(config).build();
}
+ // 分账服务
+ @Bean
+ public ProfitsharingService brandProfitSharingService() {
+ Config config = new RSAAutoCertificateConfig.Builder()
+ .merchantId(merchantId)
+ // 使用 com.wechat.pay.java.core.util中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
+ .privateKeyFromPath(privateKeyPath)
+ .merchantSerialNumber(merchantSerialNumber)
+ .apiV3Key(apiV3Key)
+ .build();
+ // 初始化服务
+ return new ProfitsharingService.Builder().config(config).build();
+ }
+
}
diff --git a/electripper-common/src/main/java/com/ruoyi/common/constant/IotConstants.java b/electripper-common/src/main/java/com/ruoyi/common/constant/IotConstants.java
index b276dd7..1417a92 100644
--- a/electripper-common/src/main/java/com/ruoyi/common/constant/IotConstants.java
+++ b/electripper-common/src/main/java/com/ruoyi/common/constant/IotConstants.java
@@ -51,6 +51,66 @@ public class IotConstants {
*/
public static final String COMMAND_CLOSE = "close";
+ /**
+ * 命令 subXX@ xx是上报时间修改,例如20 则上报20秒一次, 关闭订单之后为5倍的上报间隔也就是100秒上报一次数据
+ */
+ public static final String COMMAND_SUB = "sub";
+
+ /**
+ * 命令 超出营运区(禁行区)断电,不进行轮动检测
+ */
+ public static final String COMMAND_QLOSE = "qlose";
+
+ /**
+ * 命令 临时锁车,断电会进行轮动检测
+ */
+ public static final String COMMAND_LLOSE = "llose";
+
+ /**
+ * 命令 0欢迎
+ */
+ public static final String COMMAND_PLAY0 = "play0@";
+
+ /**
+ * 命令 1报警
+ */
+ public static final String COMMAND_PLAY1 = "play1@";
+
+ /**
+ * 命令 2营运边界
+ */
+ public static final String COMMAND_PLAY2 = "play2@";
+
+ /**
+ * 命令 3超出营运边界
+ */
+ public static final String COMMAND_PLAY3 = "play3@";
+
+ /**
+ * 命令 4车辆未解锁
+ */
+ public static final String COMMAND_PLAY4 = "play4@";
+
+ /**
+ * 命令 5超速
+ */
+ public static final String COMMAND_PLAY5 = "play5@";
+
+ /**
+ * 命令 6电量低
+ */
+ public static final String COMMAND_PLAY6 = "play6@";
+
+ /**
+ * 命令 7临时停车
+ */
+ public static final String COMMAND_PLAY7 = "play7@";
+
+ /**
+ * 命令 8使用结束
+ */
+ public static final String COMMAND_PLAY8 = "play8@";
+
/**----------------------------命令end----------------------------*/
@@ -74,8 +134,8 @@ public class IotConstants {
/**----------------------------启动模式end----------------------------*/
/**
- * ONENET日志
+ * ONENET定位日志
*/
- public static final String ONENET_LOG = "LOG";
+ public static final String ONENET_LOCATION = "sys";
}
diff --git a/electripper-common/src/main/java/com/ruoyi/common/utils/CommonUtil.java b/electripper-common/src/main/java/com/ruoyi/common/utils/CommonUtil.java
index 1580cd5..76367eb 100644
--- a/electripper-common/src/main/java/com/ruoyi/common/utils/CommonUtil.java
+++ b/electripper-common/src/main/java/com/ruoyi/common/utils/CommonUtil.java
@@ -8,6 +8,8 @@ import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
+import java.math.BigDecimal;
+
/**
* 业务工具类
*
@@ -98,4 +100,42 @@ public class CommonUtil {
}
return list;
}
+
+ /**
+ * 根据电压计算续航里程
+ *
+ * @param voltage 电压
+ * @param fullVoltage 满电电压
+ * @param lowVoltage 亏电电压
+ * @param fullEndurance 满电续航里程
+ * @author qzz
+ */
+ public static Integer getRemainingMileage(String voltage,Integer fullVoltage,Integer lowVoltage,Integer fullEndurance) {
+ // 满电电压减去亏电电压 乘以 满电续航里程 除以 满电电压
+ int current = (fullVoltage - Integer.parseInt(voltage)) ;
+ int full = (fullVoltage - lowVoltage) ;
+ BigDecimal divide = new BigDecimal(current).divide(new BigDecimal(full));//当前电量百分百
+ log.info("当前电量百分百:{}%",divide.multiply(new BigDecimal(100)));
+ BigDecimal multiply = divide.multiply(new BigDecimal(fullEndurance));
+ log.info("当前剩余续航里程:{}km",multiply);
+ return multiply.intValue();
+ }
+
+ /**
+ * 根据电压计算电量百分比
+ *
+ * @param voltage 电压
+ * @param fullVoltage 满电电压
+ * @param lowVoltage 亏电电压
+ * @author qzz
+ */
+ public static Integer getElectricQuantity(String voltage,Integer fullVoltage,Integer lowVoltage) {
+ // 满电电压减去亏电电压 乘以 满电续航里程 除以 满电电压
+ int current = (fullVoltage - Integer.parseInt(voltage)) ;
+ int full = (fullVoltage - lowVoltage) ;
+ BigDecimal divide = new BigDecimal(current).divide(new BigDecimal(full));//当前电量百分百
+ BigDecimal multiply = divide.multiply(new BigDecimal(100));
+ log.info("当前电量百分百:{}%",multiply);
+ return multiply.intValue();
+ }
}
diff --git a/electripper-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/electripper-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index 5a16226..b1ab75c 100644
--- a/electripper-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/electripper-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -121,6 +121,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
"/app/**",
// "/appVerify/**",
"/common/upload",
+ "/common/receive",
"/payment/callback/**",
"/loginByopenid").permitAll()
// 静态资源,可匿名访问
diff --git a/electripper-system/src/main/java/com/ruoyi/system/domain/EtOperatingArea.java b/electripper-system/src/main/java/com/ruoyi/system/domain/EtOperatingArea.java
index 91d966c..e81bc3f 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/domain/EtOperatingArea.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/domain/EtOperatingArea.java
@@ -103,6 +103,7 @@ public class EtOperatingArea implements Serializable
@Excel(name = "区")
private String county;
+ /** 运营区域外断电:0-关闭;1-开启 */
@Excel(name = "运营区域外断电")
private String areaOutOutage;
@@ -112,6 +113,7 @@ public class EtOperatingArea implements Serializable
@Excel(name = "电子围栏外还车调度")
private String areaOutDispatch;
+ /** 禁行区内断电:0-关闭;1-开启 */
@Excel(name = "禁行区内断电")
private String noRidingOutage;
diff --git a/electripper-system/src/main/java/com/ruoyi/system/mapper/AsDeviceMapper.java b/electripper-system/src/main/java/com/ruoyi/system/mapper/AsDeviceMapper.java
index 07248d6..9523a18 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/mapper/AsDeviceMapper.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/mapper/AsDeviceMapper.java
@@ -28,7 +28,7 @@ public interface AsDeviceMapper extends BaseMapper
* @param mac 设备列表主键
* @return 设备列表
*/
-// public AsDevice selectAsDeviceByMac(String mac);
+ public AsDevice selectAsDeviceByMac(String mac);
/**
* 根据SN查询设备信息
diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/IAsDeviceService.java b/electripper-system/src/main/java/com/ruoyi/system/service/IAsDeviceService.java
index 7192890..00c8d58 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/service/IAsDeviceService.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/service/IAsDeviceService.java
@@ -2,6 +2,7 @@ package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.domain.AsDevice;
+import com.ruoyi.system.domain.EtOperatingArea;
import com.ruoyi.system.domain.EtOrder;
import com.ruoyi.system.domain.response.OrderResponse;
import com.ruoyi.system.domain.vo.DeviceNumVo;
@@ -31,7 +32,7 @@ public interface IAsDeviceService extends IService
* @param mac 设备主键
* @return 设备
*/
-// public AsDevice selectAsDeviceByMac(String mac);
+ public AsDevice selectAsDeviceByMac(String mac);
/**
* 根据SN查询设备信息
@@ -193,4 +194,14 @@ public interface IAsDeviceService extends IService
* 根据运营区查询车辆数量
*/
Integer selectCountByAreaId(Long areaId);
+
+ /**
+ * 判断是否在运营区
+ */
+ public Boolean isAreaZone(String sn, EtOperatingArea area);
+
+ /**
+ * 判断是否在禁行区内
+ */
+ public boolean isNoRidingArea(String sn,Long areaId);
}
diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/IWxPayService.java b/electripper-system/src/main/java/com/ruoyi/system/service/IWxPayService.java
index b41f1cc..8341ce6 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/service/IWxPayService.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/service/IWxPayService.java
@@ -3,9 +3,14 @@ package com.ruoyi.system.service;
import com.ruoyi.system.domain.EtOrder;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
+import com.wechat.pay.java.service.profitsharing.model.AddReceiverResponse;
+import com.wechat.pay.java.service.profitsharing.model.CreateOrderReceiver;
+import com.wechat.pay.java.service.profitsharing.model.DeleteReceiverResponse;
+import com.wechat.pay.java.service.profitsharing.model.OrdersEntity;
import com.wechat.pay.java.service.refund.model.Refund;
import java.math.BigDecimal;
+import java.util.List;
/**
* 微信支付服务接口
@@ -51,9 +56,23 @@ public interface IWxPayService {
Refund refund(EtOrder etOrder, String reason, BigDecimal amount);
-// /**
-// * 微信支付通知
-// * @return 是否成功
-// */
-// void payNotify(HttpServletRequest request);
+ /**
+ * 请求分账API
+ * @param transactionId 微信支付单号
+ * @param receivers 分账接收方
+ */
+ public OrdersEntity createOrder(String transactionId, List receivers);
+
+ /**
+ * 添加分账接收方
+ * @param wxopenid openid
+ */
+ AddReceiverResponse addReceiver(String wxopenid);
+
+ /**
+ * 删除分账接收方
+ * @param wxopenid openid
+ */
+ DeleteReceiverResponse deleteReceiver(String wxopenid);
+
}
diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/impl/AsDeviceServiceImpl.java b/electripper-system/src/main/java/com/ruoyi/system/service/impl/AsDeviceServiceImpl.java
index 18718a8..74e0101 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/service/impl/AsDeviceServiceImpl.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/service/impl/AsDeviceServiceImpl.java
@@ -114,6 +114,17 @@ public class AsDeviceServiceImpl extends ServiceImpl i
return asDeviceMapper.selectAsDeviceByDeviceId(deviceId);
}
+ /**
+ * 根据mac查询车辆实时信息
+ *
+ * @param mac 设备mac
+ * @return 设备
+ */
+ @Override
+ public AsDevice selectAsDeviceByMac(String mac) {
+ return asDeviceMapper.selectAsDeviceByMac(mac);
+ }
+
/**
* 根据sn号查询车辆实时信息
*
@@ -453,7 +464,14 @@ public class AsDeviceServiceImpl extends ServiceImpl i
String finalOrderNo = orderNo;
Boolean execute = transactionTemplate.execute(e -> {
/** 2.发送命令*/
-// sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_OPEN,"编号开锁");
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_OPEN,"编号开锁");
+ //间隔1秒
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_PLAY0,"编号开锁播报");
/** 3.更新车辆状态*/
asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_OPEN);
asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_IN_USING);
@@ -516,6 +534,13 @@ public class AsDeviceServiceImpl extends ServiceImpl i
Boolean execute = transactionTemplate.execute(e -> {
/** 2.发送命令*/
sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_OPEN,"管理员开锁");
+ //间隔1秒
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_PLAY0,"管理员开锁播报");
return Boolean.TRUE;
});
if(!execute)throw new ServiceException("管理员开锁失败");
@@ -663,7 +688,7 @@ public class AsDeviceServiceImpl extends ServiceImpl i
String token = Token.getToken();
Boolean execute = transactionTemplate.execute(e -> {
/** 2.发送命令*/
- sendCommand(asDevice.getMac(), token,"响铃命令","响铃寻车");
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_PLAY1,"响铃寻车");
return Boolean.TRUE;
});
if(!execute)throw new ServiceException("响铃寻车失败");
@@ -689,9 +714,15 @@ public class AsDeviceServiceImpl extends ServiceImpl i
String token = Token.getToken();
String finalSn = sn;
Boolean execute = transactionTemplate.execute(e -> {
- /** TODO 临时锁车*/
/** 2.发送命令*/
sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_CLOSE,"临时锁车");
+ //间隔1秒
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_PLAY7,"临时锁车播报");
if(StrUtil.isNotBlank(orderNo)){//有订单号,则是用户临时锁车
/** 改变车辆状态:4-临时锁车 */
asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_TEMPORARILY_LOCK);//临时锁车
@@ -741,6 +772,13 @@ public class AsDeviceServiceImpl extends ServiceImpl i
/** TODO 临时解锁*/
/** 2.发送命令*/
sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_OPEN,"临时解锁");
+ //间隔1秒
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_PLAY0,"临时解锁播报");
if(StrUtil.isNotBlank(orderNo)){//有订单号,则是用户骑行中解锁
/** 改变车辆状态:3-骑行中 */
asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_IN_USING);//骑行中
@@ -811,6 +849,13 @@ public class AsDeviceServiceImpl extends ServiceImpl i
//1.发送开锁命令并更新车辆状态
String token = Token.getToken();
sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_CLOSE,"取消预约关锁");
+ //间隔1秒
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_PLAY8,"取消预约关锁播报");
/** 5.记录行程*/
int tripLog = tripLogService.tripLog(order.getOrderNo(),order.getSn(),ServiceConstants.TRIP_LOG_TYPE_UNLOCK_RIDE);
if(tripLog==0){
@@ -896,7 +941,14 @@ public class AsDeviceServiceImpl extends ServiceImpl i
String token = Token.getToken();
AsDevice device = asDeviceMapper.selectAsDeviceBySn(order.getSn());
/** 2. 车辆远程关锁*/
-// sendCommand(device.getMac(), token,IotConstants.COMMAND_CLOSE,"还车关锁");
+ sendCommand(device.getMac(), token,IotConstants.COMMAND_CLOSE,"还车关锁");
+ //间隔1秒
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ sendCommand(device.getMac(), token,IotConstants.COMMAND_PLAY8,"还车关锁播报");
/** 4. 更新车辆状态*/
device.setStatus(ServiceConstants.VEHICLE_STATUS_NORMAL);
device.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
@@ -1170,7 +1222,8 @@ public class AsDeviceServiceImpl extends ServiceImpl i
/**
* 是否在运营区内
*/
- private Boolean isAreaZone(String sn,EtOperatingArea area) {
+ @Override
+ public Boolean isAreaZone(String sn,EtOperatingArea area) {
Boolean inCircle = false;
AsDevice device = asDeviceMapper.selectAsDeviceBySn(sn);
String latitude = device.getLatitude();
@@ -1216,4 +1269,36 @@ public class AsDeviceServiceImpl extends ServiceImpl i
}
return inCircle;
}
+
+ /**
+ * 是否禁行区内
+ */
+ @Override
+ public boolean isNoRidingArea(String sn,Long areaId) {
+ Boolean isNoRiding = false;
+ EtParkingArea parkingArea = new EtParkingArea();
+ parkingArea.setAreaId(areaId);
+ List parkingAreas = parkingAreaService.selectEtParkingAreaList(parkingArea);
+ if(ObjectUtil.isNull(parkingAreas) || parkingAreas.size() == 0){
+ log.info("运营区【{}】没有禁行区,",areaId);
+ throw new ServiceException("运营区【{}】没有禁行区"+areaId.toString());
+ }
+ for (EtParkingArea etParkingArea : parkingAreas) {
+ if(etParkingArea.getType().equals(ServiceConstants.PARKING_AREA_TYPE_BANNED_RIDING)){
+ AsDevice device = asDeviceMapper.selectAsDeviceBySn(sn);
+ String latitude = device.getLatitude();
+ String longitude = device.getLongitude();
+ Geometry geometry = GeoUtils.fromWkt(etParkingArea.getBoundary());
+ isNoRiding = GeoUtils.isInCircle(longitude, latitude, geometry);
+ if(isNoRiding){
+ log.info("车辆【{}】在禁行区【{}】内",sn,etParkingArea.getParkingName());
+ isNoRiding = true;
+ break;
+ }else{
+ log.info("车辆【{}】不在禁行区【{}】内",sn,etParkingArea.getParkingName());
+ }
+ }
+ }
+ return isNoRiding;
+ }
}
diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/impl/CallbackServiceImpl.java b/electripper-system/src/main/java/com/ruoyi/system/service/impl/CallbackServiceImpl.java
index e1bb73f..771a314 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/service/impl/CallbackServiceImpl.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/service/impl/CallbackServiceImpl.java
@@ -37,6 +37,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
+import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
@@ -83,10 +84,7 @@ public class CallbackServiceImpl implements CallbackService {
@Value("${et.handlingCharge}")
private String handlingCharge;
-// @Autowired
-// private ISysUserService sysUserService;
-
- @Autowired
+ @Resource
private SysUserMapper userMapper;
@@ -101,6 +99,7 @@ public class CallbackServiceImpl implements CallbackService {
logger.info("【微信支付回调】接收对象 : " + JSON.toJSONString(body));
// 解析通知数据
Notification notification = JSON.parseObject(body, Notification.class);
+ String outTradeNo;
// 支付成功通知
if (NotifyEventType.TRANSACTION_SUCCESS.getValue().equals(notification.getEventType())) {
@@ -110,7 +109,7 @@ public class CallbackServiceImpl implements CallbackService {
// 充值成功后的业务处理
AttachVo attachVo = JSONObject.parseObject(transaction.getAttach(),AttachVo.class);
logger.info("【微信支付回调】附加信息 : " + JSON.toJSONString(attachVo));
- String outTradeNo = transaction.getOutTradeNo();
+ outTradeNo = transaction.getOutTradeNo();
EtOrder order = orderService.selectEtOrderByOutTradeNo(outTradeNo);
logger.info("【微信支付回调】订单信息 : " + JSON.toJSONString(order));
@@ -194,36 +193,6 @@ public class CallbackServiceImpl implements CallbackService {
order.setMark("取消预约支付");
asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_NORMAL);//取消预约支付后车辆正常运营
asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
- }else if(attachVo.getType().equals(ServiceConstants.BUSINESS_TYPE_MEAL)){//废弃
- logger.info("【微信支付回调】套餐支付");
- // 3-套餐支付 套餐支付中分为预约车辆和立即开锁骑行
- if(attachVo.getIsAppointment()){//购买套餐后预约
- order.setStatus(ServiceConstants.ORDER_STATUS_IN_APPOINTMENT);
- order.setAppointmentStartTime(DateUtils.getNowDate());
- order.setMark("套餐预约支付");
- /** 2.发送命令*/
-// asDeviceService.sendCommand(asDevice.getMac(), iotToken,IotConstants.COMMAND_CLOSE,"套餐预约");
- /** 3.更新车辆状态*/
- asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_IN_APPOINTMENT);//预约中
- asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
- logger.info("【微信支付回调】套餐预约成功");
- }else{
- order.setMark("套餐骑行支付");
- order.setStatus(ServiceConstants.ORDER_STATUS_RIDING);//骑行中
- /** 2.发送命令*/
-// asDeviceService.sendCommand(asDevice.getMac(), iotToken,IotConstants.COMMAND_OPEN,"套餐开锁");
- /** 3.更新车辆状态*/
- asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_OPEN);
- asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_IN_USING);
- }
- EtFeeRule etFeeRule = etFeeRuleService.selectEtFeeRuleByRuleId(order.getRuleId());
- if(ObjectUtil.isNull(etFeeRule)){
- throw new ServiceException("套餐不存在");
- }
-// Integer time = etFeeRule.getTime();//时间:以小时为单位
-// //当前时间往后推time个小时
-// Date ruleEndTime = DateUtils.addHours(DateUtils.getNowDate(),time);
-// order.setRuleEndTime(ruleEndTime);
}else if(attachVo.getType().equals(ServiceConstants.BUSINESS_TYPE_DEPOSIT)){
logger.info("【微信支付回调】押金支付");
// 4-押金支付
@@ -242,6 +211,7 @@ public class CallbackServiceImpl implements CallbackService {
}
}else{
logger.error("【微信支付回调】 : 支付场景不存在");
+ throw new ServiceException("【微信支付回调】支付场景不存在");
}
if(ObjectUtil.isNotNull(asDevice)){
int device = asDeviceService.updateAsDevice(asDevice);
@@ -260,6 +230,8 @@ public class CallbackServiceImpl implements CallbackService {
logger.error("【微信支付回调】更新用户押金失败");
throw new ServiceException("【微信支付回调】更新用户押金失败");
}
+ // 调用任务调度方法处理分账 一分钟后执行
+// paymentService.scheduleProfitSharing(outTradeNo);
}
}
}
diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/impl/EtOrderServiceImpl.java b/electripper-system/src/main/java/com/ruoyi/system/service/impl/EtOrderServiceImpl.java
index 471fc14..bc56dfa 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/service/impl/EtOrderServiceImpl.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/service/impl/EtOrderServiceImpl.java
@@ -351,7 +351,7 @@ public class EtOrderServiceImpl implements IEtOrderService
/**
* 预下单
- * 类型 1骑行 2预约 3套餐 4押金
+ * 类型 1骑行 2预约 4押金
* 获取到订单信息后,计算金额,调用微信支付接口,返回预支付信息
*/
@Transactional
@@ -378,56 +378,6 @@ public class EtOrderServiceImpl implements IEtOrderService
if(i == 0){
throw new ServiceException("订单生成失败");
}
- }else if(payType.equals("3")){
- log.info("【预下单】支付场景为:套餐支付");
- String orderNo = IdUtils.randomUUID2();
- // 套餐订单,生成订单后根据
- etOrder = createOrder(order, orderNo);
- verify(order, order.getUserId());
- /** 1.获取token*/
- String token = Token.getToken();
- AsDevice asDevice = asDeviceService.selectAsDeviceBySn(order.getSn());
-// if(order.getIsAppointment()){//购买完套餐后 预约车辆
- etOrder.setStatus(ServiceConstants.ORDER_STATUS_IN_APPOINTMENT);
- etOrder.setAppointmentStartTime(DateUtils.getNowDate());
- //校验 userI,sn,ruleId,type 3 isAppointment
- Boolean execute = transactionTemplate.execute(e -> {
- /** 2.发送命令*/
- deviceService.sendCommand(asDevice.getMac(), token, IotConstants.COMMAND_CLOSE,"套餐预约");
- /** 3.更新车辆状态*/
- asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_TEMPORARILY_LOCK);//临时锁车
- asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_OPEN);
- int device = asDeviceService.updateAsDevice(asDevice);
- if(device==0){
- log.info("【套餐预约】更新车辆状态失败");
- return Boolean.FALSE;
- }
- log.info("套餐预约成功");
- return Boolean.TRUE;
- });
- if(!execute)throw new ServiceException("套餐预约失败");
-// }else{//购买完套餐后 立即开锁骑行
-// etOrder.setStatus(ServiceConstants.ORDER_STATUS_RIDING);
-// Boolean execute = transactionTemplate.execute(e -> {
-// /** 2.发送命令*/
-// deviceService.sendCommand(order.getSn(), token,IotConstants.COMMAND_OPEN,"套餐开锁");
-// /** 3.更新车辆状态*/
-// asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_OPEN);
-// asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_IN_USING_STR);
-// int device = asDeviceService.updateAsDevice(asDevice);
-// if(device==0){
-// log.info("【套餐开锁】更新车辆状态失败");
-// return Boolean.FALSE;
-// }
-// log.info("套餐开锁成功");
-// return Boolean.TRUE;
-// });
-// if(!execute)throw new ServiceException("套餐开锁失败");
-// }
- int i = etOrderMapper.insertEtOrder(etOrder);
- if(i == 0){
- throw new ServiceException("订单生成失败");
- }
}else if(payType.equals("2")){
log.info("【预下单】支付场景为:取消预约支付");
etOrder = etOrderMapper.selectEtOrderByOrderNo(order.getOrderNo());
diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java b/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java
index fe85f4d..978e55f 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java
@@ -23,6 +23,8 @@ import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
+import com.wechat.pay.java.service.profitsharing.ProfitsharingService;
+import com.wechat.pay.java.service.profitsharing.model.*;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
@@ -32,8 +34,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
+import javax.management.relation.RelationType;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
/**
* 微信支付服务
@@ -71,6 +76,9 @@ public class WxPayService implements IWxPayService {
@Autowired
public RefundService refundService2;
+ @Autowired
+ public ProfitsharingService profitsharingService;
+
@Autowired
private RedisLock redisLock;
@@ -157,65 +165,45 @@ public class WxPayService implements IWxPayService {
return refundService2.create(request);
}
-// /**
-// * 微信支付通知
-// * @param request 请求
-// */
-// @Override
-// @Transactional
-// public void payNotify(HttpServletRequest request) {
-//
-// String body = HttpUtils.getBody(request);
-// // 解析通知数据
-// Notification notification = JSON.parseObject(body, Notification.class);
-//
-// // 判断是否重复通知,重复通知则忽略
-// if (isRepeatNotify(notification.getId())) {
-// return;
-// }
-//
-// // 支付成功通知
-// if (NotifyEventType.TRANSACTION_SUCCESS.getValue().equals(notification.getEventType())) {
-// // 验签、解密并转换成 Transaction
-// Transaction transaction = checkAndParse(request, body, Transaction.class);
-//
-// if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
-// SmTransactionBill bill = transactionBillService.selectSmTransactionBillByBillNo(transaction.getOutTradeNo());
-// ServiceUtil.assertion(bill == null, "订单不存在");
-//
-// // 充值成功,修改订单状态
-// transactionBillService.rechargeSuccess(bill.getBillId(), DateUtils.getNowDate());
-// // 保存通知数据
-// saveNotifyData(bill.getBillId(), notification, transaction);
-// }
-// }
-//
-// }
+ /** 请求分账API */
+ public OrdersEntity createOrder(String transactionId,List receivers) {
+ CreateOrderRequest request = new CreateOrderRequest();
+ request.setAppid(wxPayConfig.getAppId());
+ request.setTransactionId(transactionId);// 微信订单号
+ request.setOutOrderNo(IdUtils.getOrderNo("fz"));// 商户系统内部分账单号
-// /**
-// * 通知是否重复
-// * @param notifyId 通知id
-// */
-// private boolean isRepeatNotify(String notifyId) {
-// SmWxPayNotify repeat = smWxPayNotifyMapper.selectSmWxPayNotifyByNotifyId(notifyId);
-// return repeat != null;
-// }
+// List receivers = new ArrayList<>();
+// CreateOrderReceiver receiver = new CreateOrderReceiver();
+// receiver.setType(ReceiverType.PERSONAL_OPENID.name());
+// receiver.setAccount("openid");
+// receiver.setAccount("0.01");
+// receiver.setDescription("描述");
+// receivers.add(receiver);
+ request.setReceivers(receivers);
+ request.setUnfreezeUnsplit(false);
+ return profitsharingService.createOrder(request);
+ }
+
+ /** 添加分账接收方 */
+ @Override
+ public AddReceiverResponse addReceiver(String wxopenid) {
+ AddReceiverRequest request = new AddReceiverRequest();
+ request.setAppid(wxPayConfig.getAppId());
+ request.setType(ReceiverType.PERSONAL_OPENID);
+ request.setAccount(wxopenid);
+ request.setRelationType(ReceiverRelationType.PARTNER);
+ return profitsharingService.addReceiver(request);
+ }
+
+ /** 删除分账接收方 */
+ public DeleteReceiverResponse deleteReceiver(String wxopenid) {
+ DeleteReceiverRequest request = new DeleteReceiverRequest();
+ request.setAppid(wxPayConfig.getAppId());
+ request.setType(ReceiverType.PERSONAL_OPENID);
+ request.setAccount(wxopenid);
+ return profitsharingService.deleteReceiver(request);
+ }
-// /**
-// * 保存通知数据
-// * @param billId 订单id
-// * @param body 请求体
-// * @param plaintext 明文
-// */
-// private void saveNotifyData(long billId, Notification body, Object plaintext) {
-// SmWxPayNotify data = new SmWxPayNotify();
-// data.setNotifyId(body.getId());
-// data.setBillId(billId);
-// data.setBody(JSON.toJSONString(body));
-// data.setPlaintext(JSON.toJSONString(plaintext));
-// int i = smWxPayNotifyMapper.insertSmWxPayNotify(data);
-// ServiceUtil.assertion(i != 1, "保存通知数据失败");
-// }
/**
* 验签并解析
diff --git a/electripper-system/src/main/java/com/ruoyi/system/task/EtTask.java b/electripper-system/src/main/java/com/ruoyi/system/task/EtTask.java
index 571dc17..b7a733c 100644
--- a/electripper-system/src/main/java/com/ruoyi/system/task/EtTask.java
+++ b/electripper-system/src/main/java/com/ruoyi/system/task/EtTask.java
@@ -23,6 +23,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
+import java.util.stream.IntStream;
/**
* 定时任务调度测试
@@ -75,6 +76,7 @@ public class EtTask {
order.setEndTime(endDateStr);
order.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
order.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
+ order.setType(ServiceConstants.ORDER_TYPE_RIDING);
List orderListByDate = etOrderMapper.selectEtOrderList(order);
for(EtOrder order1:orderListByDate){
EtDividendDetail etDividendDetail = new EtDividendDetail();
@@ -89,15 +91,33 @@ public class EtTask {
etDividendDetail.setPartnerId(user.getUserId());
etDividendDetail.setOrderNo(order1.getOrderNo());
etDividendDetail.setTotalAmount(order1.getTotalFee());
- etDividendDetail.setDividendAmount(order1.getTotalFee().multiply(new BigDecimal(user.getDividendProportion()).divide(new BigDecimal(100),2, BigDecimal.ROUND_HALF_UP)));
- etDividendDetail.setDividendProportion(user.getDividendProportion());
etDividendDetail.setCreateTime(DateUtils.getNowDate());
+ etDividendDetail.setDividendProportion(user.getDividendProportion());
+ // todo 分账金额是骑行费,还是调度费,看分账项目
+ etDividendDetail.setDividendAmount(order1.getTotalFee().multiply(new BigDecimal(user.getDividendProportion()).divide(new BigDecimal(100),2, BigDecimal.ROUND_HALF_UP)));
etDividendDetail.setDividendItem(user.getDividendItem());
int i = dividendDetailService.insertEtDividendDetail(etDividendDetail);
if(i==0){
throw new ServiceException("保存分账明细失败");
}
}
+ int totalDividendProportion = IntStream.of(sysUsers.stream()
+ .mapToInt(SysUser::getDividendProportion)
+ .toArray())
+ .sum();
+ //算运营商自己的分账
+ etDividendDetail.setAreaId(area.getAreaId());
+ etDividendDetail.setPartnerId(0L);
+ etDividendDetail.setOrderNo(order1.getOrderNo());
+ etDividendDetail.setTotalAmount(order1.getTotalFee());
+ etDividendDetail.setCreateTime(DateUtils.getNowDate());
+ etDividendDetail.setDividendAmount(order1.getTotalFee().multiply(new BigDecimal(100-totalDividendProportion).divide(new BigDecimal(100),2, BigDecimal.ROUND_HALF_UP)));
+ etDividendDetail.setDividendProportion(100-totalDividendProportion);
+ etDividendDetail.setDividendItem("运营商");
+ int i = dividendDetailService.insertEtDividendDetail(etDividendDetail);
+ if(i==0){
+ throw new ServiceException("保存分账明细失败");
+ }
}
}