1.请求分账---支付一分钟后发起分账

2.运营时间判断
This commit is contained in:
邱贞招 2024-05-28 21:35:40 +08:00
parent 1abf528d60
commit 4e2b80caf5
14 changed files with 215 additions and 58 deletions

View File

@ -140,14 +140,18 @@ public class AppVerifyController extends BaseController
@PostMapping("/device/snSwitch")
public AjaxResult snSwitch(@RequestBody EtOrderVo order)
{
logger.info("【扫码/编号开锁骑行】请求:{}", JSON.toJSON(order));
if(etOrderService.isInOrder(order.getUserId(),order.getOrderNo()).size()>0){
return error("您有未完成的订单,请先完成订单");
//运营时间判断
if(!asDeviceService.isOperatingTime(order.getSn())){
return error("不在营业时间内,不得骑行");
}
//根据余额和充值记录判断是否有充值过押金没有充值过押金提示充值押金
if(!asUserService.checkIsDeposit(order.getUserId())){
return error("您还未充值押金,请先充值押金");
}
logger.info("【扫码/编号开锁骑行】请求:{}", JSON.toJSON(order));
if(etOrderService.isInOrder(order.getUserId(),order.getOrderNo()).size()>0){
return error("您有未完成的订单,请先完成订单");
}
//低电量不得骑行判断
if(asDeviceService.isLowBattery(order.getSn())){
return error("低电量不得骑行");

View File

@ -99,7 +99,9 @@ public class ReceiveController {
* 2.判断是否在禁行区内如果在根据配置禁行区内断电配置进行断电
* 3.超出运营区外断电
* 4.行程线路添加更新订单中的trip_route字段
* 5.低电量不能骑行如果电量低则声音播报
* 5.低于电量%不得骑行声音播报
* 6.低电量 生成换电工单
* 7.运营边界判断
* */
/** 1.更新车辆定位、电压;计算续航里程 */
AsDevice device = asDeviceService.selectAsDeviceByMac(logEntry.getDevName());
@ -188,6 +190,9 @@ public class ReceiveController {
throw new ServiceException("车辆状态更新失败");
}
}
/** todo 7.运营边界判断 几米判断? 5 */
}else{
log.info("更新定位失败:" +logEntry.getDevName());
}

View File

@ -114,25 +114,6 @@ public class IotConstants {
/**----------------------------命令end----------------------------*/
/**----------------------------启动模式start----------------------------*/
/**
* 启动模式 0-手动模式2-土壤湿度低启动
*/
public static final String START_MODE_MANUAL = "0";
/**
* 启动模式 1-定时模式
*/
public static final String START_MODE_REGULAR = "1";
/**
* 启动模式 2-土壤湿度低启动
*/
public static final String START_MODE_HUMIDITY = "2";
/**----------------------------启动模式end----------------------------*/
/**
* ONENET定位日志
*/

View File

@ -441,5 +441,17 @@ public class ServiceConstants {
/**----------------------------收支类型end----------------------------*/
/**----------------------------运营时间start----------------------------*/
/** 运营时间:1-全天2-自定义 */
/**
* 运营时间1-全天
*/
public static final String AREA_TIME_ALL = "1";
/**
* 运营时间2-自定义
*/
public static final String AREA_TIME_CUSTOM= "2";
/**----------------------------运营时间end----------------------------*/
}

View File

@ -118,6 +118,17 @@ public class SysUser extends BaseEntity
@Excel(name = "分账项目ids")
private List<String> dividendItemIds;
/** app用户id */
private Long appUserId;
public Long getAppUserId() {
return appUserId;
}
public void setAppUserId(Long appUserId) {
this.appUserId = appUserId;
}
public List<String> getDividendItemIds() {
return dividendItemIds;
}

View File

@ -32,6 +32,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_FORMAT_HHMMSS = "HH:mm:ss";
private static String[] parsePatterns = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
@ -252,4 +254,16 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
calendar.add(Calendar.MINUTE, xMinutes); // 加上X分钟
return calendar.getTime(); // 返回新时间
}
/**
* 判断是否在时间范围内
*/
public static Boolean isInTime(String areaTimeStart, String areaTimeEnd) {
String currentTime = new SimpleDateFormat(DATE_FORMAT_HHMMSS).format(new Date());
if (areaTimeStart.compareTo(currentTime) <= 0 && areaTimeEnd.compareTo(currentTime) >= 0) {
return true;
} else {
return false;
}
}
}

View File

@ -86,6 +86,14 @@ public class EtOperatingArea implements Serializable
@Excel(name = "运营时间:1-全天2-自定义")
private String areaTime;
/** 自定义开始运营时间 */
@Excel(name = "自定义开始运营时间")
private String areaTimeStart;
/** 自定义结束运营时间 */
@Excel(name = "自定义结束运营时间")
private String areaTimeEnd;
/** 客服电话 */
@Excel(name = "客服电话")
private String servicePhone;

View File

@ -1,6 +1,8 @@
package com.ruoyi.system.mapper;
import java.util.List;
import com.ruoyi.common.core.domain.entity.AsUser;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -124,4 +126,12 @@ public interface SysUserMapper
* @return 结果
*/
public SysUser checkEmailUnique(String email);
/**
* 绑定app用户
* @param appUserId app用户id
* @param userId 系统用户id
* @return
*/
void bandAppUser(@Param("appUserId") Long appUserId, @Param("userId") Long userId);
}

View File

@ -209,4 +209,9 @@ public interface IAsDeviceService extends IService<AsDevice>
* 低电量不得骑行判断
*/
boolean isLowBattery(String sn);
/**
* 判断是否在运营时间内
*/
Boolean isOperatingTime(String sn);
}

View File

@ -655,7 +655,7 @@ public class AsDeviceServiceImpl extends ServiceImpl<AsDeviceMapper, AsDevice> i
String finalSn = sn;
Boolean execute = transactionTemplate.execute(e -> {
/** 2.发送命令*/
sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_CLOSE,"临时锁车");
sendCommand(asDevice.getMac(), token,IotConstants.COMMAND_LLOSE,"临时锁车");
//间隔1秒
try {
Thread.sleep(500);
@ -1257,4 +1257,18 @@ public class AsDeviceServiceImpl extends ServiceImpl<AsDeviceMapper, AsDevice> i
}
return false;
}
/**
* 判断是否在运营时间内
*/
@Override
public Boolean isOperatingTime(String sn) {
AsDevice device = asDeviceMapper.selectAsDeviceBySn(sn);
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(device.getAreaId());
if(area.getAreaTime().equals(ServiceConstants.AREA_TIME_CUSTOM) && !DateUtils.isInTime(area.getAreaTimeStart(),area.getAreaTimeEnd())){
log.info("不在运营时间内,运营时间为:【{}】--【{}】",area.getAreaTimeStart(),area.getAreaTimeEnd());
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}

View File

@ -25,6 +25,9 @@ import com.wechat.pay.java.core.notification.Notification;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.profitsharing.model.CreateOrderReceiver;
import com.wechat.pay.java.service.profitsharing.model.OrdersEntity;
import com.wechat.pay.java.service.profitsharing.model.ReceiverType;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;
@ -118,6 +121,8 @@ public class CallbackServiceImpl implements CallbackService {
AsDevice asDevice = null;
if(StrUtil.isNotBlank(order.getSn())){
asDevice = asDeviceService.selectAsDeviceBySn(order.getSn());
}else{
throw new ServiceException("设备sn不存在"+order.getSn());
}
//先判断是骑行订单还是押金如果是骑行订单
// 还要区分是取消预约支付
@ -140,6 +145,7 @@ public class CallbackServiceImpl implements CallbackService {
// 还车结算___小时后自动退押金---创建一个定时器TimerTask计算出退还时间后执行退款操作
EtFeeRule rule = etFeeRuleService.selectEtFeeRuleByRuleId(order.getRuleId());
Integer autoRefundDeposit = rule.getAutoRefundDeposit();
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
if(autoRefundDeposit!=null && autoRefundDeposit>0){
//创建一个定时器TimerTask计算出退还时间后执行退款操作
Timer timer = new Timer();
@ -162,8 +168,6 @@ public class CallbackServiceImpl implements CallbackService {
if (latestOrder.isPresent()) {
EtOrder newestOrder = latestOrder.get();
// 处理找到的最新支付时间的订单
Long areaId = order.getAreaId();
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(areaId);
String deposit = area.getDeposit();
if(newestOrder.getTotalFee().compareTo(new BigDecimal(deposit))!=0){
throw new ServiceException("押金充值记录与当前运营区的押金不同");
@ -186,6 +190,42 @@ public class CallbackServiceImpl implements CallbackService {
Date refundTime = DateUtils.getTimeAfterXHours(order.getPayTime(), autoRefundDeposit);
timer.schedule(task, refundTime);
}
// 一分钟后请求分账
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
// 请求分账
List<CreateOrderReceiver> receivers = new ArrayList<>();
// 获取到合伙人的openid
SysUser sysUser = new SysUser();
sysUser.setUserType("03");
sysUser.setAreaId(area.getAreaId());
List<SysUser> sysUsers = userMapper.selectUserList(sysUser);
BigDecimal totalFee = order.getTotalFee();
for (SysUser sysUser1 : sysUsers){
AsUser asUser1 = userService.selectUserById(sysUser1.getAppUserId());
if(asUser1!=null && asUser1.getWxopenid()!=null){
CreateOrderReceiver receiver = new CreateOrderReceiver();
receiver.setType(ReceiverType.PERSONAL_OPENID.name());
receiver.setAccount(asUser1.getWxopenid());
BigDecimal multiply = totalFee.multiply(new BigDecimal(sysUser1.getDividendProportion()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP)).multiply(new BigDecimal(100));
logger.info(sysUser1.getUserName()+"分账比例:"+sysUser1.getDividendProportion()+"%,分账金额:"+multiply);
receiver.setAmount(multiply.longValue());
receiver.setDescription("系统自动分账");
receivers.add(receiver);
}
}
OrdersEntity ordersEntity = wxPayService.createOrder(outTradeNo,receivers);
if(ordersEntity!=null){
logger.info("【微信支付回调】发起分账响应:【{}】",JSON.toJSON(ordersEntity));
}else{
logger.info("【微信支付回调】发起分账失败");
throw new ServiceException("发起分账失败");
}
}
};
timer.schedule(task, new Date(System.currentTimeMillis() + 30000));
}else if(attachVo.getType().equals(ServiceConstants.BUSINESS_TYPE_APPOINTMENT)){
logger.info("【微信支付回调】取消预约支付");
// 2-取消预约支付

View File

@ -1,8 +1,10 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.AsUser;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
@ -15,10 +17,9 @@ import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.domain.SysUserPost;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.IAsDeviceService;
import com.ruoyi.system.service.IEtOperatingAreaService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.*;
import com.wechat.pay.java.service.profitsharing.model.AddReceiverResponse;
import com.wechat.pay.java.service.profitsharing.model.DeleteReceiverResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,6 +28,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
@ -42,19 +44,19 @@ public class SysUserServiceImpl implements ISysUserService
{
private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
@Autowired
@Resource
private SysUserMapper userMapper;
@Autowired
@Resource
private SysRoleMapper roleMapper;
@Autowired
@Resource
private SysPostMapper postMapper;
@Autowired
@Resource
private SysUserRoleMapper userRoleMapper;
@Autowired
@Resource
private SysUserPostMapper userPostMapper;
@Autowired
@ -69,6 +71,13 @@ public class SysUserServiceImpl implements ISysUserService
@Autowired
private IEtOperatingAreaService etOperatingAreaService;
@Autowired
private IWxPayService wxPayService;
@Autowired
private IAsUserService asUserService;
/**
* 根据条件分页查询用户列表
*
@ -300,6 +309,13 @@ public class SysUserServiceImpl implements ISysUserService
{
// 分账项目
setDividendItem(user);
// 添加分账接收方
AsUser asUser = asUserService.selectUserByPhone(user.getPhonenumber());
if(ObjectUtils.isNotEmpty(asUser)){
addReceiver(user,asUser);
}else{
throw new ServiceException("未查询到APP用户");
}
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
@ -359,9 +375,42 @@ public class SysUserServiceImpl implements ISysUserService
insertUserPost(user);
// 分账项目
setDividendItem(user);
AsUser asUser = asUserService.selectUserByPhone(user.getPhonenumber());
if(ObjectUtils.isNotEmpty(asUser)){
// 删除分账接收方
deleteReceiver(asUser.getWxopenid());
// 添加分账接收方
addReceiver(user,asUser);
}else{
throw new ServiceException("未查询到APP用户");
}
return userMapper.updateUser(user);
}
/**
* 添加分账接收方
*
* @param user 用户信息
*/
private void addReceiver(SysUser user,AsUser asUser) {
// 绑定app用户
userMapper.bandAppUser(asUser.getUserId(), user.getUserId());
// 添加分账接收方
AddReceiverResponse addReceiverResponse = wxPayService.addReceiver(asUser.getWxopenid());
log.info("添加分账接收方响应:【{}】", JSON.toJSON(addReceiverResponse));
}
/**
* 删除分账接收方
*
* @param openid
*/
private void deleteReceiver(String openid) {
// 添加分账接收方
DeleteReceiverResponse deleteReceiverResponse = wxPayService.deleteReceiver(openid);
log.info("删除分账接收方响应:【{}】", JSON.toJSON(deleteReceiverResponse));
}
/**
* 用户授权角色
*
@ -508,6 +557,10 @@ public class SysUserServiceImpl implements ISysUserService
userRoleMapper.deleteUserRoleByUserId(userId);
// 删除用户与岗位表
userPostMapper.deleteUserPostByUserId(userId);
SysUser user = selectUserById(userId);
AsUser asUser = asUserService.selectUserByPhone(user.getPhonenumber());
// 删除分账接收方
deleteReceiver(asUser.getWxopenid());
return userMapper.deleteUserById(userId);
}

View File

@ -171,14 +171,6 @@ public class WxPayService implements IWxPayService {
request.setAppid(wxPayConfig.getAppId());
request.setTransactionId(transactionId);// 微信订单号
request.setOutOrderNo(IdUtils.getOrderNo("fz"));// 商户系统内部分账单号
// List<CreateOrderReceiver> 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);

View File

@ -26,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="areaId" column="area_id" />
<result property="dividendProportion" column="dividend_proportion" />
<result property="dividendItem" column="dividend_item" />
<result property="appUserId" column="app_user_id" />
<result property="cooperationTime" column="cooperation_time" />
<result property="dividendStatus" column="dividend_status" />
<association property="dept" javaType="SysDept" resultMap="deptResult" />
@ -52,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectUserVo">
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,u.area_id,u.dividend_proportion,u.dividend_item, u.cooperation_time,u.dividend_status,
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,u.area_id,u.dividend_proportion,u.dividend_item,u.app_user_id, u.cooperation_time,u.dividend_status,
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status,a.area_name areaName
from sys_user u
@ -63,7 +64,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</sql>
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, u.area_id,u.dividend_proportion,u.dividend_item,u.cooperation_time,u.dividend_status,d.dept_name, d.leader,a.area_name areaName from sys_user u
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, u.area_id,u.dividend_proportion,u.dividend_item,u.app_user_id, u.cooperation_time,u.dividend_status,d.dept_name, d.leader,a.area_name areaName from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join et_operating_area a on u.area_id = a.area_id
where u.del_flag = '0'
@ -170,6 +171,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="areaId != null and areaId != ''">area_id,</if>
<if test="dividendProportion != null and dividendProportion != ''">dividend_proportion,</if>
<if test="dividendItem != null and dividendItem != ''">dividend_item,</if>
<if test="appUserId != null and appUserId != ''">app_user_id,</if>
<if test="cooperationTime != null">cooperation_time,</if>
<if test="dividendStatus != null and dividendStatus != ''">dividend_status,</if>
create_time
@ -190,6 +192,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="areaId != null">#{areaId},</if>
<if test="dividendProportion != null and dividendProportion != ''">#{dividendProportion},</if>
<if test="dividendItem != null and dividendItem != ''">#{dividendItem},</if>
<if test="appUserId != null and appUserId != ''">#{appUserId},</if>
<if test="cooperationTime != null">#{cooperationTime},</if>
<if test="dividendStatus != null and dividendStatus != ''">#{dividendStatus},</if>
sysdate()
@ -216,6 +219,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="areaId != null">area_id = #{areaId},</if>
<if test="dividendProportion != null">dividend_proportion = #{dividendProportion},</if>
<if test="dividendItem != null">dividend_item = #{dividendItem},</if>
<if test="appUserId != null">app_user_id = #{appUserId},</if>
<if test="cooperationTime != null">cooperation_time = #{cooperationTime},</if>
<if test="dividendStatus != null">dividend_status = #{dividendStatus},</if>
update_time = sysdate()
@ -235,6 +239,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
update sys_user set password = #{password} where user_name = #{userName}
</update>
<update id="bandAppUser" parameterType="Long">
update sys_user set app_user_id = #{appUserId} where user_id = #{userId}
</update>
<delete id="deleteUserById" parameterType="Long">
update sys_user set del_flag = '2' where user_id = #{userId}
</delete>