临时提交

This commit is contained in:
墨大叔 2024-09-13 15:09:35 +08:00
parent 7f9727c246
commit ed1df8462c
36 changed files with 1585 additions and 41 deletions

View File

@ -220,4 +220,8 @@ public class Constants
* 设备已绑定
*/
public static final int DEVICE_BINDED = 2;
/**
* 根部门
*/
public static final long ROOT_DEPT = 100L;
}

View File

@ -166,4 +166,7 @@ public class SmUser extends BaseEntity
@ApiModelProperty("是否已经实名认证")
@JsonView(JsonViewProfile.App.class)
private Boolean isReal;
@ApiModelProperty("用户类型")
private String type;
}

View File

@ -22,7 +22,8 @@ public enum RedisLockKey {
ADD_TIME_BILL("add_time_bill", "创建时长订单"),
PAY_BILL("pay_bill", "支付订单"),
PREPAY_DEPOSIT("prepay_deposit", "支付押金"),
ADD_RECHARGE_ORDER("add_recharge_order", "创建充值订单");
ADD_RECHARGE_ORDER("add_recharge_order", "创建充值订单"),
RECOVER_DEVICE_BALANCE("recover_device_balance", "恢复设备余额");
private final String key;

View File

@ -15,8 +15,9 @@ import java.util.Objects;
@AllArgsConstructor
public enum UserType {
TENANT("00", "租户"),
LANDLORD("01", "商户");
USER("1", "普通用户"),
MCH("2", "商户"),
AGENT("3", "代理商");
private final String type;
private final String name;

View File

@ -43,4 +43,9 @@ public class ServiceUtil {
throw new ServiceException(result.getMsg(), result.getCode());
}
}
public static void assertion(boolean flag, String format, Object ...args) {
if (flag) {
throw new ServiceException(String.format(format, args), 500);
}
}
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.domain.vo;
import lombok.Data;
/**
* @author wjh
* 2024/8/23
*/
@Data
public class CommonCountVO<T> {
//
private T key;
// 数量
private Integer count;
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author wjh
* 2024/8/23
*/
@Data
public class CommonSumVO<T> {
private T key;
private BigDecimal sum;
}

View File

@ -1,10 +1,14 @@
package com.ruoyi.iot.service;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.utils.NumberUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.constants.ReceiveConstants;
import com.ruoyi.iot.domain.IotDeviceDetail;
import com.ruoyi.iot.domain.ReceiveMsg;
import com.ruoyi.iot.domain.response.CommandResponse;
import com.ruoyi.iot.enums.ReceiveStatus;
import com.ruoyi.iot.enums.ReceiveType;
import com.ruoyi.ss.device.domain.Device;
@ -21,9 +25,11 @@ import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
/**
@ -46,6 +52,12 @@ public class IotReceiveServiceImpl implements IotReceiveService{
@Autowired
private IotService iotService;
@Autowired
private RedisLock redisLock;
@Autowired
private TransactionTemplate transactionTemplate;
@Override
public void handleReceive(ReceiveMsg msg) {
if (msg == null) {
@ -65,43 +77,51 @@ public class IotReceiveServiceImpl implements IotReceiveService{
// 恢复设备的余额
private void recoverBalance(ReceiveMsg msg) {
// 查询设备
DeviceVO device = deviceService.selectByMac(msg.getDevName());
if (device == null) {
String lockKey = msg.getDevName();
if (!redisLock.lock(RedisLockKey.RECOVER_DEVICE_BALANCE, lockKey)){
return;
}
try {
// 查询设备
DeviceVO device = deviceService.selectByMac(msg.getDevName());
if (device == null) {
return;
}
// 查询未结束的订单
TransactionBillQuery query = new TransactionBillQuery();
query.setDeviceId(device.getDeviceId());
query.setIsFinished(false);
query.setStatus(TransactionBillStatus.SUCCESS.getStatus());
query.setSuitFeeTypes(SuitFeeType.singleList());
List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);
// 判断上次恢复余额的时间和本次恢复余额的时间是否相同若相同则视为重复推送则忽略
if (device.getLastRecoverTime() != null && msg.getAt().equals(device.getLastRecoverTime())) {
return;
}
if (CollectionUtils.isNotEmptyElement(billList)) {
// 拼接剩余时长/电量
transactionAssembler.assembleSuitSurplus(billList);
// 待恢复的时长
LocalDateTime expireTime = device.getExpireTime();
long seconds = 0; // 待恢复的时长
BigDecimal ele = BigDecimal.ZERO; // 待恢复的电量
// 待恢复的电量
BigDecimal ele = BigDecimal.ZERO;
for (TransactionBillVO bill : billList) {
if (bill.getSuitSurplus().compareTo(BigDecimal.ZERO) > 0) {
if (SuitFeeType.rechargeTimeList().contains(bill.getSuitFeeType())) {
seconds += bill.getSuitSurplus().longValue();
} else if (SuitFeeType.rechargeCountList().contains(bill.getSuitFeeType())) {
ele = ele.add(bill.getSuitSurplus());
// 若有需要恢复余额的设备则进行操作
if (expireTime != null || ele.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal finalEle = ele;
transactionTemplate.execute(status -> {
// 记录上次恢复余额的时间
int update = deviceService.updateLastRecoverTime(device.getDeviceId(), msg.getAt());
ServiceUtil.assertion(update != 1, "更新设备信息失败");
log.info("设备:{} 恢复余额,过期时间:{},剩余电量:{}", device.getMac(), expireTime, finalEle);
if (expireTime != null ) {
int setTime = deviceService.setTime(device, expireTime, true, 3);
ServiceUtil.assertion(setTime != 1, "恢复设备时长失败");
}
if (finalEle.compareTo(BigDecimal.ZERO) > 0) {
CommandResponse res = iotService.trySetEle(device.getMac(), finalEle, device.getModelProductId(), 3);
ServiceUtil.assertion(!res.isSuccess(), "设备电量恢复失败:%s", res.getMsg());
}
}
}
if (seconds > 0) {
iotService.setTime(device.getMac(), seconds, device.getModelProductId());
}
if (ele.compareTo(BigDecimal.ZERO) > 0) {
iotService.setEle(device.getMac(), ele, device.getModelProductId());
return update;
});
}
} finally {
redisLock.unlock(RedisLockKey.RECOVER_DEVICE_BALANCE, lockKey);
}
}

View File

@ -146,4 +146,22 @@ public interface IotService {
* 直接设置设备电量
*/
CommandResponse setEle(String deviceName, BigDecimal ele, String productId);
/**
* 尝试设置设备剩余时长
* @param mac MAC
* @param seconds 时长
* @param productId 产品ID
* @param tryCount 尝试次数
*/
CommandResponse trySetTime(String mac, long seconds, String productId, int tryCount);
/**
* 尝试设置设备剩余时长
* @param mac MAC
* @param ele 电量
* @param productId 产品ID
* @param tryCount 尝试次数
*/
CommandResponse trySetEle(String mac, BigDecimal ele, String productId, int tryCount);
}

View File

@ -283,4 +283,37 @@ public class IotServiceImpl implements IotService {
return JSON.parseObject(result, CommandResponse.class);
}
@Override
public CommandResponse trySetTime(String mac, long seconds, String productId, int tryCount) {
CommandResponse res = this.setTime(mac, seconds, productId);
if (!res.isSuccess()) {
if (tryCount > 0) {
try {
Thread.sleep(1000);
return trySetTime(mac, seconds, productId, --tryCount);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
return res;
}
@Override
public CommandResponse trySetEle(String mac, BigDecimal ele, String productId, int tryCount) {
CommandResponse res = this.setEle(mac, ele, productId);
if (!res.isSuccess()) {
if (tryCount > 0) {
try {
Thread.sleep(1000);
return trySetEle(mac, ele, productId, --tryCount);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
return res;
}
}

View File

@ -0,0 +1,94 @@
package com.ruoyi.ss.bonus.domain;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import com.ruoyi.common.core.domain.JsonViewProfile;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* 分成明细对象 ss_bonus
*
* @author ruoyi
* @date 2024-08-20
*/
@Data
public class Bonus extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
@Excel(name = "订单ID")
@ApiModelProperty("订单ID")
@JsonView(JsonViewProfile.AppMch.class)
private Long billId;
@Excel(name = "订单编号")
@ApiModelProperty("订单编号")
@JsonView(JsonViewProfile.AppMch.class)
private String billNo;
@Excel(name = "状态")
@ApiModelProperty("状态")
@JsonView(JsonViewProfile.AppMch.class)
private String status;
@Excel(name = "收款方ID")
@ApiModelProperty("收款方ID")
@JsonView(JsonViewProfile.AppMch.class)
private Long arrivalId;
@Excel(name = "收款方名称")
@ApiModelProperty("收款方名称")
@JsonView(JsonViewProfile.AppMch.class)
private String arrivalName;
@Excel(name = "收款方类型")
@ApiModelProperty("收款方类型")
@JsonView(JsonViewProfile.AppMch.class)
private String arrivalType;
@Excel(name = "分成比例")
@ApiModelProperty("分成比例")
@JsonView(JsonViewProfile.AppMch.class)
private BigDecimal point;
@Excel(name = "分成金额")
@ApiModelProperty("分成金额")
@JsonView(JsonViewProfile.AppMch.class)
private BigDecimal amount;
@Excel(name = "退款金额")
@ApiModelProperty("退款金额")
@JsonView(JsonViewProfile.AppMch.class)
private BigDecimal refundAmount;
@Excel(name = "收款方祖级列表")
@ApiModelProperty("收款方祖级列表")
private String ancestors;
@Excel(name = "收款方部门")
@ApiModelProperty("收款方部门")
private Long deptId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "实际分成时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("实际分成时间")
@JsonView(JsonViewProfile.AppMch.class)
private LocalDateTime payTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "预计分成时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("预计分成时间")
@JsonView(JsonViewProfile.AppMch.class)
private LocalDateTime prePayTime;
}

View File

@ -0,0 +1,30 @@
package com.ruoyi.ss.bonus.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import nonapi.io.github.classgraph.utils.LogNode;
import java.util.List;
/**
* @author wjh
* 2024/8/23
*/
@Data
public class BonusProvideQuery {
@ApiModelProperty("到账方ID")
private Long arrivalId;
@ApiModelProperty("到账方类型")
private List<String> arrivalTypes;
@ApiModelProperty("提供者ID列表")
private List<Long> providerIds;
@ApiModelProperty("提供者类型")
private List<String> providerTypes;
@ApiModelProperty("分成状态")
private String status;
}

View File

@ -0,0 +1,37 @@
package com.ruoyi.ss.bonus.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import java.util.List;
/**
* @author wjh
* 2024/8/20
*/
@Data
public class BonusQuery extends BonusVO {
@ApiModelProperty("订单ID列表")
private List<Long> billIds;
@ApiModelProperty("分成方ID列表")
private List<Long> arrivalIds;
@ApiModelProperty("分成方类型列表")
private List<String> arrivalTypes;
@ApiModelProperty("支付时间:年")
private Integer payTimeYear;
@ApiModelProperty("支付日期(起始)")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate payDateStart;
@ApiModelProperty("支付日期(结束)")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate payDateEnd;
}

View File

@ -0,0 +1,11 @@
package com.ruoyi.ss.bonus.domain;
import lombok.Data;
/**
* @author wjh
* 2024/8/20
*/
@Data
public class BonusVO extends Bonus {
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.ss.bonus.domain.enums;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.enums.UserType;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author wjh
* 2024/8/21
*/
@Getter
@AllArgsConstructor
public enum BonusArrivalType {
PLATFORM("1", "平台"),
AGENT("2", "代理商"),
MCH("3", "商户");
private final String type;
private final String msg;
public static BonusArrivalType parseByUserType(String type) {
if (UserType.MCH.equalsType(type)) {
return MCH;
}
if (UserType.AGENT.equalsType(type)) {
return AGENT;
}
return null;
}
public static BonusArrivalType parseByDept(SysDept dept) {
if (dept == null) {
return null;
}
if (Constants.ROOT_DEPT == dept.getDeptId()) {
return PLATFORM;
}
return null;
}
public static List<String> asList(BonusArrivalType ...types) {
return Arrays.stream(types).map(BonusArrivalType::getType).collect(Collectors.toList());
}
/**
* 用户表
*/
public static List<String> userList() {
return asList(AGENT, MCH);
}
/**
* 部门表
*/
public static List<String> deptList() {
return asList(PLATFORM);
}
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.ss.bonus.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author wjh
* 2024/8/21
*/
@Getter
@AllArgsConstructor
public enum BonusStatus {
UN_DIVIDEND("1", "未分成"),
DIVIDEND("2", "已分成");
private final String status;
private final String msg;
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.ss.bonus.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* @author wjh
* 2024/8/30
*/
@Data
public class BonusDailyAmountVO {
private LocalDate key;
private BigDecimal sum;
}

View File

@ -0,0 +1,21 @@
package com.ruoyi.ss.bonus.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author wjh
* 2024/8/28
*/
@Data
public class BonusMonthAmountVO {
@ApiModelProperty("月份")
private Integer month;
@ApiModelProperty("金额")
private BigDecimal amount;
}

View File

@ -0,0 +1,22 @@
package com.ruoyi.ss.bonus.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author wjh
* 2024/8/23
*/
@Data
public class ProvideBonusVO {
// 提供者ID
private Long providerId;
// 到账人ID
private Long arrivalId;
// 到账金额
private BigDecimal arrivalAmount;
}

View File

@ -0,0 +1,113 @@
package com.ruoyi.ss.bonus.mapper;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import com.ruoyi.common.domain.vo.CommonCountVO;
import com.ruoyi.common.domain.vo.CommonSumVO;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.bonus.domain.BonusProvideQuery;
import com.ruoyi.ss.bonus.domain.BonusVO;
import com.ruoyi.ss.bonus.domain.BonusQuery;
import com.ruoyi.ss.bonus.domain.vo.BonusDailyAmountVO;
import com.ruoyi.ss.bonus.domain.vo.BonusMonthAmountVO;
import com.ruoyi.ss.bonus.domain.vo.ProvideBonusVO;
import org.apache.ibatis.annotations.Param;
/**
* 分成明细Mapper接口
*
* @author ruoyi
* @date 2024-08-20
*/
public interface BonusMapper
{
/**
* 查询分成明细
*
* @param id 分成明细主键
* @return 分成明细
*/
public BonusVO selectBonusById(Long id);
/**
* 查询分成明细列表
*
* @param query 分成明细
* @return 分成明细集合
*/
public List<BonusVO> selectBonusList(@Param("query")BonusQuery query);
/**
* 新增分成明细
*
* @param bonus 分成明细
* @return 结果
*/
public int insertBonus(Bonus bonus);
/**
* 修改分成明细
*
* @param bonus 分成明细
* @return 结果
*/
public int updateBonus(@Param("data") Bonus bonus);
/**
* 删除分成明细
*
* @param id 分成明细主键
* @return 结果
*/
public int deleteBonusById(Long id);
/**
* 批量删除分成明细
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBonusByIds(Long[] ids);
/**
* 批量插入
*/
int batchInsert(@Param("list") List<Bonus> bonusList);
/**
* 批量更新分成金额
*/
int batchUpdateAmount(@Param("list") List<BonusVO> list);
/**
* 根据到账方统计
*/
List<CommonCountVO<Long>> selectCountByArrival(@Param("query") BonusQuery query);
/**
* 根据到账方统计金额
*/
List<CommonSumVO<Long>> selectBillAmountByArrival(@Param("query") BonusQuery query);
/**
* 查询提供分成
*/
List<ProvideBonusVO> selectProvideBonus(@Param("query") BonusProvideQuery query);
/**
* 增加退款金额
*/
int addRefundAmount(@Param("id") Long id, @Param("amount") BigDecimal amount);
/**
* 按月查询
*/
List<BonusMonthAmountVO> selectMonthAmount(@Param("query") BonusQuery query);
/**
* 按日查询
*/
List<BonusDailyAmountVO> selectDailyAmount(@Param("query") BonusQuery query);
}

View File

@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ss.bonus.mapper.BonusMapper">
<resultMap type="BonusVO" id="BonusResult" autoMapping="true"/>
<sql id="selectBonusVo">
select
sb.id,
sb.bill_id,
sb.bill_no,
sb.status,
sb.arrival_id,
sb.arrival_name,
sb.arrival_type,
sb.point,
sb.amount,
sb.refund_amount,
sb.ancestors,
sb.create_time,
sb.pay_time,
sb.dept_id,
sb.pre_pay_time
<include refid="searchTables"/>
</sql>
<sql id="searchTables">
from ss_bonus sb
left join sys_dept sd on sd.dept_id = sb.dept_id
</sql>
<sql id="searchCondition">
<if test="query.id != null "> and sb.id = #{query.id}</if>
<if test="query.billId != null "> and sb.bill_id = #{query.billId}</if>
<if test="query.deptId != null "> and sb.dept_id = #{query.deptId}</if>
<if test="query.billNo != null and query.billNo != ''"> and sb.bill_no like concat('%', #{query.billNo}, '%')</if>
<if test="query.status != null and query.status != ''"> and sb.status = #{query.status}</if>
<if test="query.arrivalId != null "> and sb.arrival_id = #{query.arrivalId}</if>
<if test="query.arrivalType != null and query.arrivalType != ''"> and sb.arrival_type = #{query.arrivalType}</if>
<if test="query.arrivalName != null and query.arrivalName != ''"> and sb.arrival_name like concat('%', #{query.arrivalName}, '%')</if>
<if test="query.payTimeYear != null "> and year(sb.pay_time) = #{query.payTimeYear}</if>
<if test="query.payDateStart != null "> and date(sb.pay_time) >= #{query.payDateStart}</if>
<if test="query.payDateEnd != null "> and date(sb.pay_time) &lt;= #{query.payDateEnd}</if>
<if test="query.billIds != null and query.billIds.size() > 0 ">
and sb.bill_id in
<foreach collection="query.billIds" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
<if test="query.arrivalTypes != null and query.arrivalTypes.size() > 0 ">
and sb.arrival_type in
<foreach collection="query.arrivalTypes" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
<if test="query.arrivalIds != null and query.arrivalIds.size() > 0 ">
and sb.arrival_id in
<foreach collection="query.arrivalIds" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
${query.params.dataScope}
${@com.ruoyi.common.utils.DataScopeUtil@dataScopeDept("sd", query.needScope)}
</sql>
<select id="selectBonusList" parameterType="BonusQuery" resultMap="BonusResult">
<include refid="selectBonusVo"/>
<where>
<include refid="searchCondition"/>
</where>
</select>
<select id="selectBonusById" parameterType="Long" resultMap="BonusResult">
<include refid="selectBonusVo"/>
where sb.id = #{id}
</select>
<select id="selectCountByArrival" resultType="com.ruoyi.common.domain.vo.CommonCountVO">
select
sb.arrival_id as `key`,
count(sb.id) as `count`
<include refid="searchTables"/>
<where>
<include refid="searchCondition"/>
</where>
group by `key`
</select>
<select id="selectBillAmountByArrival" resultType="com.ruoyi.common.domain.vo.CommonSumVO">
select
sb.arrival_id as `key`,
sum(stb.money) as `sum`
from ss_bonus sb
left join sm_transaction_bill stb on stb.bill_id = sb.bill_id
<where>
<include refid="searchCondition"/>
</where>
group by `key`
</select>
<resultMap id="ProvideBonusVO" type="ProvideBonusVO" autoMapping="true">
<result property="arrivalAmount" column="arrival_amount" typeHandler="com.ruoyi.system.mapper.typehandler.NonNullDecimalTypeHandler"/>
</resultMap>
<select id="selectProvideBonus" resultMap="ProvideBonusVO">
select
p.arrival_id as provider_id,
a.arrival_id as arrival_id,
sum(a.amount - a.refund_amount) as arrival_amount
from ss_bonus p
left join ss_bonus a on a.bill_id = p.bill_id
<where>
<if test="query.providerIds != null and query.providerIds.size() > 0">
and p.arrival_id in
<foreach collection="query.providerIds" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
<if test="query.providerTypes != null and query.providerTypes.size() > 0">
and p.arrival_type in
<foreach collection="query.providerTypes" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
<if test="query.arrivalId != null">
and a.arrival_id = #{query.arrivalId}
</if>
<if test="query.arrivalTypes != null and query.arrivalTypes.size() > 0">
and a.arrival_type in
<foreach collection="query.arrivalTypes" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</where>
group by p.arrival_id, a.arrival_id
</select>
<resultMap id="BonusMonthAmountVO" type="BonusMonthAmountVO">
<result property="amount" column="amount" typeHandler="com.ruoyi.system.mapper.typehandler.NonNullDecimalTypeHandler"/>
</resultMap>
<select id="selectMonthAmount" resultMap="BonusMonthAmountVO">
select
month(sb.pay_time) as month,
sum(amount) as amount
<include refid="searchTables"/>
<where>
<include refid="searchCondition"/>
</where>
group by `month`
</select>
<select id="selectDailyAmount" resultType="BonusDailyAmountVO">
select
date(sb.pay_time) as `key`,
sum(amount) as `sum`
<include refid="searchTables"/>
<where>
<include refid="searchCondition"/>
</where>
group by `key`
</select>
<insert id="insertBonus" parameterType="Bonus" useGeneratedKeys="true" keyProperty="id">
insert into ss_bonus
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="billId != null">bill_id,</if>
<if test="billNo != null">bill_no,</if>
<if test="status != null and status != ''">`status`,</if>
<if test="arrivalId != null">arrival_id,</if>
<if test="arrivalName != null and arrivalName != ''">arrival_name,</if>
<if test="arrivalType != null and arrivalType != ''">arrival_type,</if>
<if test="point != null">`point`,</if>
<if test="amount != null">amount,</if>
<if test="refundAmount != null">refund_amount,</if>
<if test="ancestors != null and ancestors != ''">ancestors,</if>
<if test="createTime != null">create_time,</if>
<if test="payTime != null">pay_time,</if>
<if test="deptId != null">dept_id,</if>
<if test="prePayTime != null">pre_pay_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="billId != null">#{billId},</if>
<if test="billNo != null">#{billNo},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="arrivalId != null">#{arrivalId},</if>
<if test="arrivalName != null and arrivalName != ''">#{arrivalName},</if>
<if test="arrivalType != null and arrivalType != ''">#{arrivalType},</if>
<if test="point != null">#{point},</if>
<if test="amount != null">#{amount},</if>
<if test="refundAmount != null">#{refundAmount},</if>
<if test="ancestors != null and ancestors != ''">#{ancestors},</if>
<if test="createTime != null">#{createTime},</if>
<if test="payTime != null">#{payTime},</if>
<if test="deptId != null">#{deptId},</if>
<if test="prePayTime != null">#{prePayTime},</if>
</trim>
</insert>
<insert id="batchInsert">
insert into ss_bonus(
bill_id,
bill_no,
status,
arrival_id,
arrival_name,
arrival_type,
point,
amount,
refund_amount,
ancestors,
create_time,
pay_time,
dept_id,
pre_pay_time
)
values
<foreach collection="list" item="i" separator=",">
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="i.billId != null">#{i.billId},</if>
<if test="i.billId == null">default,</if>
<if test="i.billNo != null">#{i.billNo},</if>
<if test="i.billNo == null">default,</if>
<if test="i.status != null and i.status != ''">#{i.status},</if>
<if test="i.status == null">default,</if>
<if test="i.arrivalId != null">#{i.arrivalId},</if>
<if test="i.arrivalId == null">default,</if>
<if test="i.arrivalName != null and i.arrivalName != ''">#{i.arrivalName},</if>
<if test="i.arrivalName == null or i.arrivalName == ''">default,</if>
<if test="i.arrivalType != null and i.arrivalType != ''">#{i.arrivalType},</if>
<if test="i.arrivalType == null or i.arrivalType == ''">default,</if>
<if test="i.point != null">#{i.point},</if>
<if test="i.point == null">default,</if>
<if test="i.amount != null">#{i.amount},</if>
<if test="i.amount == null">default,</if>
<if test="i.refundAmount != null">#{i.refundAmount},</if>
<if test="i.refundAmount == null">default,</if>
<if test="i.ancestors != null and i.ancestors != ''">#{i.ancestors},</if>
<if test="i.ancestors == null or i.ancestors == ''">default,</if>
<if test="i.createTime != null">#{i.createTime},</if>
<if test="i.createTime == null">default,</if>
<if test="i.payTime != null">#{i.payTime},</if>
<if test="i.payTime == null">default,</if>
<if test="i.deptId != null">#{i.deptId},</if>
<if test="i.deptId == null">default,</if>
<if test="i.prePayTime != null">#{i.prePayTime},</if>
<if test="i.prePayTime == null">default,</if>
</trim>
</foreach>
</insert>
<insert id="addRefundAmount">
update ss_bonus
set refund_amount = refund_amount + #{amount}
where id = #{id} and amount >= refund_amount + #{amount}
</insert>
<update id="updateBonus" parameterType="Bonus">
update ss_bonus
<trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/>
</trim>
where id = #{data.id}
</update>
<update id="batchUpdateAmount">
update ss_bonus
<trim prefix="SET" suffixOverrides=",">
<foreach open="amount = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.amount != null">
WHEN #{item.id} THEN #{item.amount}
</when>
<otherwise>
WHEN #{item.id} THEN amount
</otherwise>
</choose>
</foreach>
<foreach open="status = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.status != null">
WHEN #{item.id} THEN #{item.status}
</when>
<otherwise>
WHEN #{item.id} THEN `status`
</otherwise>
</choose>
</foreach>
<foreach open="pay_time = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.payTime != null">
WHEN #{item.id} THEN #{item.payTime}
</when>
<otherwise>
WHEN #{item.id} THEN `pay_time`
</otherwise>
</choose>
</foreach>
</trim>
where id in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.id}
</foreach>
and `status` = '1'
</update>
<sql id="updateColumns">
<if test="data.billId != null">bill_id = #{data.billId},</if>
<if test="data.billNo != null">bill_no = #{data.billNo},</if>
<if test="data.status != null and data.status != ''">`status` = #{data.status},</if>
<if test="data.arrivalId != null">arrival_id = #{data.arrivalId},</if>
<if test="data.arrivalName != null and data.arrivalName != ''">arrival_name = #{data.arrivalName},</if>
<if test="data.arrivalType != null and data.arrivalType != ''">arrival_type = #{data.arrivalType},</if>
<if test="data.point != null">`point` = #{data.point},</if>
<if test="data.amount != null">amount = #{data.amount},</if>
<if test="data.refundAmount != null">refund_amount = #{data.refundAmount},</if>
<if test="data.ancestors != null and data.ancestors != ''">ancestors = #{data.ancestors},</if>
<if test="data.createTime != null">create_time = #{data.createTime},</if>
<if test="data.payTime != null">pay_time = #{data.payTime},</if>
<if test="data.deptId != null">dept_id = #{data.deptId},</if>
<if test="data.prePayTime != null">pre_pay_time = #{data.prePayTime},</if>
</sql>
<delete id="deleteBonusById" parameterType="Long">
delete from ss_bonus where id = #{id}
</delete>
<delete id="deleteBonusByIds" parameterType="String">
delete from ss_bonus where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -0,0 +1,32 @@
package com.ruoyi.ss.bonus.service;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.transactionBill.domain.bo.RechargeBO;
import com.ruoyi.ss.user.domain.SmUserVo;
import java.util.List;
/**
* @author wjh
* 2024/8/20
*/
public interface BonusConverter {
/**
* 订单转为分成明细
*/
List<Bonus> toPo(RechargeBO bo);
/**
* 生成分成列表
*
* @param mch
* @param agent
* @param platform
* @param device
* @return
*/
List<Bonus> genBonusList(SmUserVo mch, SmUserVo agent, SysDept platform, DeviceVO device);
}

View File

@ -0,0 +1,120 @@
package com.ruoyi.ss.bonus.service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import com.ruoyi.common.domain.vo.CommonCountVO;
import com.ruoyi.common.domain.vo.CommonSumVO;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.bonus.domain.BonusProvideQuery;
import com.ruoyi.ss.bonus.domain.BonusVO;
import com.ruoyi.ss.bonus.domain.BonusQuery;
import com.ruoyi.ss.bonus.domain.vo.BonusDailyAmountVO;
import com.ruoyi.ss.bonus.domain.vo.BonusMonthAmountVO;
import com.ruoyi.ss.bonus.domain.vo.ProvideBonusVO;
/**
* 分成明细Service接口
*
* @author ruoyi
* @date 2024-08-20
*/
public interface BonusService
{
/**
* 查询分成明细
*
* @param id 分成明细主键
* @return 分成明细
*/
public BonusVO selectBonusById(Long id);
/**
* 查询分成明细列表
*
* @param bonus 分成明细
* @return 分成明细集合
*/
public List<BonusVO> selectBonusList(BonusQuery bonus);
/**
* 新增分成明细
*
* @param bonus 分成明细
* @return 结果
*/
public int insertBonus(Bonus bonus);
/**
* 修改分成明细
*
* @param bonus 分成明细
* @return 结果
*/
public int updateBonus(Bonus bonus);
/**
* 批量删除分成明细
*
* @param ids 需要删除的分成明细主键集合
* @return 结果
*/
public int deleteBonusByIds(Long[] ids);
/**
* 删除分成明细信息
*
* @param id 分成明细主键
* @return 结果
*/
public int deleteBonusById(Long id);
/**
* 批量新增
*/
int batchInsert(List<Bonus> bonusList);
/**
* 处理分成支付
* @param bonusList 分成列表
* @param money 总金额
* @return 处理数量
*/
int payBonus(List<BonusVO> bonusList, BigDecimal money);
/**
* 根据到账方统计
*/
List<CommonCountVO<Long>> selectCountByArrival(BonusQuery query);
/**
* 根据到账方统计订单金额
*/
List<CommonSumVO<Long>> selectBillAmountByArrival(BonusQuery query);
/**
* 查询提供分成
*/
List<ProvideBonusVO> selectProvideBonus(BonusProvideQuery query);
/**
* 增加已退款金额
*/
int addRefundAmount(Long id, BigDecimal amount);
/**
* 数据隔离过滤
*/
<T extends Bonus> List<T> filterBonusScope(List<T> bonusList);
/**
* 按月查询分成金额
*/
List<BonusMonthAmountVO> selectMonthAmount(BonusQuery query);
/**
* 按日查询分成
*/
List<BonusDailyAmountVO> selectDailyAmount(BonusQuery query);
}

View File

@ -0,0 +1,168 @@
package com.ruoyi.ss.bonus.service.impl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.bonus.domain.enums.BonusArrivalType;
import com.ruoyi.ss.bonus.domain.enums.BonusStatus;
import com.ruoyi.ss.bonus.service.BonusConverter;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.store.service.StoreService;
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
import com.ruoyi.ss.transactionBill.domain.bo.RechargeBO;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import com.ruoyi.ss.user.domain.SmUserVo;
import com.ruoyi.ss.user.service.ISmUserService;
import com.ruoyi.system.service.ISysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
/**
* @author wjh
* 2024/8/20
*/
@Service
public class BonusConverterImpl implements BonusConverter {
@Autowired
private ISmUserService userService;
@Autowired
private StoreService storeService;
@Autowired
private ISysDeptService deptService;
@Autowired
private TransactionBillService transactionBillService;
/**
* 订单转为分成明细
*
* @param bo
*/
@Override
public List<Bonus> toPo(RechargeBO bo) {
if (bo == null) {
return Collections.emptyList();
}
TransactionBill bill = bo.getOrder();
if (bill == null) {
return null;
}
List<Bonus> result = bo.getBonusList();
for (Bonus bonus : result) {
bonus.setBillId(bill.getBillId());
bonus.setBillNo(bill.getBillNo());
}
return result;
}
@Override
public List<Bonus> genBonusList(SmUserVo mch, SmUserVo agent, SysDept platform, DeviceVO device) {
List<Bonus> result = new ArrayList<>();
BigDecimal point = BigDecimal.valueOf(100);
// TODO 平台收取服务费
// TODO 代理商收取服务费
// TODO 剩余的给商户
//
// // 根据设备的投资人经营场所获取各自的分成比例
// result.add(this.toPo(store, storeInvestor, mch));
// result.add(this.toPo(mch, mch.getPoint()));
// BigDecimal lastPoint = mch.getPoint().add(storeInvestor.getPoint()); // 上一次的分成比例
//
// // 投资人上级
// if (StringUtils.hasText(mch.getAncestors()) && CollectionUtils.isNotEmpty(agent)) {
// List<Long> collect = Arrays.stream(mch.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
// for (int i = collect.size() - 1; i >= 0; i --) {
// Long id = collect.get(i);
// SmUserVo investorAncestor = agent.stream().filter(item -> item.getUserId().equals(id)).findFirst().orElse(null);
// if (investorAncestor != null) {
// result.add(this.toPo(investorAncestor, investorAncestor.getPoint().subtract(lastPoint)));
// lastPoint = investorAncestor.getPoint();
// }
// }
// }
//
// // 直属部门
// result.add(this.toPo(platform, platform.getPoint().subtract(lastPoint)));
// lastPoint = platform.getPoint();
//
// // 直属部门的祖级列表
// if (StringUtils.hasText(platform.getAncestors()) && CollectionUtils.isNotEmpty(investorDeptList)) {
// List<Long> collect = Arrays.stream(platform.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
// for (int i = collect.size() - 1; i >= 0; i --) {
// Long id = collect.get(i);
// SysDept d = investorDeptList.stream().filter(item -> item.getDeptId().equals(id)).findFirst().orElse(null);
// if (d != null) {
// result.add(this.toPo(d, d.getPoint().subtract(lastPoint)));
// lastPoint = d.getPoint();
// }
// }
// }
// 校验分成比例
// ServiceUtil.assertion(lastPoint.compareTo(BigDecimal.valueOf(100)) != 0, "分成计算失败最终分成比例不等于100");
// BigDecimal total = BigDecimal.ZERO;
// BigDecimal decimal100 = BigDecimal.valueOf(100);
// for (Bonus bonus : result) {
// ServiceUtil.assertion(bonus.getPoint().compareTo(BigDecimal.ZERO) < 0, "分成计算失败分成比例小于0");
// ServiceUtil.assertion(bonus.getPoint().compareTo(decimal100) > 0, "分成计算失败分成比例大于100");
// total = total.add(bonus.getPoint());
// }
// ServiceUtil.assertion(total.compareTo(decimal100) > 0, "分成计算失败分成比例总和大于100");
return result;
}
private Bonus toPo(SysDept dept, BigDecimal point) {
if (dept == null) {
return null;
}
Bonus po = new Bonus();
po.setStatus(BonusStatus.UN_DIVIDEND.getStatus());
po.setArrivalId(dept.getDeptId());
po.setArrivalName(dept.getDeptName());
BonusArrivalType arrivalType = BonusArrivalType.parseByDept(dept);
if (arrivalType != null) {
po.setArrivalType(arrivalType.getType());
}
po.setPoint(point);
po.setAncestors(dept.getAncestors());
po.setDeptId(dept.getDeptId());
return po;
}
private Bonus toPo(SmUserVo user, BigDecimal point, DeviceVO device) {
if (user == null || point == null) {
return null;
}
Bonus po = new Bonus();
po.setStatus(BonusStatus.UN_DIVIDEND.getStatus());
po.setArrivalId(user.getUserId());
po.setArrivalName(user.getRealOrUserName());
BonusArrivalType arrivalType = BonusArrivalType.parseByUserType(user.getType());
if (arrivalType != null) {
po.setArrivalType(arrivalType.getType());
}
po.setPoint(point);
// po.setAncestors(Arrays.asList(device.getAgentId(), device.getUserId()));
po.setDeptId(Constants.ROOT_DEPT);
return po;
}
}

View File

@ -0,0 +1,269 @@
package com.ruoyi.ss.bonus.service.impl;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.domain.vo.CommonCountVO;
import com.ruoyi.common.domain.vo.CommonSumVO;
import com.ruoyi.common.enums.LoginType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.bonus.domain.BonusProvideQuery;
import com.ruoyi.ss.bonus.domain.enums.BonusArrivalType;
import com.ruoyi.ss.bonus.domain.enums.BonusStatus;
import com.ruoyi.ss.bonus.domain.vo.BonusDailyAmountVO;
import com.ruoyi.ss.bonus.domain.vo.BonusMonthAmountVO;
import com.ruoyi.ss.bonus.domain.vo.ProvideBonusVO;
import com.ruoyi.ss.recordBalance.domain.enums.RecordBalanceBstType;
import com.ruoyi.ss.store.service.StoreService;
import com.ruoyi.ss.user.service.ISmUserService;
import com.ruoyi.system.service.ISysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.ss.bonus.mapper.BonusMapper;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.bonus.domain.BonusVO;
import com.ruoyi.ss.bonus.domain.BonusQuery;
import com.ruoyi.ss.bonus.service.BonusService;
import org.springframework.transaction.support.TransactionTemplate;
/**
* 分成明细Service业务层处理
*
* @author ruoyi
* @date 2024-08-20
*/
@Service
public class BonusServiceImpl implements BonusService
{
@Autowired
private BonusMapper bonusMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private ISmUserService userService;
@Autowired
private ISysDeptService deptService;
@Autowired
private StoreService storeService;
/**
* 查询分成明细
*
* @param id 分成明细主键
* @return 分成明细
*/
@Override
public BonusVO selectBonusById(Long id)
{
return bonusMapper.selectBonusById(id);
}
/**
* 查询分成明细列表
*
* @param bonus 分成明细
* @return 分成明细
*/
@Override
public List<BonusVO> selectBonusList(BonusQuery bonus)
{
return bonusMapper.selectBonusList(bonus);
}
/**
* 新增分成明细
*
* @param bonus 分成明细
* @return 结果
*/
@Override
public int insertBonus(Bonus bonus)
{
this.buildBeforeCreate(bonus);
return bonusMapper.insertBonus(bonus);
}
// 补全新增前的数据
private void buildBeforeCreate(Bonus bonus) {
bonus.setCreateTime(DateUtils.getNowDate());
}
/**
* 修改分成明细
*
* @param bonus 分成明细
* @return 结果
*/
@Override
public int updateBonus(Bonus bonus)
{
return bonusMapper.updateBonus(bonus);
}
/**
* 批量删除分成明细
*
* @param ids 需要删除的分成明细主键
* @return 结果
*/
@Override
public int deleteBonusByIds(Long[] ids)
{
return bonusMapper.deleteBonusByIds(ids);
}
/**
* 删除分成明细信息
*
* @param id 分成明细主键
* @return 结果
*/
@Override
public int deleteBonusById(Long id)
{
return bonusMapper.deleteBonusById(id);
}
@Override
public int batchInsert(List<Bonus> bonusList) {
if (CollectionUtils.isEmptyElement(bonusList)) {
return 0;
}
for (Bonus bonus : bonusList) {
this.buildBeforeCreate(bonus);
}
return bonusMapper.batchInsert(bonusList);
}
@Override
public int payBonus(List<BonusVO> bonusList, BigDecimal money) {
if (CollectionUtils.isEmptyElement(bonusList)) {
return 0;
}
BigDecimal decimal100 = new BigDecimal(100);
BigDecimal dividedAmount = BigDecimal.ZERO; // 已分配金额
LocalDateTime now = LocalDateTime.now();
// 循环遍历构造分成金额
for (BonusVO bonus : bonusList) {
BigDecimal amount = money.multiply(bonus.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP);
bonus.setAmount(amount);
bonus.setStatus(BonusStatus.DIVIDEND.getStatus());
bonus.setPayTime(now);
dividedAmount = dividedAmount.add(amount);
}
// 平台吃掉误差
BonusVO platform = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())).findFirst().orElse(null);
ServiceUtil.assertion(platform == null, "平台不存在");
if (dividedAmount.compareTo(money) != 0) {
BigDecimal subtract = money.subtract(dividedAmount); // 误差值
platform.setAmount(platform.getAmount().add(subtract));
}
Integer result = transactionTemplate.execute(status -> {
// 循环遍历添加金额到账户上
for (BonusVO bonus : bonusList) {
int add = 0;
// 根据类型添加金额
if (BonusArrivalType.userList().contains(bonus.getArrivalType())) {
add = userService.addBalance(bonus.getArrivalId(), bonus.getAmount(), String.format("订单分成:%s", bonus.getBillNo()), RecordBalanceBstType.RECHARGE, bonus.getBillId());
} else if (BonusArrivalType.deptList().contains(bonus.getArrivalType())) {
// add = deptService.addBalance(bonus.getArrivalId(), bonus.getAmount(), String.format("订单分成:%s", bonus.getBillNo()), RecordBalanceBstType.RECHARGE, bonus.getBillId());
}
ServiceUtil.assertion(add != 1, "增加账户金额失败");
}
// 批量更新分成金额
int updateAmount = this.batchUpdateAmount(bonusList);
ServiceUtil.assertion(updateAmount != bonusList.size(), "更新分成金额失败");
return updateAmount;
});
return result == null ? 0 : result;
}
@Override
public List<CommonCountVO<Long>> selectCountByArrival(BonusQuery query) {
return bonusMapper.selectCountByArrival(query);
}
@Override
public List<CommonSumVO<Long>> selectBillAmountByArrival(BonusQuery query) {
return bonusMapper.selectBillAmountByArrival(query);
}
@Override
public List<ProvideBonusVO> selectProvideBonus(BonusProvideQuery query) {
return bonusMapper.selectProvideBonus(query);
}
@Override
public int addRefundAmount(Long id, BigDecimal amount) {
if (id == null) {
return 0;
}
ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "退款金额必须大于0");
return bonusMapper.addRefundAmount(id, amount);
}
@Override
public <T extends Bonus> List<T> filterBonusScope(List<T> bonusList) {
LoginUser loginUser = SecurityUtils.getLoginUser();
Long userId = loginUser.getUserId();
Long deptId = loginUser.getDeptId();
return bonusList.stream().filter(bonus -> {
// 前台用户过滤
if (LoginType.FRONT.equals(loginUser.getLoginType())) {
return BonusArrivalType.userList().contains(bonus.getArrivalType()) && (
Arrays.asList(bonus.getAncestors().split(",")).contains(userId.toString())
|| bonus.getArrivalId().equals(userId)
);
}
// 后台用户过滤
else if (LoginType.ADMIN.equals(loginUser.getLoginType())) {
if (BonusArrivalType.userList().contains(bonus.getArrivalType())) {
return true;
}
if (BonusArrivalType.deptList().contains(bonus.getArrivalType())) {
return Arrays.asList(bonus.getAncestors().split(",")).contains(deptId.toString())
|| bonus.getArrivalId().equals(deptId);
}
}
return false;
}).collect(Collectors.toList());
}
@Override
public List<BonusMonthAmountVO> selectMonthAmount(BonusQuery query) {
return bonusMapper.selectMonthAmount(query);
}
@Override
public List<BonusDailyAmountVO> selectDailyAmount(BonusQuery query) {
return bonusMapper.selectDailyAmount(query);
}
private int batchUpdateAmount(List<BonusVO> list) {
if (CollectionUtils.isEmptyElement(list)) {
return 0;
}
return bonusMapper.batchUpdateAmount(list);
}
}

View File

@ -199,4 +199,7 @@ public class Device extends BaseEntity
@ApiModelProperty("剩余电量(度)")
@JsonView(JsonViewProfile.App.class)
private BigDecimal surplusEle;
@ApiModelProperty("上次恢复余额的时间戳")
private Long lastRecoverTime;
}

View File

@ -10,6 +10,7 @@ import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@ -307,4 +308,15 @@ public interface DeviceService
*/
int resetEleWithBill(Long deviceId);
int updateLastRecoverTime(Long deviceId, Long lastRecoverTime);
/**
* 设置时间
* @param device 设备
* @param expireTime 过期时间
* @param withIot 是否操作设备
* @param tryCount 尝试次数
*/
int setTime(DeviceVO device, LocalDateTime expireTime, boolean withIot, int tryCount);
}

View File

@ -540,6 +540,20 @@ public class DeviceServiceImpl implements DeviceService
return result == null ? 0 : result;
}
@Override
public int updateLastRecoverTime(Long deviceId, Long lastRecoverTime) {
Device data = new Device();
data.setDeviceId(deviceId);
data.setLastRecoverTime(lastRecoverTime);
return deviceMapper.updateSmDevice(data);
}
// TODO
@Override
public int setTime(DeviceVO device, LocalDateTime expireTime, boolean withIot, int tryCount) {
return 0;
}
@Override
public boolean addTime(Long deviceId, long seconds, boolean withIot) {

View File

@ -1,5 +1,6 @@
package com.ruoyi.ss.transactionBill.domain.bo;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.channel.domain.ChannelVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.store.domain.StoreVo;
@ -9,6 +10,8 @@ import com.ruoyi.ss.transactionBill.domain.dto.RechargeDTO;
import com.ruoyi.ss.user.domain.SmUserVo;
import lombok.Data;
import java.util.List;
/**
* 设备充值BO
* @author wjh
@ -38,4 +41,7 @@ public class RechargeBO {
// 店铺
private StoreVo store;
// 分成详情
private List<Bonus> bonusList;
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.ss.transactionBill.service;
import com.ruoyi.ss.channel.domain.ChannelVO;
import com.ruoyi.ss.dashboard.vo.BillCountVo;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.payBill.domain.vo.DoPayVO;
@ -12,7 +13,9 @@ import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
import com.ruoyi.ss.transactionBill.domain.dto.WithdrawApprovalDTO;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
import com.ruoyi.ss.transactionBill.domain.vo.UserRechargeServiceVO;
import com.ruoyi.ss.transactionBill.domain.vo.UserWithdrawServiceVO;
import com.ruoyi.ss.user.domain.SmUserVo;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@ -293,4 +296,9 @@ public interface TransactionBillService
* 开启/关闭订单设备
*/
int switchDevice(TransactionBillVO bill, boolean open);
/**
* 获取商户的充值手续费
*/
UserRechargeServiceVO getMchRechargeService(ChannelVO channel, SmUserVo mch, DeviceVO device);
}

View File

@ -62,7 +62,7 @@ public class RechargeDepositAfterPay implements AfterPay {
query.setBillId(bill.getBillId());
query.setStatus(TransactionBillStatus.UNPAID_DEPOSIT.getStatus());
int update = transactionBillService.updateByQuery(data, query);
ServiceUtil.assertion(update != 1, "修改订单信息失败,状态已经发生改变");
ServiceUtil.assertion(update != 1, "修改订单信息失败,状态已经发生改变%s", bill.getBillNo());
try {
iotService.open(device.getMac(), device.getModelProductId());

View File

@ -272,7 +272,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
private void handleRechargeService(RechargePayBO bo) {
TransactionBillVO order = bo.getOrder();
// 获取商户的服务费配置
UserRechargeServiceVO userRechargeService = this.getUserRechargeService(bo.getChannel(), bo.getMch(), bo.getDevice());
UserRechargeServiceVO userRechargeService = this.getMchRechargeService(bo.getChannel(), bo.getMch(), bo.getDevice());
String serviceType = userRechargeService.getServiceType(); // 服务费类型
BigDecimal serviceRate = userRechargeService.getServiceRate(); // 服务费
@ -313,7 +313,8 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
}
private UserRechargeServiceVO getUserRechargeService(ChannelVO channel, SmUserVo mch, DeviceVO device) {
@Override
public UserRechargeServiceVO getMchRechargeService(ChannelVO channel, SmUserVo mch, DeviceVO device) {
// 优先级 设备 > 商户 > 渠道
if (device.getServiceRate() != null && StringUtils.hasText(device.getServiceType())) {
return new UserRechargeServiceVO(device.getServiceType(), device.getServiceRate());
@ -973,14 +974,14 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
data.setSuitEndEle(totalEle);
TransactionBillQuery query = new TransactionBillQuery();
query.setBillId(order.getBillId());
query.setStatus(TransactionBillStatus.SUCCESS.getStatus());
if (SuitFeeType.TIME.getType().equals(order.getSuitFeeType())) {
query.setStartSuitEndTime(endTime); // 计时的话需要结束时间在这之后的订单
} else if (SuitFeeType.COUNT.getType().equals(order.getSuitFeeType())) {
query.setStartSuitEndEle(totalEle); // 计量的话需要结束电量比这个大的订单
}
int update = this.updateByQuery(data, query);
ServiceUtil.assertion(update != 1, "修改订单信息失败");
ServiceUtil.assertion(update != 1, "修改订单信息失败,请重试:%s", order.getBillNo());
// 若金额 > 0.01 则申请退款
if (BigDecimal.valueOf(0.01).compareTo(refundAmount) < 0) {
@ -1069,13 +1070,14 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
data.setSuitEndEle(totalEle);
TransactionBillQuery query = new TransactionBillQuery();
query.setBillId(order.getBillId());
query.setStatus(TransactionBillStatus.SUCCESS.getStatus());
if (SuitFeeType.TIME.getType().equals(order.getSuitFeeType())) {
query.setStartSuitEndTime(endTime); // 计时的话需要结束时间在这之后的订单
} else if (SuitFeeType.COUNT.getType().equals(order.getSuitFeeType())) {
query.setStartSuitEndEle(totalEle); // 计量的话需要结束电量比这个大的订单
}
int update = this.updateByQuery(data, query);
ServiceUtil.assertion(update != 1, "修改订单信息失败");
ServiceUtil.assertion(update != 1, "修改订单信息失败,请重试:%s", order.getBillNo());
return update;
});
@ -1439,7 +1441,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
billQuery.setBillId(dto.getBillId());
billQuery.setStatus(TransactionBillStatus.SUCCESS.getStatus());
int updateBill = this.updateByQuery(data, billQuery);
ServiceUtil.assertion(updateBill != 1, "修改订单状态失败");
ServiceUtil.assertion(updateBill != 1, "退款时修改订单状态失败,订单状态已发生改变,请刷新后重试");
// 商户余额按照比例扣减
// 按比例计算退款金额
@ -1600,10 +1602,25 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
ServiceUtil.assertion(StringUtils.isBlank(device.getMac()), "设备MAC为空请联系管理员处理");
if (open) {
return iotService.open(device.getMac(), device.getModelProductId()) ? 1 : 0;
if (SuitFeeType.timingList().contains(bill.getSuitFeeType())) {
return iotService.open(device.getMac(), device.getModelProductId()) ? 1 : 0;
} else {
// 计算设备剩余时长
LocalDateTime expireTime = device.getExpireTime();
if (expireTime == null) {
return 0;
}
Duration between = Duration.between(LocalDateTime.now(), expireTime);
if (between.getSeconds() > 0) {
CommandResponse res = iotService.setTime(device.getMac(), between.getSeconds(), device.getModelProductId());
return res.isSuccess() ? 1 : 0;
}
}
} else {
return iotService.close(device.getMac(), device.getModelProductId()) ? 1 : 0;
}
return 0;
}
@Override

View File

@ -39,4 +39,8 @@ public class SmUserVo extends SmUser {
@ApiModelProperty("商户店铺数量")
@JsonView(JsonViewProfile.App.class)
private Integer storeCount;
@ApiModelProperty("实名或用户名")
private String realOrUserName;
}

View File

@ -45,6 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
su.real_id_card,
su.real_phone,
su.is_real,
if(su.is_real, su.real_name, su.user_name) as real_or_user_name,
(select sum(stb.money) from sm_transaction_bill stb where stb.user_id = su.user_id and stb.type = '1' and stb.status = '2') as recharge_amount,
(select sum(stb.arrival_amount) from sm_transaction_bill stb where stb.user_id = su.user_id and stb.type = '2' and stb.status = '14') as with_drawl_amount,
(select sum(stb.arrival_amount) from sm_transaction_bill stb where stb.mch_id = su.user_id and stb.type = '1' and stb.status = '2') as total_income

View File

@ -109,7 +109,7 @@ public interface ISmUserService
* @param bstType
* @param bstId
*/
void addBalance(Long userId, BigDecimal amount, String reason, RecordBalanceBstType bstType, Long bstId);
int addBalance(Long userId, BigDecimal amount, String reason, RecordBalanceBstType bstType, Long bstId);
/**
* 减少余额

View File

@ -128,7 +128,7 @@ public class SmUserServiceImpl implements ISmUserService
@Override
@Transactional
public void addBalance(Long userId, BigDecimal amount, String reason, RecordBalanceBstType bstType, Long bstId) {
public int addBalance(Long userId, BigDecimal amount, String reason, RecordBalanceBstType bstType, Long bstId) {
ServiceUtil.assertion(BigDecimal.ZERO.compareTo(amount) > 0, "增加的金额需要大于0");
// 查询用户
@ -141,6 +141,8 @@ public class SmUserServiceImpl implements ISmUserService
// 余额变动记录
int record = recordBalanceService.record(userId, user.getBalance(), amount, reason, bstType, bstId);
ServiceUtil.assertion(record != 1, "用户余额变动记录失败");
return updateCount;
}
@Override