Merge remote-tracking branch 'origin/deploy' into dev

This commit is contained in:
磷叶 2025-02-12 10:59:42 +08:00
commit fb7a37b4ee
14 changed files with 199 additions and 87 deletions

View File

@ -273,7 +273,7 @@ public class RedisCache
* @param timeUnit 时间单位
* @return 累计后的值
*/
public long incrementCacheValue(final String key, final long timeout, final TimeUnit timeUnit) {
public Long incrementCacheValue(final String key, final long timeout, final TimeUnit timeUnit) {
// 使用 SET 命令的 NX EX 选项确保初始值设置和过期时间设置是原子性的
Boolean isNewKey = redisTemplate.opsForValue().setIfAbsent(key, 0, timeout, timeUnit);
if (isNewKey != null && isNewKey) {
@ -281,7 +281,7 @@ public class RedisCache
return 1L;
}
// 使用 INCR 命令累计值
long newValue = redisTemplate.opsForValue().increment(key);
Long newValue = redisTemplate.opsForValue().increment(key);
// 刷新过期时间
redisTemplate.expire(key, timeout, timeUnit);
return newValue;

View File

@ -41,4 +41,9 @@ public class MathUtils {
}
return result;
}
// 把Integer转为Long类型
public static Long IntegerToLong(Integer num) {
return num == null ? null : Long.valueOf(num);
}
}

View File

@ -22,6 +22,9 @@ public class BusinessStatisticsQuery {
@ApiModelProperty("商户ID")
private Long mchId;
@ApiModelProperty("合伙人ID")
private Long staffId;
@ApiModelProperty("店铺ID")
private Long storeId;

View File

@ -29,6 +29,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join sm_device sd on sd.device_id = stb.device_id
<where>
<if test="mchId != null">and stb.mch_id = #{mchId}</if>
<if test="staffId != null">
and ss.store_id in (
select distinct sss.store_id
from ss_store_staff sss
where sss.user_id = #{staffId}
)
</if>
<!--店铺id=-1时查询未分配店铺的数据-->
<if test="storeId != null and storeId == -1">and stb.store_id is null</if>
<if test="storeId != null and storeId != -1">and stb.store_id = #{storeId}</if>

View File

@ -1,5 +1,18 @@
package com.ruoyi.iot.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.CacheConstants;
@ -10,33 +23,32 @@ import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.oneNet.Token;
import com.ruoyi.iot.domain.*;
import com.ruoyi.iot.domain.response.*;
import com.ruoyi.iot.domain.CreateDeviceVo;
import com.ruoyi.iot.domain.CurrentDeviceData;
import com.ruoyi.iot.domain.HistoryDeviceData;
import com.ruoyi.iot.domain.IotDeviceDetail;
import com.ruoyi.iot.domain.IotDeviceInfo;
import com.ruoyi.iot.domain.response.CommandResponse;
import com.ruoyi.iot.domain.response.CurrentDataPointResponse;
import com.ruoyi.iot.domain.response.DetailResponse;
import com.ruoyi.iot.domain.response.HistoryDataPointResponse;
import com.ruoyi.iot.domain.response.ListResponse;
import com.ruoyi.iot.enums.IotHttpStatus;
import com.ruoyi.iot.interfaces.IotDevice;
import com.ruoyi.iot.service.IotConverter;
import com.ruoyi.iot.service.IotService;
import com.ruoyi.ss.commandLog.service.ICommandLogService;
import com.ruoyi.ss.device.domain.enums.DeviceOnlineStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.ruoyi.ss.device.service.DeviceService;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
/**
* @author wjh
@ -75,6 +87,12 @@ public class IotServiceImpl implements IotService {
@Autowired
private RedisLock redisLock;
@Autowired
private DeviceService deviceService;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
// 查询OneNet设备在线状态
@Override
public String getOnlineStatus(String deviceName, String productId, String type) {
@ -88,35 +106,12 @@ public class IotServiceImpl implements IotService {
if (lock) {
// log.info("进入lock");
// 从缓存获取上次的在线状态若不存在则发命令防止短时间发送命令过于频繁
String cacheKey = CacheConstants.DEVICE_ONLINE_STATUS + deviceName;
String incrementCacheKey = CacheConstants.DEVICE_OFFLINE_INCREMENT + deviceName;
String cacheKey = this.getOnlineCacheKey(deviceName);
String status = redisCache.getCacheObject(cacheKey);
if (StringUtils.isBlank(status)) {
// log.info("进入command");
// 发送命令
CommandResponse res = uploadData(deviceName, productId, "获取在线状态");
// 若是离线则直接返回离线
if (res != null && res.isNotOnline()) {
status = DeviceOnlineStatus.OFFLINE.getStatus();
}
// 若是命令超时则累加redis数据超过1次则判断为离线
else if (res == null || res.isCmdTimeout()) {
long offlineCount = redisCache.incrementCacheValue(incrementCacheKey, 60, TimeUnit.SECONDS);
if (offlineCount >= 1) {
status = DeviceOnlineStatus.OFFLINE.getStatus();
} else {
status = DeviceOnlineStatus.ONLINE.getStatus();
}
}
// 若命令发送成功则清空redis累加数据
else if (res.isSuccess()) {
status = DeviceOnlineStatus.ONLINE.getStatus();
redisCache.deleteObject(incrementCacheKey);
}
// 缓存结果30秒
redisCache.setCacheObject(cacheKey, status, 30, TimeUnit.SECONDS);
status = this.parseToOnlineStatus(res, deviceName, false);
}
return status;
@ -137,6 +132,31 @@ public class IotServiceImpl implements IotService {
}
}
private String parseToOnlineStatus(CommandResponse res, String deviceName, boolean increment) {
String incrementCacheKey = CacheConstants.DEVICE_OFFLINE_INCREMENT + deviceName;
// 若命令发送成功则清空redis累加数据
if (res != null && res.isSuccess()) {
redisCache.deleteObject(incrementCacheKey);
return DeviceOnlineStatus.ONLINE.getStatus();
}
// 若是离线则直接返回离线
if (res != null && res.isNotOnline()) {
return DeviceOnlineStatus.OFFLINE.getStatus();
}
// 若是命令超时或者其他情况则累加redis数据超过2次则判断为离线
else {
Long offlineCount = MathUtils.IntegerToLong(redisCache.getCacheObject(incrementCacheKey));
if (increment) {
offlineCount = redisCache.incrementCacheValue(incrementCacheKey, 60, TimeUnit.SECONDS);
}
if (offlineCount != null && offlineCount >= 2) {
return DeviceOnlineStatus.OFFLINE.getStatus();
} else {
return DeviceOnlineStatus.ONLINE.getStatus();
}
}
}
@Override
public String getOnlineStatus(IotDevice device) {
String status = DeviceOnlineStatus.OFFLINE.getStatus();
@ -552,10 +572,17 @@ public class IotServiceImpl implements IotService {
// 记录成功日志
this.addCommandLog(deviceName, command, res.getMsg(), reason, res.getCode());
// 若返回数据为离线或者超时则判断设备是否在线
// if (IotHttpStatus.checkOnlineList().contains(res.getCode())) {
// deviceService.checkOnlineByCommandLogSync(deviceName);
// }
// 转为在线状态并缓存结果30秒
String status = this.parseToOnlineStatus(res, deviceName, true);
redisCache.setCacheObject(this.getOnlineCacheKey(deviceName), status, 30, TimeUnit.SECONDS);
// 异步更新设备在线状态
scheduledExecutorService.schedule(() -> {
int update = deviceService.updateOnlineStatusByMac(deviceName, status);
if (update != 1) {
log.error("异步更新设备在线状态失败,MAC={},status={}", deviceName, status);
}
},0, TimeUnit.SECONDS);
return res;
} catch (Exception e) {
this.addCommandLog(deviceName, command, "操作失败:" + e.getMessage(), reason, null);
@ -563,6 +590,10 @@ public class IotServiceImpl implements IotService {
}
}
private String getOnlineCacheKey(String deviceName) {
return CacheConstants.DEVICE_ONLINE_STATUS + deviceName;
}
// 异步添加日志
private void addCommandLog(String deviceName, String command, String result, String reason, Integer iotCode) {
LoginUser loginUser = null;

View File

@ -47,6 +47,7 @@ public class DeviceVO extends Device implements IotDevice {
private String picture;
@ApiModelProperty("型号标签列表")
@JsonView(JsonViewProfile.App.class)
private List<String> modelTags;
@ApiModelProperty("套餐列表")

View File

@ -227,4 +227,9 @@ public interface DeviceMapper
* 查询ID列表
*/
List<Long> selectIds(@Param("query") DeviceQuery query);
/**
* 更新在线状态
*/
int updateOnlineStatusByMac(@Param("mac") String mac, @Param("status") String status);
}

View File

@ -728,6 +728,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where device_id = #{deviceId}
</update>
<update id="updateOnlineStatusByMac">
update sm_device
set online_status1 = if (#{mac} = mac, #{status}, online_status1),
online_status2 = if (#{mac} = mac2, #{status}, online_status2),
online_status = if (#{mac} = mac,
if (#{status} = '1' or online_status2 = '1', '1', '0'),
if (#{status} = '1' or online_status1 = '1', '1', '0')
),
last_online_time = if (#{mac} = mac,
if (#{status} = '1' or online_status2 = '1', now(), last_online_time),
if (#{status} = '1' or online_status1 = '1', now(), last_online_time)
)
where mac = #{mac} or mac2 = #{mac}
</update>
<delete id="deleteSmDeviceByDeviceId" parameterType="Long">
delete from sm_device where device_id = #{deviceId}
</delete>

View File

@ -436,4 +436,9 @@ public interface DeviceService
* 查询商户设备列表
*/
List<DeviceVO> selectByMchId(Long userId);
/**
* 根据mac修改在线状态
*/
int updateOnlineStatusByMac(String mac, String status);
}

View File

@ -1451,6 +1451,14 @@ public class DeviceServiceImpl implements DeviceService
return selectSmDeviceList(query);
}
@Override
public int updateOnlineStatusByMac(String mac, String status) {
if (StringUtils.isAnyBlank(mac, status)) {
return 0;
}
return deviceMapper.updateOnlineStatusByMac(mac, status);
}
private List<Long> selectIds(DeviceQuery query) {
return deviceMapper.selectIds(query);
}

View File

@ -1,12 +1,13 @@
package com.ruoyi.ss.user.mapper;
import java.math.BigDecimal;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.common.core.domain.entity.SmUser;
import com.ruoyi.ss.user.domain.SmUserQuery;
import com.ruoyi.ss.user.domain.SmUserVO;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
/**
* 普通用户信息Mapper接口
@ -66,16 +67,6 @@ public interface SmUserMapper
int selectCount(SmUserQuery dto);
/**
* 增加余额
* @param userId 用户id
* @param amount 金额
* @param beforeBalance 变动前的余额并发
*/
int addBalance(@Param("userId") Long userId,
@Param("amount") BigDecimal amount,
@Param("beforeBalance") BigDecimal beforeBalance
);
SmUserVO selectSmUserByWxOpenId(String openId);
@ -106,12 +97,19 @@ public interface SmUserMapper
* @param userId 用户id
* @param amount 金额
* @param check 校验余额是否充足
* @param beforeBalance 变动前的余额并发
*/
int subtractBalance(@Param("userId") Long userId,
@Param("amount") BigDecimal amount,
@Param("check") boolean check,
@Param("beforeBalance") BigDecimal beforeBalance
@Param("check") boolean check
);
/**
* 增加余额
* @param userId 用户id
* @param amount 金额
*/
int addBalance(@Param("userId") Long userId,
@Param("amount") BigDecimal amount
);
/**
@ -146,4 +144,11 @@ public interface SmUserMapper
* 解除实名认证
*/
int resetRealName(Long userId);
/**
* 查询用户余额
*/
BigDecimal selectBalanceForUpdate(@Param("userId") Long userId);
}

View File

@ -290,12 +290,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where user_id = #{userId} and del_flag = '0'
</update>
<update id="addBalance">
update sm_user
set balance = balance + #{amount}
where user_id = #{userId} and del_flag = '0' and balance = #{beforeBalance}
</update>
<update id="updateSmUser" parameterType="SmUser">
update sm_user
<trim prefix="SET" suffixOverrides=",">
@ -367,13 +361,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="subtractBalance">
update sm_user
set balance = balance - #{amount}
<where>
user_id = #{userId} and del_flag = '0' and balance = #{beforeBalance}
<if test="check">
and balance >= #{amount}
</if>
where user_id = #{userId} and del_flag = '0'
<if test="check">
and balance >= #{amount}
</if>
</update>
</where>
<update id="addBalance">
update sm_user
set balance = balance + #{amount}
where user_id = #{userId} and del_flag = '0'
</update>
<update id="updateServiceRate">
@ -431,4 +428,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from sm_user su
where su.phonenumber = #{phonenumber} and su.del_flag != '2'
</select>
<!-- selectBalanceForUpdate -->
<select id="selectBalanceForUpdate">
select balance
from sm_user
where user_id = #{userId} and del_flag = '0'
for update
</select>
</mapper>

View File

@ -231,19 +231,19 @@ public class UserServiceImpl implements UserService
if (BigDecimal.ZERO.compareTo(amount) == 0) {
return 1;
}
// 查询用户
SmUserVO user = selectSmUserByUserId(userId);
// 查询用户余额
BigDecimal balance = smUserMapper.selectBalanceForUpdate(userId);
ServiceUtil.assertion(balance == null, "增加ID为%s的用户余额%s元失败请重试", userId, amount);
// 修改余额
int updateCount = smUserMapper.addBalance(userId, amount, user.getBalance());
ServiceUtil.assertion(updateCount != 1, "增加用户余额失败,请重试");
int update = smUserMapper.addBalance(userId, amount);
ServiceUtil.assertion(update != 1, "增加用户余额失败,请重试");
// 余额变动记录
int record = recordBalanceService.record(userId, user.getBalance(), amount, reason, bstType, bstId);
int record = recordBalanceService.record(userId, balance, amount, reason, bstType, bstId);
ServiceUtil.assertion(record != 1, "用户余额变动记录失败");
return updateCount;
return update;
}
@Override
@ -259,18 +259,19 @@ public class UserServiceImpl implements UserService
return 1;
}
// 查询用户
SmUserVO user = selectSmUserByUserId(userId);
// 查询用户余额
BigDecimal balance = smUserMapper.selectBalanceForUpdate(userId);
ServiceUtil.assertion(balance == null || balance.compareTo(amount) < 0, "减少ID为%s的用户余额%s元失败请重试", userId, amount);
// 更新用户余额
int updateCount = smUserMapper.subtractBalance(userId, amount, check, user.getBalance());
ServiceUtil.assertion(updateCount != 1, "减少ID为%s的用户余额%s元失败请重试", userId, amount);
int update = smUserMapper.subtractBalance(userId, amount, check);
ServiceUtil.assertion(update != 1, "减少ID为%s的用户余额%s元失败请重试", userId, amount);
// 余额变动记录
int record = recordBalanceService.record(userId, user.getBalance(), amount.negate(), reason, bstType, bstId);
int record = recordBalanceService.record(userId, balance, amount.negate(), reason, bstType, bstId);
ServiceUtil.assertion(record != 1, "记录ID为%s的用户余额变动记录%s元失败", userId, amount);
return updateCount;
return update;
}

View File

@ -1,8 +1,12 @@
package com.ruoyi.web.controller.staff;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.dashboard.domain.dto.BusinessStatisticsQuery;
import com.ruoyi.dashboard.service.DashboardService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -18,4 +22,19 @@ public class StaffDashboardController extends BaseController {
@Autowired
DashboardService dashboardService;
@ApiOperation("合伙人获取店铺维度的订单统计")
@GetMapping("/businessStatisticsByStore")
public AjaxResult businessStatisticsByStore(BusinessStatisticsQuery query) {
query.setStaffId(getUserId());
return success(dashboardService.businessStatisticsByStore(query));
}
@ApiOperation("合伙人获取指定店铺的订单统计")
@GetMapping("/businessStatisticsByDevice")
public AjaxResult businessStatisticsByDevice(BusinessStatisticsQuery query) {
query.setStaffId(getUserId());
return success(dashboardService.businessStatisticsByDevice(query));
}
}