fix: 3月3日后develop分支代码迁移至xc分支
This commit is contained in:
+4
@@ -22,6 +22,10 @@ public class WatchH5IndexItemNewVo {
|
|||||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
private Date dataDate;
|
private Date dataDate;
|
||||||
|
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date dataDateNew;
|
||||||
/**
|
/**
|
||||||
* 注意 当wdType=sleep时此字段返回长睡时长
|
* 注意 当wdType=sleep时此字段返回长睡时长
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.renkang.watch.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TianZi
|
||||||
|
* @date 2023/11/7 14:13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BindDeviceDTO {
|
||||||
|
|
||||||
|
@Schema(title = "手表编号")
|
||||||
|
private String watchNo;
|
||||||
|
}
|
||||||
+59
-2
@@ -1,22 +1,79 @@
|
|||||||
package com.renkang.watch.vo.UserData.res;
|
package com.renkang.watch.vo.UserData.res;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.renkang.watch.WatchH5IndexItemNewVo;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/** 用户统计数据
|
/**
|
||||||
|
* 用户统计数据
|
||||||
|
*
|
||||||
* @author stan
|
* @author stan
|
||||||
* @since 2024-11-04 16:33
|
* @since 2024-11-04 16:33
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class UserStat {
|
public class UserStat {
|
||||||
|
/**
|
||||||
|
* 数据类型:0=心率 1=血氧 2=压力 3=体温 4=睡眠
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
private BigDecimal latest;
|
private BigDecimal latest;
|
||||||
private BigDecimal max;
|
private BigDecimal max;
|
||||||
private BigDecimal min;
|
private BigDecimal min;
|
||||||
private BigDecimal avg;
|
private BigDecimal avg;
|
||||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date latestTime;
|
private Date latestTime;
|
||||||
|
|
||||||
|
public UserStat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserStat(WatchH5IndexItemNewVo watchH5IndexItemNewVo) {
|
||||||
|
switch (watchH5IndexItemNewVo.getWdType()) {
|
||||||
|
case "sleep":
|
||||||
|
this.type = "4";
|
||||||
|
break;
|
||||||
|
case "heart_rate":
|
||||||
|
this.type = "0";
|
||||||
|
break;
|
||||||
|
case "stress":
|
||||||
|
this.type = "2";
|
||||||
|
break;
|
||||||
|
case "spo2":
|
||||||
|
this.type = "1";
|
||||||
|
break;
|
||||||
|
case "temperature":
|
||||||
|
this.type = "3";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (watchH5IndexItemNewVo.getWdType().equals("sleep")) {
|
||||||
|
this.latest = parseSafe(watchH5IndexItemNewVo.getDataMax());// 总时长
|
||||||
|
this.max = parseSafe(watchH5IndexItemNewVo.getDataMax());
|
||||||
|
this.min = parseSafe(watchH5IndexItemNewVo.getDataMin());
|
||||||
|
this.avg = parseSafe(watchH5IndexItemNewVo.getDataAvg());
|
||||||
|
this.latestTime = watchH5IndexItemNewVo.getDataDate();
|
||||||
|
}else {
|
||||||
|
this.latest = parseSafe(watchH5IndexItemNewVo.getDataValue());
|
||||||
|
this.max = parseSafe(watchH5IndexItemNewVo.getDataMax());
|
||||||
|
this.min = parseSafe(watchH5IndexItemNewVo.getDataMin());
|
||||||
|
this.avg = parseSafe(watchH5IndexItemNewVo.getDataAvg());
|
||||||
|
this.latestTime = watchH5IndexItemNewVo.getDataDateNew();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全解析字符串为 BigDecimal,入参为 null 或空串时返回 null
|
||||||
|
*
|
||||||
|
* @param value 待解析的字符串数值
|
||||||
|
* @return 解析结果,无效输入返回 null
|
||||||
|
*/
|
||||||
|
private static BigDecimal parseSafe(String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return BigDecimal.valueOf(Double.parseDouble(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -41,6 +42,16 @@ public class WatchDataApiController {
|
|||||||
return Result.ok(watchDataApiService.userStatResult(type));
|
return Result.ok(watchDataApiService.userStatResult(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户手表数据(批量返回)
|
||||||
|
*
|
||||||
|
* @return data
|
||||||
|
*/
|
||||||
|
@GetMapping("userWatchDataAll")
|
||||||
|
public Result<List<UserStat>> userStatResultAll(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
|
||||||
|
return Result.ok(watchDataApiService.userStatResultAll(date));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户此刻是否拥有手表
|
* 用户此刻是否拥有手表
|
||||||
*
|
*
|
||||||
|
|||||||
+9
@@ -11,6 +11,7 @@ import com.renkang.watch.vo.UserData.res.sleep.WatchDataSleepRes;
|
|||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface WatchDataApiService {
|
public interface WatchDataApiService {
|
||||||
@@ -43,6 +44,14 @@ public interface WatchDataApiService {
|
|||||||
|
|
||||||
UserStat userStatResult(String type);
|
UserStat userStatResult(String type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前登录用户所有健康指标统计数据(按指定日期过滤)
|
||||||
|
*
|
||||||
|
* @param date 查询日期,为 null 时返回最新一条数据
|
||||||
|
* @return 各指标统计结果列表
|
||||||
|
*/
|
||||||
|
List<UserStat> userStatResultAll(Date date);
|
||||||
|
|
||||||
Boolean userHaveWatch(String userId);
|
Boolean userHaveWatch(String userId);
|
||||||
|
|
||||||
UserWatchInfo findWatchInfo(String userId);
|
UserWatchInfo findWatchInfo(String userId);
|
||||||
|
|||||||
+236
-32
@@ -7,6 +7,8 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||||
|
import com.renkang.watch.WatchH5IndexItemNewVo;
|
||||||
import com.renkang.watch.api.bean.response.UserWatchInfo;
|
import com.renkang.watch.api.bean.response.UserWatchInfo;
|
||||||
import com.renkang.watch.api.service.WatchDataApiService;
|
import com.renkang.watch.api.service.WatchDataApiService;
|
||||||
import com.renkang.watch.dto.WatchDataWorkoutDTO;
|
import com.renkang.watch.dto.WatchDataWorkoutDTO;
|
||||||
@@ -24,31 +26,34 @@ import com.renkang.watch.vo.UserData.res.bodyTemperature.BodyTempStat;
|
|||||||
import com.renkang.watch.vo.UserData.res.bodyTemperature.BodyTempStatRes;
|
import com.renkang.watch.vo.UserData.res.bodyTemperature.BodyTempStatRes;
|
||||||
import com.renkang.watch.vo.UserData.res.heartRate.WatchDataHeartRateRes;
|
import com.renkang.watch.vo.UserData.res.heartRate.WatchDataHeartRateRes;
|
||||||
import com.renkang.watch.vo.UserData.res.sleep.WatchDataSleepRes;
|
import com.renkang.watch.vo.UserData.res.sleep.WatchDataSleepRes;
|
||||||
|
import com.renkang.watch.vo.WatchH5IndexItemVo;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.common.system.vo.LoginUser;
|
import org.jeecg.common.system.vo.LoginUser;
|
||||||
import org.jeecg.common.util.DateUtils;
|
import org.jeecg.common.util.DateUtils;
|
||||||
|
import org.jeecg.common.util.RedisUtil;
|
||||||
import org.jeecg.global.GlobalUtils;
|
import org.jeecg.global.GlobalUtils;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class WatchDataApiServiceImpl implements WatchDataApiService {
|
public class WatchDataApiServiceImpl implements WatchDataApiService {
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WatchDataWorkoutMapper watchDataWorkoutMapper;
|
private WatchDataWorkoutMapper watchDataWorkoutMapper;
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private WatchStatUserInfoDayHeartRateMapper watchStatUserInfoDayHeartRateMapper;
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WatchDataHeartRateMapper watchDataHeartRateMapper;
|
private WatchDataHeartRateMapper watchDataHeartRateMapper;
|
||||||
|
|
||||||
@@ -60,9 +65,6 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private WatchStatUserInfoDaySpo2ServiceImpl watchStatUserInfoDaySpo2ServiceImpl;
|
private WatchStatUserInfoDaySpo2ServiceImpl watchStatUserInfoDaySpo2ServiceImpl;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private WatchStatUserInfoDaySleepMapper watchStatUserInfoDaySleepMapper;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WatchDataSleepNewMinuteMapper watchDataSleepNewMinuteMapper;
|
private WatchDataSleepNewMinuteMapper watchDataSleepNewMinuteMapper;
|
||||||
|
|
||||||
@@ -96,7 +98,6 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IWatchDataTemperatureService watchDataTemperatureService;
|
private IWatchDataTemperatureService watchDataTemperatureService;
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IWatchDataStressService watchDataStressService;
|
private IWatchDataStressService watchDataStressService;
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -106,12 +107,47 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private WatchUserDataMapper watchUserDataMapper;
|
private WatchUserDataMapper watchUserDataMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private WatchStatUserInfoDayHeartRateMapper watchStatUserInfoDayHeartRateMapper;
|
||||||
|
@Autowired
|
||||||
|
private WatchStatUserInfoDaySleepMapper watchStatUserInfoDaySleepMapper;
|
||||||
|
@Autowired
|
||||||
private WatchStatUserInfoDaySpo2Mapper watchStatUserInfoDaySpo2Mapper;
|
private WatchStatUserInfoDaySpo2Mapper watchStatUserInfoDaySpo2Mapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private WatchStatUserInfoDayStressMapper watchStatUserInfoDayStressMapper;
|
private WatchStatUserInfoDayStressMapper watchStatUserInfoDayStressMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private WatchStatUserInfoDayTempMapper watchStatUserInfoDayTempMapper;
|
private WatchStatUserInfoDayTempMapper watchStatUserInfoDayTempMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
|
/** 用户统计全量数据 Redis 缓存 key 前缀 */
|
||||||
|
private static final String USER_STAT_ALL_CACHE_KEY = "watch:userStatAll:";
|
||||||
|
|
||||||
|
private AsyncTaskExecutor asyncTaskExecutor;
|
||||||
|
|
||||||
|
public AsyncTaskExecutor getAsyncTaskExecutor() {
|
||||||
|
if (asyncTaskExecutor == null) {
|
||||||
|
initAsyncExecutor();
|
||||||
|
}
|
||||||
|
return asyncTaskExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
initAsyncExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAsyncExecutor() {
|
||||||
|
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||||
|
taskExecutor.setCorePoolSize(150);
|
||||||
|
taskExecutor.setMaxPoolSize(500);
|
||||||
|
taskExecutor.setQueueCapacity(500);
|
||||||
|
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
|
||||||
|
taskExecutor.setThreadNamePrefix("watch-latest-pool1-");
|
||||||
|
taskExecutor.initialize();
|
||||||
|
asyncTaskExecutor = taskExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Map<String,Object>> selectSportCostListApi() {
|
public Result<Map<String,Object>> selectSportCostListApi() {
|
||||||
LoginUser loginUser = GlobalUtils.getLoginUser();
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
@@ -834,20 +870,171 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
String userId = GlobalUtils.getLoginUser().getId();
|
String userId = GlobalUtils.getLoginUser().getId();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "0":
|
case "0":
|
||||||
return handleHeartRate(userId);
|
return handleHeartRate(userId,null);
|
||||||
case "1":
|
case "1":
|
||||||
return handleBloodOxygen(userId);
|
return handleBloodOxygen(userId,null);
|
||||||
case "2":
|
case "2":
|
||||||
return handleStress(userId);
|
return handleStress(userId,null);
|
||||||
case "3":
|
case "3":
|
||||||
return handleBodyTemperature(userId);
|
return handleBodyTemperature(userId,null);
|
||||||
case "4":
|
case "4":
|
||||||
return handleSleep(userId);
|
return handleSleep(userId,null);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserStat> userStatResultAll(Date date) {
|
||||||
|
// 在主线程提前获取 userId,避免子线程无法访问 ThreadLocal 中的登录信息
|
||||||
|
String userId = GlobalUtils.getLoginUser().getId();
|
||||||
|
// 有时间参数时缓存 key 携带日期,避免与无参缓存冲突
|
||||||
|
String cacheKey = USER_STAT_ALL_CACHE_KEY + userId + (date != null ? "_" + date.getTime() : "");
|
||||||
|
|
||||||
|
// 命中 Redis 缓存直接返回
|
||||||
|
Object cached = redisUtil.get(cacheKey);
|
||||||
|
if (cached != null) {
|
||||||
|
return (List<UserStat>) cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建「数据类型 → 异步查询 Future」映射,消除 switch-case 冗余
|
||||||
|
Map<String, CompletableFuture<WatchH5IndexItemNewVo>> futureMap = new HashMap<>();
|
||||||
|
futureMap.put("sleep", CompletableFuture.supplyAsync(() -> latestSleepData(userId), getAsyncTaskExecutor()));
|
||||||
|
futureMap.put("heart_rate", CompletableFuture.supplyAsync(() -> latestHeartRateData(userId), getAsyncTaskExecutor()));
|
||||||
|
futureMap.put("spo2", CompletableFuture.supplyAsync(() -> latestSpo2Data(userId), getAsyncTaskExecutor()));
|
||||||
|
futureMap.put("stress", CompletableFuture.supplyAsync(() -> latestStressData(userId), getAsyncTaskExecutor()));
|
||||||
|
futureMap.put("temperature", CompletableFuture.supplyAsync(() -> latestTempData(userId), getAsyncTaskExecutor()));
|
||||||
|
|
||||||
|
// 等待全部异步查询完成
|
||||||
|
CompletableFuture.allOf(futureMap.values().toArray(new CompletableFuture[0])).join();
|
||||||
|
|
||||||
|
// 转为顺序流遍历,避免并行流写入 ArrayList 的线程安全问题
|
||||||
|
List<WatchH5IndexItemNewVo> list = new ArrayList<>();
|
||||||
|
emptyItems().sequential().forEach(itemVo -> {
|
||||||
|
CompletableFuture<WatchH5IndexItemNewVo> future = futureMap.get(itemVo.getWdType());
|
||||||
|
// steps 等暂无对应查询的类型直接跳过
|
||||||
|
if (future == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WatchH5IndexItemNewVo data = future.join();
|
||||||
|
if (data == null) {
|
||||||
|
data = new WatchH5IndexItemNewVo();
|
||||||
|
}
|
||||||
|
// 将模板字段(wdType、wdTypeName、阈值)合并到结果对象
|
||||||
|
data.fieldCopy(itemVo);
|
||||||
|
// 睡眠数据特殊处理:dataValue 为空时补 "0"
|
||||||
|
if ("sleep".equals(itemVo.getWdType()) && StrUtil.isEmpty(data.getDataValue())) {
|
||||||
|
data.setDataValue("0");
|
||||||
|
}
|
||||||
|
list.add(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
List<UserStat> result = list.stream().map(UserStat::new).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 写入 Redis 缓存,TTL=3分钟(180秒)
|
||||||
|
redisUtil.set(cacheKey, result, 180);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<WatchH5IndexItemVo> emptyItems() {
|
||||||
|
//睡眠
|
||||||
|
WatchH5IndexItemVo sleepVo = new WatchH5IndexItemVo();
|
||||||
|
sleepVo.setWdType("sleep");
|
||||||
|
//步数
|
||||||
|
WatchH5IndexItemVo stepsVo = new WatchH5IndexItemVo();
|
||||||
|
stepsVo.setWdType("steps");
|
||||||
|
//心率
|
||||||
|
WatchH5IndexItemVo hearRateVo = new WatchH5IndexItemVo();
|
||||||
|
hearRateVo.setWdType("heart_rate");
|
||||||
|
//压力
|
||||||
|
WatchH5IndexItemVo stressVo = new WatchH5IndexItemVo();
|
||||||
|
stressVo.setWdType("stress");
|
||||||
|
//血氧饱和度
|
||||||
|
WatchH5IndexItemVo spo2Vo = new WatchH5IndexItemVo();
|
||||||
|
spo2Vo.setWdType("spo2");
|
||||||
|
//体温
|
||||||
|
WatchH5IndexItemVo tempVo = new WatchH5IndexItemVo();
|
||||||
|
tempVo.setWdType("temperature");
|
||||||
|
List<WatchH5IndexItemVo> list = Arrays.asList(sleepVo, stepsVo, hearRateVo, stressVo, spo2Vo, tempVo);
|
||||||
|
return list.parallelStream()
|
||||||
|
.peek(itemVo -> {
|
||||||
|
itemVo.setWarnMin("0.0");
|
||||||
|
itemVo.setWarnMax("0.0");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private WatchH5IndexItemNewVo latestSleepData(String userId) {
|
||||||
|
MPJLambdaWrapper<WatchStatUserInfoDaySleep> wrapper = new MPJLambdaWrapper<>();
|
||||||
|
//因为睡眠详情页取的是长睡时长作为当天的睡眠时间 , 所以这里保持一致并没有取当天的睡眠总时长
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySleep::getLongDurationTotal,WatchH5IndexItemNewVo::getDataValue);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySleep::getDeepSleepTimes,WatchH5IndexItemNewVo::getDataMax);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySleep::getLightSleepTimes,WatchH5IndexItemNewVo::getDataMin);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySleep::getSleepAvg,WatchH5IndexItemNewVo::getDataAvg);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySleep::getLastUploadTime,WatchH5IndexItemNewVo::getDataDateNew);
|
||||||
|
wrapper.eq(WatchStatUserInfoDaySleep::getUserId, userId);
|
||||||
|
wrapper.orderByDesc(WatchStatUserInfoDaySleep::getDataDate);
|
||||||
|
wrapper.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
|
return watchStatUserInfoDaySleepMapper.selectJoinOne(WatchH5IndexItemNewVo.class,wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WatchH5IndexItemNewVo latestHeartRateData(String userId) {
|
||||||
|
MPJLambdaWrapper<WatchStatUserInfoDayHeartRate> wrapper = new MPJLambdaWrapper<>();
|
||||||
|
// 必须是watch_stat_user_info_day_heart_rate表中new_data字段启用后才能这样查
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayHeartRate::getNewValue,WatchH5IndexItemNewVo::getDataValue);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayHeartRate::getMaxValue,WatchH5IndexItemNewVo::getDataMax);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayHeartRate::getMinValue,WatchH5IndexItemNewVo::getDataMin);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayHeartRate::getAvgValue,WatchH5IndexItemNewVo::getDataAvg);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayHeartRate::getLastUploadTime,WatchH5IndexItemNewVo::getDataDateNew);
|
||||||
|
wrapper.eq(WatchStatUserInfoDayHeartRate::getUserId, userId);
|
||||||
|
wrapper.orderByDesc(WatchStatUserInfoDayHeartRate::getDataDate);
|
||||||
|
wrapper.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
|
return watchStatUserInfoDayHeartRateMapper.selectJoinOne(WatchH5IndexItemNewVo.class,wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WatchH5IndexItemNewVo latestSpo2Data(String userId) {
|
||||||
|
MPJLambdaWrapper<WatchStatUserInfoDaySpo2> wrapper = new MPJLambdaWrapper<>();
|
||||||
|
// 必须是watch_stat_user_info_day_spo2表中new_data字段启用后才能这样查
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySpo2::getNewValue,WatchH5IndexItemNewVo::getDataValue);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySpo2::getMaxValue,WatchH5IndexItemNewVo::getDataMax);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySpo2::getMinValue,WatchH5IndexItemNewVo::getDataMin);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySpo2::getAvgValue,WatchH5IndexItemNewVo::getDataAvg);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDaySpo2::getLastUploadTime,WatchH5IndexItemNewVo::getDataDateNew);
|
||||||
|
wrapper.eq(WatchStatUserInfoDaySpo2::getUserId, userId);
|
||||||
|
wrapper.orderByDesc(WatchStatUserInfoDaySpo2::getDataDate);
|
||||||
|
wrapper.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
|
return watchStatUserInfoDaySpo2Mapper.selectJoinOne(WatchH5IndexItemNewVo.class,wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WatchH5IndexItemNewVo latestStressData(String userId) {
|
||||||
|
MPJLambdaWrapper<WatchStatUserInfoDayStress> wrapper = new MPJLambdaWrapper<>();
|
||||||
|
// 必须是watch_stat_user_info_day_stress表中new_data字段启用后才能这样查
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayStress::getNewValue,WatchH5IndexItemNewVo::getDataValue);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayStress::getMaxValue,WatchH5IndexItemNewVo::getDataMax);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayStress::getMinValue,WatchH5IndexItemNewVo::getDataMin);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayStress::getAvgValue,WatchH5IndexItemNewVo::getDataAvg);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayStress::getLastUploadTime,WatchH5IndexItemNewVo::getDataDateNew);
|
||||||
|
wrapper.eq(WatchStatUserInfoDayStress::getUserId, userId);
|
||||||
|
wrapper.orderByDesc(WatchStatUserInfoDayStress::getDataDate);
|
||||||
|
wrapper.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
|
return watchStatUserInfoDayStressMapper.selectJoinOne(WatchH5IndexItemNewVo.class,wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WatchH5IndexItemNewVo latestTempData(String userId) {
|
||||||
|
MPJLambdaWrapper<WatchStatUserInfoDayTemp> wrapper = new MPJLambdaWrapper<>();
|
||||||
|
// 必须是watch_stat_user_info_day_temp表中new_data字段启用后才能这样查
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayTemp::getNewValue,WatchH5IndexItemNewVo::getDataValue);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayTemp::getMaxValue,WatchH5IndexItemNewVo::getDataMax);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayTemp::getMinValue,WatchH5IndexItemNewVo::getDataMin);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayTemp::getAvgValue,WatchH5IndexItemNewVo::getDataAvg);
|
||||||
|
wrapper.selectAs(WatchStatUserInfoDayTemp::getLastUploadTime,WatchH5IndexItemNewVo::getDataDateNew);
|
||||||
|
wrapper.eq(WatchStatUserInfoDayTemp::getUserId, userId);
|
||||||
|
wrapper.orderByDesc(WatchStatUserInfoDayTemp::getDataDate);
|
||||||
|
wrapper.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
|
return watchStatUserInfoDayTempMapper.selectJoinOne(WatchH5IndexItemNewVo.class,wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean userHaveWatch(String userId) {
|
public Boolean userHaveWatch(String userId) {
|
||||||
if (StrUtil.isBlank(userId)) {
|
if (StrUtil.isBlank(userId)) {
|
||||||
@@ -885,7 +1072,7 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private UserStat handleSleep(String userId) {
|
private UserStat handleSleep(String userId, Date date) {
|
||||||
LambdaQueryWrapper<WatchStatUserInfoDaySleep> queryWrapper = Wrappers.lambdaQuery(WatchStatUserInfoDaySleep.class)
|
LambdaQueryWrapper<WatchStatUserInfoDaySleep> queryWrapper = Wrappers.lambdaQuery(WatchStatUserInfoDaySleep.class)
|
||||||
.select(WatchStatUserInfoDaySleep::getId,
|
.select(WatchStatUserInfoDaySleep::getId,
|
||||||
WatchStatUserInfoDaySleep::getDeepSleepTimes,
|
WatchStatUserInfoDaySleep::getDeepSleepTimes,
|
||||||
@@ -893,13 +1080,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
WatchStatUserInfoDaySleep::getDeepLightTimes,
|
WatchStatUserInfoDaySleep::getDeepLightTimes,
|
||||||
WatchStatUserInfoDaySleep::getDataDate)
|
WatchStatUserInfoDaySleep::getDataDate)
|
||||||
.eq(WatchStatUserInfoDaySleep::getUserId, userId)
|
.eq(WatchStatUserInfoDaySleep::getUserId, userId)
|
||||||
.orderByDesc(WatchStatUserInfoDaySleep::getDataDate)
|
// 传了日期则精确匹配,否则取最新一条
|
||||||
|
.eq(date != null, WatchStatUserInfoDaySleep::getDataDate, date)
|
||||||
|
.orderByDesc(date == null, WatchStatUserInfoDaySleep::getDataDate)
|
||||||
.last("FETCH FIRST 1 ROWS ONLY");
|
.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
|
|
||||||
WatchStatUserInfoDaySleep data = watchStatUserInfoDaySleepMapper.selectOne(queryWrapper);
|
WatchStatUserInfoDaySleep data = watchStatUserInfoDaySleepMapper.selectOne(queryWrapper);
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return null;
|
return new UserStat();
|
||||||
}
|
}
|
||||||
|
|
||||||
UserStat stat = new UserStat();
|
UserStat stat = new UserStat();
|
||||||
@@ -915,15 +1104,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserStat handleBodyTemperature(String userId) {
|
private UserStat handleBodyTemperature(String userId, Date date) {
|
||||||
int type = 5;
|
int type = 5;
|
||||||
int scale = 1;
|
int scale = 1;
|
||||||
// 最新数据
|
// 最新数据
|
||||||
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
||||||
if (userStat == null) {
|
if (userStat == null) {
|
||||||
return null;
|
return new UserStat();
|
||||||
}
|
}
|
||||||
LambdaQueryWrapper<WatchStatUserInfoDayTemp> queryWrapper = getQueryWrapper(userId);
|
LambdaQueryWrapper<WatchStatUserInfoDayTemp> queryWrapper = getQueryWrapper(userId, date);
|
||||||
WatchStatUserInfoDayTemp data = watchStatUserInfoDayTempMapper.selectOne(queryWrapper);
|
WatchStatUserInfoDayTemp data = watchStatUserInfoDayTempMapper.selectOne(queryWrapper);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return userStat;
|
return userStat;
|
||||||
@@ -933,15 +1122,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
return userStat;
|
return userStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserStat handleStress(String userId) {
|
private UserStat handleStress(String userId, Date date) {
|
||||||
int type = 3;
|
int type = 3;
|
||||||
int scale = 0;
|
int scale = 0;
|
||||||
// 最新数据
|
// 最新数据
|
||||||
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
||||||
if (userStat == null) {
|
if (userStat == null) {
|
||||||
return null;
|
return new UserStat();
|
||||||
}
|
}
|
||||||
LambdaQueryWrapper<WatchStatUserInfoDayStress> queryWrapper = getQueryWrapper(userId);
|
LambdaQueryWrapper<WatchStatUserInfoDayStress> queryWrapper = getQueryWrapper(userId, date);
|
||||||
WatchStatUserInfoDayStress data = watchStatUserInfoDayStressMapper.selectOne(queryWrapper);
|
WatchStatUserInfoDayStress data = watchStatUserInfoDayStressMapper.selectOne(queryWrapper);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return userStat;
|
return userStat;
|
||||||
@@ -951,15 +1140,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
return userStat;
|
return userStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserStat handleBloodOxygen(String userId) {
|
private UserStat handleBloodOxygen(String userId, Date date) {
|
||||||
int type = 2;
|
int type = 2;
|
||||||
int scale = 0;
|
int scale = 0;
|
||||||
// 最新数据
|
// 最新数据
|
||||||
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
||||||
if (userStat == null) {
|
if (userStat == null) {
|
||||||
return null;
|
return new UserStat();
|
||||||
}
|
}
|
||||||
LambdaQueryWrapper<WatchStatUserInfoDaySpo2> queryWrapper = getQueryWrapper(userId);
|
LambdaQueryWrapper<WatchStatUserInfoDaySpo2> queryWrapper = getQueryWrapper(userId, date);
|
||||||
WatchStatUserInfoDaySpo2 data = watchStatUserInfoDaySpo2Mapper.selectOne(queryWrapper);
|
WatchStatUserInfoDaySpo2 data = watchStatUserInfoDaySpo2Mapper.selectOne(queryWrapper);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return userStat;
|
return userStat;
|
||||||
@@ -969,15 +1158,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
return userStat;
|
return userStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserStat handleHeartRate(String userId) {
|
private UserStat handleHeartRate(String userId, Date date) {
|
||||||
int type = 1;
|
int type = 1;
|
||||||
int scale = 0;
|
int scale = 0;
|
||||||
// 最新数据
|
// 最新数据
|
||||||
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
UserStat userStat = userWatchDataLatest(userId, type, scale);
|
||||||
if (userStat == null) {
|
if (userStat == null) {
|
||||||
return null;
|
return new UserStat();
|
||||||
}
|
}
|
||||||
LambdaQueryWrapper<WatchStatUserInfoDayHeartRate> queryWrapper = getQueryWrapper(userId);
|
LambdaQueryWrapper<WatchStatUserInfoDayHeartRate> queryWrapper = getQueryWrapper(userId, date);
|
||||||
WatchStatUserInfoDayHeartRate data = watchStatUserInfoDayHeartRateMapper.selectOne(queryWrapper);
|
WatchStatUserInfoDayHeartRate data = watchStatUserInfoDayHeartRateMapper.selectOne(queryWrapper);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return userStat;
|
return userStat;
|
||||||
@@ -1029,10 +1218,12 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> LambdaQueryWrapper<T> getQueryWrapper(String userId) {
|
private <T> LambdaQueryWrapper<T> getQueryWrapper(String userId, Date date) {
|
||||||
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.eq("user_id", userId)
|
queryWrapper.eq("user_id", userId)
|
||||||
.orderByDesc("data_date")
|
// 传了日期则精确匹配,否则取最新一条
|
||||||
|
.eq(date != null, "data_date", date)
|
||||||
|
.orderByDesc(date == null, "data_date")
|
||||||
.last("FETCH FIRST 1 ROWS ONLY");
|
.last("FETCH FIRST 1 ROWS ONLY");
|
||||||
return queryWrapper.lambda();
|
return queryWrapper.lambda();
|
||||||
}
|
}
|
||||||
@@ -1051,4 +1242,17 @@ public class WatchDataApiServiceImpl implements WatchDataApiService {
|
|||||||
return BigDecimal.valueOf(value).setScale(0, RoundingMode.HALF_UP);
|
return BigDecimal.valueOf(value).setScale(0, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置统计数据类型标识,stat 为 null 时直接返回 null
|
||||||
|
*
|
||||||
|
* @param stat UserStat 对象
|
||||||
|
* @param type 类型编码:0=心率 1=血氧 2=压力 3=体温 4=睡眠
|
||||||
|
*/
|
||||||
|
private UserStat setType(UserStat stat, String type) {
|
||||||
|
if (stat != null) {
|
||||||
|
stat.setType(type);
|
||||||
|
}
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -11,10 +11,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -327,4 +324,5 @@ public class WatchController {
|
|||||||
// }
|
// }
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-1
@@ -185,6 +185,12 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
|
|||||||
return watchDeviceService.bindUser(dto);
|
return watchDeviceService.bindUser(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "移动端绑定设备(手表)", description = "移动端绑定设备(手表)")
|
||||||
|
@GetMapping("/app/bindDevice")
|
||||||
|
public Result bindDevice(@RequestParam(name = "watchNo") String watchNo) throws ParseException {
|
||||||
|
return watchDeviceService.bindDevice(watchNo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解绑用户
|
* 解绑用户
|
||||||
*
|
*
|
||||||
@@ -194,10 +200,17 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
|
|||||||
@Operation(summary = "解绑用户", description = "解绑用户")
|
@Operation(summary = "解绑用户", description = "解绑用户")
|
||||||
@PostMapping(value = "/unbundleUser")
|
@PostMapping(value = "/unbundleUser")
|
||||||
@RequiresPermissions("watch:watch_device:unbindUser")
|
@RequiresPermissions("watch:watch_device:unbindUser")
|
||||||
public Result unbundleUser(@RequestBody() BindUserDTO dto) throws ParseException {
|
public Result unbundleUser(@RequestBody() BindUserDTO dto) {
|
||||||
return watchDeviceService.unbundleUser(dto);
|
return watchDeviceService.unbundleUser(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "移动端解绑设备(手表)", description = "移动端解绑设备(手表)")
|
||||||
|
@GetMapping("/app/unbundleDevice")
|
||||||
|
public Result unbundleDevice(@RequestParam(name = "watchNo") String watchNo) {
|
||||||
|
return watchDeviceService.unbundleDevice(watchNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置上传频次
|
* 设置上传频次
|
||||||
*
|
*
|
||||||
|
|||||||
+5
-1
@@ -43,12 +43,16 @@ public interface IWatchDeviceService extends IService<WatchDevice> {
|
|||||||
*/
|
*/
|
||||||
Result bindUser(BindUserDTO dto) throws ParseException;
|
Result bindUser(BindUserDTO dto) throws ParseException;
|
||||||
|
|
||||||
|
Result bindDevice(String watchNo) throws ParseException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定用户
|
* 绑定用户
|
||||||
* @param dto
|
* @param dto
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Result unbundleUser(BindUserDTO dto) throws ParseException;
|
Result unbundleUser(BindUserDTO dto);
|
||||||
|
|
||||||
|
Result unbundleDevice(String watchNo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具开关
|
* 工具开关
|
||||||
|
|||||||
+22
-2
@@ -291,7 +291,7 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
|||||||
}
|
}
|
||||||
WatchDevice watchDevice = getDeviceByWatchNo(dto.getWatchNo());
|
WatchDevice watchDevice = getDeviceByWatchNo(dto.getWatchNo());
|
||||||
if (watchDevice == null) {
|
if (watchDevice == null) {
|
||||||
return Result.error("该工具不存在");
|
return Result.error("该监测工具不存在");
|
||||||
}
|
}
|
||||||
if (isDeviceAlreadyBound(watchDevice, dto)) {
|
if (isDeviceAlreadyBound(watchDevice, dto)) {
|
||||||
return Result.error("该设备已绑定该员工");
|
return Result.error("该设备已绑定该员工");
|
||||||
@@ -350,6 +350,17 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
|||||||
return Result.ok("操作成功");
|
return Result.ok("操作成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Result bindDevice(String watchNo) throws ParseException {
|
||||||
|
BindUserDTO dto = new BindUserDTO();
|
||||||
|
dto.setBindUserId(GlobalUtils.getLoginUser().getId());
|
||||||
|
dto.setWatchNo(watchNo);
|
||||||
|
dto.setBindDate(new Date());
|
||||||
|
dto.setChangeUser(false);
|
||||||
|
return bindUser(dto);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改用户拉取数据
|
* 修改用户拉取数据
|
||||||
*
|
*
|
||||||
@@ -385,7 +396,7 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Result unbundleUser(BindUserDTO dto) throws ParseException {
|
public Result unbundleUser(BindUserDTO dto){
|
||||||
WatchDevice watchDevice = getDeviceByWatchNo(dto.getWatchNo());
|
WatchDevice watchDevice = getDeviceByWatchNo(dto.getWatchNo());
|
||||||
if (watchDevice == null) {
|
if (watchDevice == null) {
|
||||||
return Result.error("该设备不存在");
|
return Result.error("该设备不存在");
|
||||||
@@ -409,6 +420,15 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
|||||||
return Result.ok("操作成功");
|
return Result.ok("操作成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Result unbundleDevice(String watchNo){
|
||||||
|
BindUserDTO dto = new BindUserDTO();
|
||||||
|
dto.setBindUserId(GlobalUtils.getLoginUser().getId());
|
||||||
|
dto.setWatchNo(watchNo);
|
||||||
|
dto.setBindDate(new Date());
|
||||||
|
return unbundleUser(dto);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据手表编号获取手表信息
|
* 根据手表编号获取手表信息
|
||||||
|
|||||||
@@ -721,4 +721,9 @@ public interface CommonConstant {
|
|||||||
* 多选
|
* 多选
|
||||||
*/
|
*/
|
||||||
String PD = "PD";
|
String PD = "PD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url加解密密钥(AES)
|
||||||
|
*/
|
||||||
|
String URL_SECRET_KEY = "a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package org.jeecg.util;
|
||||||
|
|
||||||
|
import org.jeecg.common.constant.CommonConstant;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES/CBC/PKCS5Padding 对称加密工具类
|
||||||
|
* 加密结果经 Base64 URL Safe(无填充)编码,可安全用于 URL 传参
|
||||||
|
*/
|
||||||
|
public class AesEncryptUtils {
|
||||||
|
|
||||||
|
private static final String ALGORITHM = "AES";
|
||||||
|
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
|
||||||
|
/**
|
||||||
|
* CBC 模式 IV 固定 16 字节
|
||||||
|
*/
|
||||||
|
private static final int IV_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
* 随机生成 IV,将 [IV(16字节) + 密文] 拼接后进行 Base64 URL Safe(无填充)编码
|
||||||
|
*
|
||||||
|
* @param plainText 明文字符串
|
||||||
|
* @param secretKey 密钥(任意长度字符串,内部经 SHA-256 截取为 128 位)
|
||||||
|
* @return URL 安全的密文字符串,不含 +、/、= 等特殊字符
|
||||||
|
* @throws Exception 加密异常
|
||||||
|
*/
|
||||||
|
public static String encrypt(String plainText, String secretKey) throws Exception {
|
||||||
|
byte[] keyBytes = deriveKey(secretKey);
|
||||||
|
byte[] ivBytes = generateIv();
|
||||||
|
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE,
|
||||||
|
new SecretKeySpec(keyBytes, ALGORITHM),
|
||||||
|
new IvParameterSpec(ivBytes));
|
||||||
|
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
||||||
|
// 将 IV 前缀拼接到密文,解密时从头部分离
|
||||||
|
byte[] combined = new byte[IV_LENGTH + encrypted.length];
|
||||||
|
System.arraycopy(ivBytes, 0, combined, 0, IV_LENGTH);
|
||||||
|
System.arraycopy(encrypted, 0, combined, IV_LENGTH, encrypted.length);
|
||||||
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
* 从密文头部分离 IV,再执行 AES/CBC 解密
|
||||||
|
*
|
||||||
|
* @param cipherText URL 安全的密文字符串(由 encrypt 方法生成)
|
||||||
|
* @param secretKey 密钥(须与加密时保持一致)
|
||||||
|
* @return 解密后的明文字符串
|
||||||
|
* @throws Exception 解密异常(密钥错误或数据被篡改时抛出)
|
||||||
|
*/
|
||||||
|
public static String decrypt(String cipherText, String secretKey) throws Exception {
|
||||||
|
byte[] combined = Base64.getUrlDecoder().decode(cipherText);
|
||||||
|
byte[] ivBytes = Arrays.copyOfRange(combined, 0, IV_LENGTH);
|
||||||
|
byte[] encrypted = Arrays.copyOfRange(combined, IV_LENGTH, combined.length);
|
||||||
|
byte[] keyBytes = deriveKey(secretKey);
|
||||||
|
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE,
|
||||||
|
new SecretKeySpec(keyBytes, ALGORITHM),
|
||||||
|
new IvParameterSpec(ivBytes));
|
||||||
|
return new String(cipher.doFinal(encrypted), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 SHA-256 对密钥字符串进行哈希,取前 16 字节作为 AES 128 位密钥
|
||||||
|
* 支持任意长度的密钥输入,避免 AES 对密钥长度的严格限制
|
||||||
|
*
|
||||||
|
* @param secretKey 原始密钥字符串
|
||||||
|
* @return 16 字节的 AES 密钥
|
||||||
|
*/
|
||||||
|
private static byte[] deriveKey(String secretKey) throws Exception {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] hash = digest.digest(secretKey.getBytes(StandardCharsets.UTF_8));
|
||||||
|
// 取前 16 字节 = 128 位 AES 密钥
|
||||||
|
return Arrays.copyOf(hash, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 SecureRandom 随机生成 16 字节 IV
|
||||||
|
* 每次加密使用不同 IV,防止相同明文产生相同密文
|
||||||
|
*
|
||||||
|
* @return 16 字节随机 IV
|
||||||
|
*/
|
||||||
|
private static byte[] generateIv() {
|
||||||
|
byte[] iv = new byte[IV_LENGTH];
|
||||||
|
new SecureRandom().nextBytes(iv);
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
String plainText = "https://hello world";
|
||||||
|
String secretKey = CommonConstant.URL_SECRET_KEY;
|
||||||
|
// String cipherText = AesEncryptUtils.encrypt(plainText, secretKey);
|
||||||
|
// System.out.println(cipherText);
|
||||||
|
String decryptedText = AesEncryptUtils.decrypt("6oFxHVMYNHXpz8VtsREcNlokVqOdvaZNSGBJ5U3_7mY", secretKey);
|
||||||
|
System.out.println(decryptedText);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -82,18 +82,36 @@ public class BaseEmployeeInfo {
|
|||||||
@Schema(title = "二级部门Code(单位)")
|
@Schema(title = "二级部门Code(单位)")
|
||||||
private String secondDeptCode;
|
private String secondDeptCode;
|
||||||
|
|
||||||
|
@Schema(title = "二级部门id(单位)")
|
||||||
|
private String secondDeptId;
|
||||||
|
|
||||||
@Schema(title = "二级部门名称(单位)")
|
@Schema(title = "二级部门名称(单位)")
|
||||||
private String secondDeptName;
|
private String secondDeptName;
|
||||||
|
|
||||||
@Schema(title = "三级部门Code(部门)")
|
@Schema(title = "三级部门Code(部门)")
|
||||||
private String thirdDeptCode;
|
private String thirdDeptCode;
|
||||||
|
|
||||||
|
@Schema(title = "三级部门id(部门)")
|
||||||
|
private String thirdDeptId;
|
||||||
|
|
||||||
@Schema(title = "三级部门名称(部门)")
|
@Schema(title = "三级部门名称(部门)")
|
||||||
private String thirdDeptName;
|
private String thirdDeptName;
|
||||||
|
|
||||||
@Schema(title = "当前部门Code(用户所属部门)")
|
@Schema(title = "当前部门Code(用户所属部门)")
|
||||||
private String thisDeptCode;
|
private String thisDeptCode;
|
||||||
|
|
||||||
|
@Schema(title = "当前部门id(用户所属部门)")
|
||||||
|
private String thisDeptId;
|
||||||
|
|
||||||
@Schema(title = "当前部门名称(用户所属部门)")
|
@Schema(title = "当前部门名称(用户所属部门)")
|
||||||
private String thisDeptName;
|
private String thisDeptName;
|
||||||
|
|
||||||
|
@Schema(title = "角色编码")
|
||||||
|
private String roleCodes;
|
||||||
|
|
||||||
|
@Schema(title = "管理部门(code)")
|
||||||
|
private String departCodes;
|
||||||
|
|
||||||
|
@Schema(title = "管理部门(id)")
|
||||||
|
private String departIds;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -3,16 +3,20 @@ package org.jeecg.modules.api.controller;
|
|||||||
import org.jeecg.base.UserBase;
|
import org.jeecg.base.UserBase;
|
||||||
import org.jeecg.bean.response.BaseEmployeeInfo;
|
import org.jeecg.bean.response.BaseEmployeeInfo;
|
||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.constant.CommonConstant;
|
||||||
import org.jeecg.modules.system.bean.response.ScalePersonalUserInfo;
|
import org.jeecg.modules.system.bean.response.ScalePersonalUserInfo;
|
||||||
import org.jeecg.modules.system.service.ISysUserService;
|
import org.jeecg.modules.system.service.ISysUserService;
|
||||||
import org.jeecg.modules.system.service.ScalePersonalService;
|
import org.jeecg.modules.system.service.ScalePersonalService;
|
||||||
|
import org.jeecg.util.AesEncryptUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author stan
|
* @author stan
|
||||||
@@ -67,5 +71,14 @@ public class SysApiController {
|
|||||||
return Result.ok(sysUserService.getUserInfo());
|
return Result.ok(sysUserService.getUserInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串加密
|
||||||
|
*/
|
||||||
|
@GetMapping("encryptInfo")
|
||||||
|
public Result<Map<String, String>> encryptInfo(@RequestParam String str) throws Exception {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("encryptData", AesEncryptUtils.encrypt(str, CommonConstant.URL_SECRET_KEY));
|
||||||
|
return Result.ok(map);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -3906,6 +3906,16 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
if (null == loginUser) {
|
if (null == loginUser) {
|
||||||
ExceptionAssertsUtil.fail("用户不存在!");
|
ExceptionAssertsUtil.fail("用户不存在!");
|
||||||
}
|
}
|
||||||
|
List<String> managedCodes = GlobalUtils.getManagedCodes(null);
|
||||||
|
Set<String> departIds = new HashSet<>();
|
||||||
|
if (CollUtil.isNotEmpty(managedCodes)) {
|
||||||
|
managedCodes.forEach(s -> {
|
||||||
|
String departIdByOrgCode = sysCache.getDepartIdByOrgCode(s);
|
||||||
|
if (StringUtils.isNotBlank(departIdByOrgCode) && !"null".equals(departIdByOrgCode)) {
|
||||||
|
departIds.add(departIdByOrgCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
MPJLambdaWrapper<SysUser> queryWrapper = new MPJLambdaWrapper<SysUser>()
|
MPJLambdaWrapper<SysUser> queryWrapper = new MPJLambdaWrapper<SysUser>()
|
||||||
.selectAs(SysUser::getId, BaseEmployeeInfo::getUserId)
|
.selectAs(SysUser::getId, BaseEmployeeInfo::getUserId)
|
||||||
.selectAs(SysUser::getUsername, BaseEmployeeInfo::getUserName)
|
.selectAs(SysUser::getUsername, BaseEmployeeInfo::getUserName)
|
||||||
@@ -3931,13 +3941,19 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
baseEmployeeInfo.setAge(ObjectUtils.isNotEmpty(loginUser.getAge()) && loginUser.getAge() > 0 ? loginUser.getAge() : null);
|
baseEmployeeInfo.setAge(ObjectUtils.isNotEmpty(loginUser.getAge()) && loginUser.getAge() > 0 ? loginUser.getAge() : null);
|
||||||
SysDepart secondDepart = sysCache.getDepartByOrgCode(GlobalUtils.getSecondDepartOrgCode(loginUser.getOrgCode()));
|
SysDepart secondDepart = sysCache.getDepartByOrgCode(GlobalUtils.getSecondDepartOrgCode(loginUser.getOrgCode()));
|
||||||
baseEmployeeInfo.setSecondDeptCode(secondDepart.getOrgCode());
|
baseEmployeeInfo.setSecondDeptCode(secondDepart.getOrgCode());
|
||||||
|
baseEmployeeInfo.setSecondDeptId(secondDepart.getId());
|
||||||
baseEmployeeInfo.setSecondDeptName(secondDepart.getDepartName());
|
baseEmployeeInfo.setSecondDeptName(secondDepart.getDepartName());
|
||||||
SysDepart thirdDepart = sysCache.getDepartByOrgCode(GlobalUtils.getThirdDepartOrgCode(loginUser.getOrgCode()));
|
SysDepart thirdDepart = sysCache.getDepartByOrgCode(GlobalUtils.getThirdDepartOrgCode(loginUser.getOrgCode()));
|
||||||
baseEmployeeInfo.setThirdDeptCode(thirdDepart.getOrgCode());
|
baseEmployeeInfo.setThirdDeptCode(thirdDepart.getOrgCode());
|
||||||
|
baseEmployeeInfo.setThirdDeptId(thirdDepart.getId());
|
||||||
baseEmployeeInfo.setThirdDeptName(thirdDepart.getDepartName());
|
baseEmployeeInfo.setThirdDeptName(thirdDepart.getDepartName());
|
||||||
baseEmployeeInfo.setThisDeptCode(loginUser.getOrgCode());
|
baseEmployeeInfo.setThisDeptCode(loginUser.getOrgCode());
|
||||||
SysDepart departByOrgCode = sysCache.getDepartByOrgCode(loginUser.getOrgCode());
|
SysDepart departByOrgCode = sysCache.getDepartByOrgCode(loginUser.getOrgCode());
|
||||||
baseEmployeeInfo.setThisDeptName(departByOrgCode.getDepartName());
|
baseEmployeeInfo.setThisDeptName(departByOrgCode.getDepartName());
|
||||||
|
baseEmployeeInfo.setThisDeptId(departByOrgCode.getId());
|
||||||
|
baseEmployeeInfo.setRoleCodes(loginUser.getRoleCodes());
|
||||||
|
baseEmployeeInfo.setDepartCodes(String.join(",", managedCodes));
|
||||||
|
baseEmployeeInfo.setDepartIds(String.join(",", departIds));
|
||||||
return baseEmployeeInfo;
|
return baseEmployeeInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user