Browse Source

益寿缘-优化App弹窗展示接口

cgp 10 hours ago
parent
commit
60091b5e03

+ 6 - 0
fs-service/src/main/java/com/fs/doctor/service/IFsDoctorOnlineService.java

@@ -4,6 +4,7 @@ import java.time.LocalDateTime;
 import java.util.List;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.doctor.domain.FsDoctorOnline;
+import com.fs.doctor.vo.DoctorLoginLogVO;
 
 /**
  * 医生在线状态Service接口
@@ -74,6 +75,11 @@ public interface IFsDoctorOnlineService extends IService<FsDoctorOnline>{
      */
     int updateOnlineStatusOnLogout(Long doctorId);
 
+    /**
+     *  获取医生登录日志
+     * */
+    public List<DoctorLoginLogVO> getUserLoginLogByDay(Long doctorId, String date);
+
     /**
      * 定时检查 - 离线医生处理
      *

+ 99 - 2
fs-service/src/main/java/com/fs/doctor/service/impl/FsDoctorOnlineServiceImpl.java

@@ -1,9 +1,18 @@
 package com.fs.doctor.service.impl;
 
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.doctor.vo.DoctorLoginLogVO;
+import com.fs.his.domain.FsDoctor;
+import com.fs.his.mapper.FsDoctorMapper;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.doctor.mapper.FsDoctorOnlineMapper;
@@ -16,12 +25,28 @@ import com.fs.doctor.service.IFsDoctorOnlineService;
  * @author fs
  * @date 2025-12-12
  */
+@Slf4j
 @Service
 public class FsDoctorOnlineServiceImpl extends ServiceImpl<FsDoctorOnlineMapper, FsDoctorOnline> implements IFsDoctorOnlineService {
 
     @Autowired
     private FsDoctorOnlineMapper fsDoctorOnlineMapper;
 
+    @Autowired
+    private FsDoctorMapper fsDoctorMapper;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    // 注入 ObjectMapper 用于 JSON 序列化/反序列化
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    /** 医生登录key*/
+    private static final String LOGIN_LOG_REDIS_KEY_PREFIX = "doctor:login:log:";
+    /** key的有效时长*/
+    private static final long REDIS_EXPIRE_HOURS = 48;
+
     /**
      * 查询医生在线状态
      *
@@ -94,7 +119,13 @@ public class FsDoctorOnlineServiceImpl extends ServiceImpl<FsDoctorOnlineMapper,
         record.setDoctorId(doctorId);
         record.setLastHeartbeat(LocalDateTime.now());
         record.setIsOnline(1);
-        return fsDoctorOnlineMapper.upsertHeartbeat(record);
+        int result = fsDoctorOnlineMapper.upsertHeartbeat(record);
+        if (result > 0){
+            FsDoctor doctor = fsDoctorMapper.selectFsDoctorByDoctorId(doctorId);
+            //这里用redis记录登录时间(医生名称、医生id、登入、时间)
+            recordUserAction(doctorId, doctor.getDoctorName(), "login", LocalDateTime.now());
+        }
+        return result;
     }
 
     @Override
@@ -103,7 +134,73 @@ public class FsDoctorOnlineServiceImpl extends ServiceImpl<FsDoctorOnlineMapper,
         record.setDoctorId(doctorId);
         record.setLastHeartbeat(LocalDateTime.now()); // 正常登出,更新为当前时间
         record.setIsOnline(0);
-        return fsDoctorOnlineMapper.upsertHeartbeat(record);
+        int result = fsDoctorOnlineMapper.upsertHeartbeat(record);
+        if (result > 0){
+            //这里用redis记录登出时间((医生名称、医生id、登出、时间))
+            FsDoctor doctor = fsDoctorMapper.selectFsDoctorByDoctorId(doctorId);
+            recordUserAction(doctorId, doctor.getDoctorName(), "logout", LocalDateTime.now());
+        }
+        return result;
+    }
+
+    /**
+     * 记录用户登录或登出事件到Redis
+     * @param doctorId 医生ID
+     * @param doctorName 医生姓名
+     * @param action 动作 ("login" or "logout")
+     * @param timestamp 时间戳
+     */
+    private void recordUserAction(Long doctorId, String doctorName, String action, LocalDateTime timestamp) {
+        try {
+            // 构建要存储的对象
+            DoctorLoginLogVO logVO = new DoctorLoginLogVO(doctorId, doctorName, action, timestamp);
+
+            // 序列化为JSON字符串
+            String logJsonStr = objectMapper.writeValueAsString(logVO);
+
+            // 构建key (按天分组)
+            String dateStr = timestamp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+            String redisKey = LOGIN_LOG_REDIS_KEY_PREFIX + dateStr + ":" + doctorId;
+
+            // 将记录推送到Redis的List中
+            redisCache.setVoice(redisKey, logJsonStr);
+
+            // 设置过期时间(48小时)
+            redisCache.expire(redisKey, REDIS_EXPIRE_HOURS, TimeUnit.HOURS);
+
+        } catch (Exception e) {
+            // 记录错误日志,但不影响主流程
+            log.error("Failed to record user action to Redis. doctorId: {}, action: {}", doctorId, action, e);
+        }
+    }
+
+
+    /**
+     * 从Redis查询指定用户某天的登录登出记录
+     * @param doctorId 医生ID
+     * @param date 日期 (格式: "yyyy-MM-dd")
+     * @return 记录列表
+     */
+    @Override
+    public List<DoctorLoginLogVO> getUserLoginLogByDay(Long doctorId, String date) {
+        try {
+            String redisKey = LOGIN_LOG_REDIS_KEY_PREFIX + date + ":" + doctorId;
+            List<String> jsonLogList = (List<String>) redisCache.getVoiceAllList(redisKey);
+
+            if (jsonLogList == null || jsonLogList.isEmpty()) {
+                return new ArrayList<>();
+            }
+
+            List<DoctorLoginLogVO> logVOList = new ArrayList<>();
+            for (String jsonStr : jsonLogList) {
+                DoctorLoginLogVO logVO = objectMapper.readValue(jsonStr, DoctorLoginLogVO.class);
+                logVOList.add(logVO);
+            }
+            return logVOList;
+        } catch (Exception e) {
+            log.error("Failed to query user login log from Redis. doctorId: {}, date: {}", doctorId, date, e);
+            return new ArrayList<>();
+        }
     }
 
     @Override

+ 21 - 0
fs-service/src/main/java/com/fs/doctor/vo/DoctorLoginLogVO.java

@@ -0,0 +1,21 @@
+package com.fs.doctor.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+@Data
+public class DoctorLoginLogVO {
+    private Long doctorId;
+    private String doctorName;
+    private String action; // "login" or "logout"
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime timestamp;
+
+    public DoctorLoginLogVO(Long doctorId, String doctorName, String action, LocalDateTime timestamp) {
+        this.doctorId = doctorId;
+        this.doctorName = doctorName;
+        this.action = action;
+        this.timestamp = timestamp;
+    }
+}

+ 9 - 3
fs-service/src/main/java/com/fs/his/service/impl/AppUserRewardServiceImpl.java

@@ -75,8 +75,10 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
     @Autowired
     private FsStoreProductScrmMapper fsStoreProductMapper;
 
+    /** App用户每天的有效看课标识*/
     private static final String APP_WATCH_COURSE_DAY_KEY = "app:watch:course:day:";
 
+    /**  下发奖励锁 */
     private static final String LOCK_WATCH_REWARD_KEY = "lock:watch:reward:";
 
     @Override
@@ -424,7 +426,6 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
             log.info("用户:{}已注册登录过,非首次登录", fsUserId);
             return fsAppRole;
         }
-
         // 2. 查奖品表判断是否已发放登录注册奖励
         FsUserRewards queryReward = rewardsMapper.selectByUserIdAndFirstLoginType(fsUserId);
         if (queryReward == null) {
@@ -449,6 +450,9 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
         if (defaultConfig == null) {
             return noShowWindow;
         }
+        if (fsUser.getAppRewardsViewedDays()<defaultConfig.getCourseNeedDay()){
+            return noShowWindow;
+        }
         //查询今日领取红包记录
         FsCourseRedPacketLog fsCourseRedPacketLog = redPacketLogMapper.selectRedPacketLogByUserIdToday(fsUserId);
         if (fsCourseRedPacketLog != null) {
@@ -604,14 +608,13 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
         return fsAppRole;
     }
     //根据用户id、角色id获取配置的奖品信息(实物商品包含商品名称和图片)
-    private FsAppRole getAppRoleConfigWatchCourse(Long fsUserId) {
+    private FsAppRole  getAppRoleConfigWatchCourse(Long fsUserId) {
         //获取当前用户最高权重角色, "false"表示查询等级数字最大的权重角色
         FsAppRole fsAppRole = appRoleMapper.selectHighestLevelAppRoleByUserId(fsUserId, "false");
         if (fsAppRole == null) {
             log.info("未找到用户:{}对应的角色看课奖励配置", fsUserId);
             return null;
         }
-        fsAppRole.setShow(true);//显示弹窗
         //补充实物商品名称以及商品图片
         if (fsAppRole.getCourseRewardType()!=null&&fsAppRole.getCourseRewardType()==3){
             if (fsAppRole.getCourseProductType()!=null&&fsAppRole.getCourseProductType()==1){
@@ -619,6 +622,7 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
                 if (fsPackage != null){
                     fsAppRole.setGoodsName(fsPackage.getPackageName());
                     fsAppRole.setGoodsUrl(fsPackage.getImgUrl());
+                    fsAppRole.setShow(true);//显示弹窗
                     return fsAppRole;
                 }
             } else if ( fsAppRole.getCourseProductType()!=null&&fsAppRole.getCourseProductType()==2){
@@ -626,10 +630,12 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
                 if (storeProductScrm != null){
                     fsAppRole.setGoodsName(storeProductScrm.getProductName());
                     fsAppRole.setGoodsUrl(storeProductScrm.getImage());
+                    fsAppRole.setShow(true);//显示弹窗
                     return fsAppRole;
                 }
             }
         }
+        fsAppRole.setShow(true);//显示弹窗
         return fsAppRole;
     }