From a3bf6a2063d6995afde50d80d74f892d81024e4c Mon Sep 17 00:00:00 2001 From: hejiayang <296714699@qq.com> Date: Tue, 24 Mar 2026 12:02:26 +0800 Subject: [PATCH] =?UTF-8?q?fix:=203=E6=9C=883=E6=97=A5=E5=90=8Edevelop?= =?UTF-8?q?=E5=88=86=E6=94=AF=E4=BB=A3=E7=A0=81=E8=BF=81=E7=A7=BB=E8=87=B3?= =?UTF-8?q?xc=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../renkang/watch/WatchH5IndexItemNewVo.java | 4 + .../com/renkang/watch/dto/BindDeviceDTO.java | 19 ++ .../watch/vo/UserData/res/UserStat.java | 61 +++- .../controller/WatchDataApiController.java | 11 + .../api/service/WatchDataApiService.java | 9 + .../service/impl/WatchDataApiServiceImpl.java | 268 +++++++++++++++--- .../watch/controller/WatchController.java | 6 +- .../controller/WatchDeviceController.java | 15 +- .../watch/service/IWatchDeviceService.java | 6 +- .../service/impl/WatchDeviceServiceImpl.java | 24 +- .../jeecg/common/constant/CommonConstant.java | 5 + .../java/org/jeecg/util/AesEncryptUtils.java | 110 +++++++ .../jeecg/bean/response/BaseEmployeeInfo.java | 18 ++ .../api/controller/SysApiController.java | 13 + .../service/impl/SysUserServiceImpl.java | 16 ++ 15 files changed, 543 insertions(+), 42 deletions(-) create mode 100644 health-watch/health-watch-api/src/main/java/com/renkang/watch/dto/BindDeviceDTO.java create mode 100644 jeecg-boot-base-core/src/main/java/org/jeecg/util/AesEncryptUtils.java diff --git a/health-watch/health-watch-api/src/main/java/com/renkang/watch/WatchH5IndexItemNewVo.java b/health-watch/health-watch-api/src/main/java/com/renkang/watch/WatchH5IndexItemNewVo.java index ec8d8d1..da37a7c 100644 --- a/health-watch/health-watch-api/src/main/java/com/renkang/watch/WatchH5IndexItemNewVo.java +++ b/health-watch/health-watch-api/src/main/java/com/renkang/watch/WatchH5IndexItemNewVo.java @@ -22,6 +22,10 @@ public class WatchH5IndexItemNewVo { @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") 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时此字段返回长睡时长 */ diff --git a/health-watch/health-watch-api/src/main/java/com/renkang/watch/dto/BindDeviceDTO.java b/health-watch/health-watch-api/src/main/java/com/renkang/watch/dto/BindDeviceDTO.java new file mode 100644 index 0000000..694637c --- /dev/null +++ b/health-watch/health-watch-api/src/main/java/com/renkang/watch/dto/BindDeviceDTO.java @@ -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; +} diff --git a/health-watch/health-watch-api/src/main/java/com/renkang/watch/vo/UserData/res/UserStat.java b/health-watch/health-watch-api/src/main/java/com/renkang/watch/vo/UserData/res/UserStat.java index 508b545..16ba259 100644 --- a/health-watch/health-watch-api/src/main/java/com/renkang/watch/vo/UserData/res/UserStat.java +++ b/health-watch/health-watch-api/src/main/java/com/renkang/watch/vo/UserData/res/UserStat.java @@ -1,22 +1,79 @@ package com.renkang.watch.vo.UserData.res; import com.fasterxml.jackson.annotation.JsonFormat; +import com.renkang.watch.WatchH5IndexItemNewVo; import lombok.Data; import java.math.BigDecimal; import java.util.Date; -/** 用户统计数据 +/** + * 用户统计数据 + * * @author stan * @since 2024-11-04 16:33 */ @Data public class UserStat { + /** + * 数据类型:0=心率 1=血氧 2=压力 3=体温 4=睡眠 + */ + private String type; private BigDecimal latest; private BigDecimal max; private BigDecimal min; 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; + 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)); + } } diff --git a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/controller/WatchDataApiController.java b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/controller/WatchDataApiController.java index 5c65153..ccda5c6 100644 --- a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/controller/WatchDataApiController.java +++ b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/controller/WatchDataApiController.java @@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Date; +import java.util.List; import java.util.Map; @RestController @@ -41,6 +42,16 @@ public class WatchDataApiController { return Result.ok(watchDataApiService.userStatResult(type)); } + /** + * 用户手表数据(批量返回) + * + * @return data + */ + @GetMapping("userWatchDataAll") + public Result> userStatResultAll(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date date) { + return Result.ok(watchDataApiService.userStatResultAll(date)); + } + /** * 用户此刻是否拥有手表 * diff --git a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/WatchDataApiService.java b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/WatchDataApiService.java index d453318..5358df0 100644 --- a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/WatchDataApiService.java +++ b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/WatchDataApiService.java @@ -11,6 +11,7 @@ import com.renkang.watch.vo.UserData.res.sleep.WatchDataSleepRes; import org.jeecg.common.api.vo.Result; import java.util.Date; +import java.util.List; import java.util.Map; public interface WatchDataApiService { @@ -43,6 +44,14 @@ public interface WatchDataApiService { UserStat userStatResult(String type); + /** + * 查询当前登录用户所有健康指标统计数据(按指定日期过滤) + * + * @param date 查询日期,为 null 时返回最新一条数据 + * @return 各指标统计结果列表 + */ + List userStatResultAll(Date date); + Boolean userHaveWatch(String userId); UserWatchInfo findWatchInfo(String userId); diff --git a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/impl/WatchDataApiServiceImpl.java b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/impl/WatchDataApiServiceImpl.java index c2aef6a..b67f336 100644 --- a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/impl/WatchDataApiServiceImpl.java +++ b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/api/service/impl/WatchDataApiServiceImpl.java @@ -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.QueryWrapper; 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.service.WatchDataApiService; 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.heartRate.WatchDataHeartRateRes; import com.renkang.watch.vo.UserData.res.sleep.WatchDataSleepRes; +import com.renkang.watch.vo.WatchH5IndexItemVo; import org.apache.commons.collections4.CollectionUtils; import org.jeecg.common.api.vo.Result; import org.jeecg.common.system.vo.LoginUser; import org.jeecg.common.util.DateUtils; +import org.jeecg.common.util.RedisUtil; import org.jeecg.global.GlobalUtils; import org.springframework.beans.BeanUtils; 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 javax.annotation.PostConstruct; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Service public class WatchDataApiServiceImpl implements WatchDataApiService { - @Autowired private WatchDataWorkoutMapper watchDataWorkoutMapper; - - @Autowired - private WatchStatUserInfoDayHeartRateMapper watchStatUserInfoDayHeartRateMapper; - - @Autowired private WatchDataHeartRateMapper watchDataHeartRateMapper; @@ -60,9 +65,6 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { @Autowired private WatchStatUserInfoDaySpo2ServiceImpl watchStatUserInfoDaySpo2ServiceImpl; - @Autowired - private WatchStatUserInfoDaySleepMapper watchStatUserInfoDaySleepMapper; - @Autowired private WatchDataSleepNewMinuteMapper watchDataSleepNewMinuteMapper; @@ -96,7 +98,6 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { @Autowired private IWatchDataTemperatureService watchDataTemperatureService; - @Autowired private IWatchDataStressService watchDataStressService; @Autowired @@ -106,12 +107,47 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { @Autowired private WatchUserDataMapper watchUserDataMapper; @Autowired + private WatchStatUserInfoDayHeartRateMapper watchStatUserInfoDayHeartRateMapper; + @Autowired + private WatchStatUserInfoDaySleepMapper watchStatUserInfoDaySleepMapper; + @Autowired private WatchStatUserInfoDaySpo2Mapper watchStatUserInfoDaySpo2Mapper; @Autowired private WatchStatUserInfoDayStressMapper watchStatUserInfoDayStressMapper; @Autowired 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 public Result> selectSportCostListApi() { LoginUser loginUser = GlobalUtils.getLoginUser(); @@ -834,20 +870,171 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { String userId = GlobalUtils.getLoginUser().getId(); switch (type) { case "0": - return handleHeartRate(userId); + return handleHeartRate(userId,null); case "1": - return handleBloodOxygen(userId); + return handleBloodOxygen(userId,null); case "2": - return handleStress(userId); + return handleStress(userId,null); case "3": - return handleBodyTemperature(userId); + return handleBodyTemperature(userId,null); case "4": - return handleSleep(userId); + return handleSleep(userId,null); default: return null; } } + @Override + public List 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) cached; + } + + // 构建「数据类型 → 异步查询 Future」映射,消除 switch-case 冗余 + Map> 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 list = new ArrayList<>(); + emptyItems().sequential().forEach(itemVo -> { + CompletableFuture 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 result = list.stream().map(UserStat::new).collect(Collectors.toList()); + + // 写入 Redis 缓存,TTL=3分钟(180秒) + redisUtil.set(cacheKey, result, 180); + return result; + } + + private Stream 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 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 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 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 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 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 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 public Boolean userHaveWatch(String 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 queryWrapper = Wrappers.lambdaQuery(WatchStatUserInfoDaySleep.class) .select(WatchStatUserInfoDaySleep::getId, WatchStatUserInfoDaySleep::getDeepSleepTimes, @@ -893,13 +1080,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { WatchStatUserInfoDaySleep::getDeepLightTimes, WatchStatUserInfoDaySleep::getDataDate) .eq(WatchStatUserInfoDaySleep::getUserId, userId) - .orderByDesc(WatchStatUserInfoDaySleep::getDataDate) + // 传了日期则精确匹配,否则取最新一条 + .eq(date != null, WatchStatUserInfoDaySleep::getDataDate, date) + .orderByDesc(date == null, WatchStatUserInfoDaySleep::getDataDate) .last("FETCH FIRST 1 ROWS ONLY"); WatchStatUserInfoDaySleep data = watchStatUserInfoDaySleepMapper.selectOne(queryWrapper); if (data == null) { - return null; + return new UserStat(); } UserStat stat = new UserStat(); @@ -915,15 +1104,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { return stat; } - private UserStat handleBodyTemperature(String userId) { + private UserStat handleBodyTemperature(String userId, Date date) { int type = 5; int scale = 1; // 最新数据 UserStat userStat = userWatchDataLatest(userId, type, scale); if (userStat == null) { - return null; + return new UserStat(); } - LambdaQueryWrapper queryWrapper = getQueryWrapper(userId); + LambdaQueryWrapper queryWrapper = getQueryWrapper(userId, date); WatchStatUserInfoDayTemp data = watchStatUserInfoDayTempMapper.selectOne(queryWrapper); if (data == null) { return userStat; @@ -933,15 +1122,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { return userStat; } - private UserStat handleStress(String userId) { + private UserStat handleStress(String userId, Date date) { int type = 3; int scale = 0; // 最新数据 UserStat userStat = userWatchDataLatest(userId, type, scale); if (userStat == null) { - return null; + return new UserStat(); } - LambdaQueryWrapper queryWrapper = getQueryWrapper(userId); + LambdaQueryWrapper queryWrapper = getQueryWrapper(userId, date); WatchStatUserInfoDayStress data = watchStatUserInfoDayStressMapper.selectOne(queryWrapper); if (data == null) { return userStat; @@ -951,15 +1140,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { return userStat; } - private UserStat handleBloodOxygen(String userId) { + private UserStat handleBloodOxygen(String userId, Date date) { int type = 2; int scale = 0; // 最新数据 UserStat userStat = userWatchDataLatest(userId, type, scale); if (userStat == null) { - return null; + return new UserStat(); } - LambdaQueryWrapper queryWrapper = getQueryWrapper(userId); + LambdaQueryWrapper queryWrapper = getQueryWrapper(userId, date); WatchStatUserInfoDaySpo2 data = watchStatUserInfoDaySpo2Mapper.selectOne(queryWrapper); if (data == null) { return userStat; @@ -969,15 +1158,15 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { return userStat; } - private UserStat handleHeartRate(String userId) { + private UserStat handleHeartRate(String userId, Date date) { int type = 1; int scale = 0; // 最新数据 UserStat userStat = userWatchDataLatest(userId, type, scale); if (userStat == null) { - return null; + return new UserStat(); } - LambdaQueryWrapper queryWrapper = getQueryWrapper(userId); + LambdaQueryWrapper queryWrapper = getQueryWrapper(userId, date); WatchStatUserInfoDayHeartRate data = watchStatUserInfoDayHeartRateMapper.selectOne(queryWrapper); if (data == null) { return userStat; @@ -1029,10 +1218,12 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { return stat; } - private LambdaQueryWrapper getQueryWrapper(String userId) { + private LambdaQueryWrapper getQueryWrapper(String userId, Date date) { QueryWrapper queryWrapper = new QueryWrapper<>(); 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"); return queryWrapper.lambda(); } @@ -1051,4 +1242,17 @@ public class WatchDataApiServiceImpl implements WatchDataApiService { 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; + } + } diff --git a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchController.java b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchController.java index 3c94fa4..eb27c08 100644 --- a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchController.java +++ b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchController.java @@ -11,10 +11,7 @@ import lombok.extern.slf4j.Slf4j; import org.jeecg.common.api.vo.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.Date; import java.util.List; @@ -327,4 +324,5 @@ public class WatchController { // } return result; } + } diff --git a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchDeviceController.java b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchDeviceController.java index 4a71e99..e45e96f 100644 --- a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchDeviceController.java +++ b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/controller/WatchDeviceController.java @@ -185,6 +185,12 @@ public class WatchDeviceController extends JeecgController { */ Result bindUser(BindUserDTO dto) throws ParseException; + Result bindDevice(String watchNo) throws ParseException; + /** * 绑定用户 * @param dto * @return */ - Result unbundleUser(BindUserDTO dto) throws ParseException; + Result unbundleUser(BindUserDTO dto); + + Result unbundleDevice(String watchNo); /** * 工具开关 diff --git a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/service/impl/WatchDeviceServiceImpl.java b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/service/impl/WatchDeviceServiceImpl.java index f830e7d..8620930 100644 --- a/health-watch/health-watch-biz/src/main/java/com/renkang/watch/service/impl/WatchDeviceServiceImpl.java +++ b/health-watch/health-watch-biz/src/main/java/com/renkang/watch/service/impl/WatchDeviceServiceImpl.java @@ -291,7 +291,7 @@ public class WatchDeviceServiceImpl extends ServiceImpl> encryptInfo(@RequestParam String str) throws Exception { + Map map = new HashMap<>(); + map.put("encryptData", AesEncryptUtils.encrypt(str, CommonConstant.URL_SECRET_KEY)); + return Result.ok(map); + } } diff --git a/jeecg-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java b/jeecg-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java index bc3957c..97749d5 100644 --- a/jeecg-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java +++ b/jeecg-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java @@ -3906,6 +3906,16 @@ public class SysUserServiceImpl extends ServiceImpl impl if (null == loginUser) { ExceptionAssertsUtil.fail("用户不存在!"); } + List managedCodes = GlobalUtils.getManagedCodes(null); + Set 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 queryWrapper = new MPJLambdaWrapper() .selectAs(SysUser::getId, BaseEmployeeInfo::getUserId) .selectAs(SysUser::getUsername, BaseEmployeeInfo::getUserName) @@ -3931,13 +3941,19 @@ public class SysUserServiceImpl extends ServiceImpl impl baseEmployeeInfo.setAge(ObjectUtils.isNotEmpty(loginUser.getAge()) && loginUser.getAge() > 0 ? loginUser.getAge() : null); SysDepart secondDepart = sysCache.getDepartByOrgCode(GlobalUtils.getSecondDepartOrgCode(loginUser.getOrgCode())); baseEmployeeInfo.setSecondDeptCode(secondDepart.getOrgCode()); + baseEmployeeInfo.setSecondDeptId(secondDepart.getId()); baseEmployeeInfo.setSecondDeptName(secondDepart.getDepartName()); SysDepart thirdDepart = sysCache.getDepartByOrgCode(GlobalUtils.getThirdDepartOrgCode(loginUser.getOrgCode())); baseEmployeeInfo.setThirdDeptCode(thirdDepart.getOrgCode()); + baseEmployeeInfo.setThirdDeptId(thirdDepart.getId()); baseEmployeeInfo.setThirdDeptName(thirdDepart.getDepartName()); baseEmployeeInfo.setThisDeptCode(loginUser.getOrgCode()); SysDepart departByOrgCode = sysCache.getDepartByOrgCode(loginUser.getOrgCode()); baseEmployeeInfo.setThisDeptName(departByOrgCode.getDepartName()); + baseEmployeeInfo.setThisDeptId(departByOrgCode.getId()); + baseEmployeeInfo.setRoleCodes(loginUser.getRoleCodes()); + baseEmployeeInfo.setDepartCodes(String.join(",", managedCodes)); + baseEmployeeInfo.setDepartIds(String.join(",", departIds)); return baseEmployeeInfo; } }