2 Commits

Author SHA1 Message Date
KilLze
9c1b701594 删除AI的小巧思2 2026-01-20 15:13:58 +08:00
KilLze
b77952164b 删除AI的小巧思 2026-01-20 15:13:35 +08:00
6 changed files with 2 additions and 580 deletions

View File

@@ -7,7 +7,6 @@ package com.bao.dating.context;
public class UserContext { public class UserContext {
private static final ThreadLocal<Long> USER_HOLDER = new ThreadLocal<>(); private static final ThreadLocal<Long> USER_HOLDER = new ThreadLocal<>();
private static final ThreadLocal<String> TOKEN_HOLDER = new ThreadLocal<>();
/** /**
* 设置当前线程的用户ID * 设置当前线程的用户ID
@@ -25,27 +24,4 @@ public class UserContext {
return USER_HOLDER.get(); return USER_HOLDER.get();
} }
/**
* 设置当前线程的token
* @param token 用户token
*/
public static void setToken(String token) {
TOKEN_HOLDER.set(token);
}
/**
* 获取当前线程的token
* @return 当前token如果未设置则返回null
*/
public static String getToken() {
return TOKEN_HOLDER.get();
}
/**
* 清除当前线程的用户ID和token
*/
public static void clear() {
USER_HOLDER.remove();
TOKEN_HOLDER.remove();
}
} }

View File

@@ -1,6 +1,5 @@
package com.bao.dating.controller; package com.bao.dating.controller;
import com.bao.dating.anno.Log;
import com.bao.dating.common.Result; import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode; import com.bao.dating.common.ResultCode;
import com.bao.dating.context.UserContext; import com.bao.dating.context.UserContext;
@@ -8,11 +7,9 @@ import com.bao.dating.pojo.dto.UserDeviceDTO;
import com.bao.dating.pojo.dto.UserInfoDTO; import com.bao.dating.pojo.dto.UserInfoDTO;
import com.bao.dating.pojo.dto.UserLoginDTO; import com.bao.dating.pojo.dto.UserLoginDTO;
import com.bao.dating.pojo.dto.UserLoginWithDeviceDTO; import com.bao.dating.pojo.dto.UserLoginWithDeviceDTO;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.vo.UserDeviceVO; import com.bao.dating.pojo.vo.UserDeviceVO;
import com.bao.dating.pojo.vo.UserInfoVO; import com.bao.dating.pojo.vo.UserInfoVO;
import com.bao.dating.pojo.vo.UserLoginVO; import com.bao.dating.pojo.vo.UserLoginVO;
import com.bao.dating.service.UserDeviceService;
import com.bao.dating.service.UserService; import com.bao.dating.service.UserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -34,44 +31,11 @@ public class UserController {
@Autowired @Autowired
private UserService userService; private UserService userService;
@Autowired
private UserDeviceService userDeviceService;
/**
* 登录(带设备信息,推荐)
* @param loginDTO 登录参数(包含设备信息)
* @param request 请求
*/
@PostMapping("/login")
public Result<UserLoginVO> login(@RequestBody UserLoginWithDeviceDTO loginDTO, HttpServletRequest request) {
UserLoginDTO userLoginDTO = new UserLoginDTO();
userLoginDTO.setUsername(loginDTO.getUsername());
userLoginDTO.setPassword(loginDTO.getPassword());
UserLoginVO userLoginVO = userService.userLogin(userLoginDTO);
// 记录设备信息
if (loginDTO.getDeviceId() != null && !loginDTO.getDeviceId().isEmpty()) {
UserDeviceDTO deviceDTO = new UserDeviceDTO();
deviceDTO.setDeviceId(loginDTO.getDeviceId());
deviceDTO.setDeviceType(loginDTO.getDeviceType());
deviceDTO.setDeviceName(loginDTO.getDeviceName());
deviceDTO.setDeviceBrand(loginDTO.getDeviceBrand());
deviceDTO.setOsVersion(loginDTO.getOsVersion());
deviceDTO.setAppVersion(loginDTO.getAppVersion());
deviceDTO.setIpAddress(getClientIp(request));
userDeviceService.recordDevice(userLoginVO.getUserId(), deviceDTO, userLoginVO.getToken());
}
return Result.success(ResultCode.SUCCESS, "登录成功", userLoginVO);
}
/** /**
* 简单登录(不记录设备信息) * 简单登录(不记录设备信息)
* @param userLoginDTO 登录参数 * @param userLoginDTO 登录参数
*/ */
@PostMapping("/simple-login") @PostMapping("/login")
public Result<UserLoginVO> simpleLogin(@RequestBody UserLoginDTO userLoginDTO) { public Result<UserLoginVO> simpleLogin(@RequestBody UserLoginDTO userLoginDTO) {
UserLoginVO userLoginVO = userService.userLogin(userLoginDTO); UserLoginVO userLoginVO = userService.userLogin(userLoginDTO);
return Result.success(ResultCode.SUCCESS, "登录成功", userLoginVO); return Result.success(ResultCode.SUCCESS, "登录成功", userLoginVO);
@@ -249,81 +213,7 @@ public class UserController {
boolean online = userService.isUserOnline(userId); boolean online = userService.isUserOnline(userId);
return Result.success(ResultCode.SUCCESS, "查询成功", online); return Result.success(ResultCode.SUCCESS, "查询成功", online);
} }
// ==================== 设备管理接口 ====================
/**
* 获取用户所有登录设备
* @return 设备列表
*/
@GetMapping("/devices")
public Result<List<UserDeviceVO>> getUserDevices() {
Long userId = UserContext.getUserId();
List<UserDeviceVO> devices = userDeviceService.getUserDevices(userId);
return Result.success(ResultCode.SUCCESS, "获取成功", devices);
}
/**
* 强制下线某设备
* @param deviceId 设备ID
* @return 操作结果
*/
@PostMapping("/devices/kick/{deviceId}")
public Result<Void> kickDevice(@PathVariable String deviceId) {
Long userId = UserContext.getUserId();
boolean success = userDeviceService.kickDevice(userId, deviceId);
if (success) {
return Result.success(ResultCode.SUCCESS, "设备已下线", null);
} else {
return Result.error(ResultCode.FAIL, "设备不存在或无法下线");
}
}
/**
* 下线其他所有设备(除当前设备外)
* @return 被下线的设备数量
*/
@PostMapping("/devices/kick-others")
public Result<Integer> kickOtherDevices() {
Long userId = UserContext.getUserId();
// 获取当前设备
UserDeviceVO currentDevice = userDeviceService.getDeviceByToken(
UserContext.getToken() != null ? UserContext.getToken() : ""
);
String currentDeviceId = currentDevice != null ? currentDevice.getDeviceId() : null;
if (currentDeviceId == null) {
// 如果找不到当前设备,查询最新的设备
List<UserDeviceVO> devices = userDeviceService.getUserDevices(userId);
if (!devices.isEmpty()) {
currentDeviceId = devices.get(0).getDeviceId();
}
}
if (currentDeviceId == null) {
return Result.error(ResultCode.FAIL, "未找到设备");
}
int count = userDeviceService.kickOtherDevices(userId, currentDeviceId);
return Result.success(ResultCode.SUCCESS, "已下线 " + count + " 个设备", count);
}
/**
* 获取当前设备信息
* @return 当前设备信息
*/
@GetMapping("/devices/current")
public Result<UserDeviceVO> getCurrentDevice() {
String token = UserContext.getToken();
if (token == null) {
return Result.error(ResultCode.FAIL, "未登录");
}
UserDeviceVO device = userDeviceService.getDeviceByToken(token);
return Result.success(ResultCode.SUCCESS, "获取成功", device);
}
// ==================== 工具方法 ====================
/** /**
* 获取客户端IP地址 * 获取客户端IP地址
* @param request 请求 * @param request 请求

View File

@@ -1,108 +0,0 @@
package com.bao.dating.controller;
import com.bao.dating.common.Result;
import com.bao.dating.common.ResultCode;
import com.bao.dating.context.UserContext;
import com.bao.dating.pojo.dto.UserDeviceDTO;
import com.bao.dating.pojo.vo.UserDeviceVO;
import com.bao.dating.service.UserDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户设备管理控制器
* @author KilLze
*/
@RestController
@RequestMapping("/user/device")
public class UserDeviceController {
@Autowired
private UserDeviceService userDeviceService;
/**
* 获取用户所有登录设备
* @return 设备列表
*/
@GetMapping("/list")
public Result<List<UserDeviceVO>> getUserDevices() {
Long userId = UserContext.getUserId();
List<UserDeviceVO> devices = userDeviceService.getUserDevices(userId);
return Result.success(ResultCode.SUCCESS, "获取成功", devices);
}
/**
* 强制下线某设备
* @param deviceId 设备ID
* @return 操作结果
*/
@PostMapping("/kick/{deviceId}")
public Result<Void> kickDevice(@PathVariable String deviceId) {
Long userId = UserContext.getUserId();
boolean success = userDeviceService.kickDevice(userId, deviceId);
if (success) {
return Result.success(ResultCode.SUCCESS, "设备已下线", null);
} else {
return Result.error(ResultCode.FAIL, "设备不存在或无法下线");
}
}
/**
* 设置当前设备
* @param deviceId 设备ID
* @return 操作结果
*/
@PostMapping("/current/{deviceId}")
public Result<Void> setCurrentDevice(@PathVariable String deviceId) {
Long userId = UserContext.getUserId();
boolean success = userDeviceService.setCurrentDevice(userId, deviceId);
if (success) {
return Result.success(ResultCode.SUCCESS, "设置成功", null);
} else {
return Result.error(ResultCode.FAIL, "设备不存在");
}
}
/**
* 删除设备记录(不能删除当前设备)
* @param deviceId 设备ID
* @return 操作结果
*/
@DeleteMapping("/{deviceId}")
public Result<Void> deleteDevice(@PathVariable String deviceId) {
Long userId = UserContext.getUserId();
boolean success = userDeviceService.deleteDevice(userId, deviceId);
if (success) {
return Result.success(ResultCode.SUCCESS, "删除成功", null);
} else {
return Result.error(ResultCode.FAIL, "设备不存在或无法删除当前设备");
}
}
/**
* 下线其他所有设备(除当前设备外)
* @return 被下线的设备数量
*/
@PostMapping("/kick-others")
public Result<Integer> kickOtherDevices() {
Long userId = UserContext.getUserId();
// 获取当前设备ID
List<UserDeviceVO> devices = userDeviceService.getUserDevices(userId);
String currentDeviceId = null;
for (UserDeviceVO device : devices) {
if (Boolean.TRUE.equals(device.getIsCurrent())) {
currentDeviceId = device.getDeviceId();
break;
}
}
if (currentDeviceId == null) {
return Result.error(ResultCode.FAIL, "未找到当前设备");
}
int count = userDeviceService.kickOtherDevices(userId, currentDeviceId);
return Result.success(ResultCode.SUCCESS, "已下线 " + count + " 个设备", count);
}
}

View File

@@ -1,38 +0,0 @@
package com.bao.dating.mapper;
import com.bao.dating.pojo.entity.UserDevice;
import com.bao.dating.pojo.vo.UserDeviceVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户设备Mapper
* @author KilLze
*/
@Mapper
public interface UserDeviceMapper {
int insert(UserDevice userDevice);
UserDevice selectByDeviceId(@Param("deviceId") String deviceId);
List<UserDeviceVO> selectByUserId(@Param("userId") Long userId);
int updateStatus(@Param("deviceId") String deviceId, @Param("status") Integer status);
int updateLastActiveAt(@Param("deviceId") String deviceId);
int clearCurrentDevice(@Param("userId") Long userId);
int setCurrentDevice(@Param("deviceId") String deviceId);
int deleteByDeviceId(@Param("deviceId") String deviceId);
UserDevice selectByToken(@Param("token") String token);
int update(UserDevice userDevice);
int deleteByUserId(@Param("userId") Long userId);
}

View File

@@ -1,74 +0,0 @@
package com.bao.dating.service;
import com.bao.dating.pojo.dto.UserDeviceDTO;
import com.bao.dating.pojo.vo.UserDeviceVO;
import java.util.List;
/**
* 用户设备服务接口
* @author KilLze
*/
public interface UserDeviceService {
/**
* 记录用户登录设备
* @param userId 用户ID
* @param deviceDTO 设备信息
* @param token 登录Token
* @return 设备信息
*/
UserDeviceVO recordDevice(Long userId, UserDeviceDTO deviceDTO, String token);
/**
* 获取用户所有登录设备
* @param userId 用户ID
* @return 设备列表
*/
List<UserDeviceVO> getUserDevices(Long userId);
/**
* 强制下线某设备
* @param userId 用户ID
* @param deviceId 设备ID
* @return 是否成功
*/
boolean kickDevice(Long userId, String deviceId);
/**
* 设置某设备为当前设备
* @param userId 用户ID
* @param deviceId 设备ID
* @return 是否成功
*/
boolean setCurrentDevice(Long userId, String deviceId);
/**
* 删除设备记录
* @param userId 用户ID
* @param deviceId 设备ID
* @return 是否成功
*/
boolean deleteDevice(Long userId, String deviceId);
/**
* 更新设备活跃时间
* @param token 用户Token
*/
void updateDeviceActive(String token);
/**
* 根据Token获取设备信息
* @param token 用户Token
* @return 设备信息
*/
UserDeviceVO getDeviceByToken(String token);
/**
* 下线其他所有设备(除当前设备外)
* @param userId 用户ID
* @param currentDeviceId 当前设备ID
* @return 被下线的设备数量
*/
int kickOtherDevices(Long userId, String currentDeviceId);
}

View File

@@ -1,224 +0,0 @@
package com.bao.dating.service.impl;
import com.bao.dating.context.UserContext;
import com.bao.dating.mapper.UserDeviceMapper;
import com.bao.dating.mapper.UserMapper;
import com.bao.dating.pojo.dto.UserDeviceDTO;
import com.bao.dating.pojo.entity.User;
import com.bao.dating.pojo.entity.UserDevice;
import com.bao.dating.pojo.vo.UserDeviceVO;
import com.bao.dating.service.UserDeviceService;
import com.bao.dating.session.WsSessionManager;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* 用户设备服务实现类
* @author KilLze
*/
@Service
public class UserDeviceServiceImpl implements UserDeviceService {
@Autowired
private UserDeviceMapper userDeviceMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private WsSessionManager wsSessionManager;
@Override
public UserDeviceVO recordDevice(Long userId, UserDeviceDTO deviceDTO, String token) {
// 先查找是否已存在该设备
UserDevice existingDevice = userDeviceMapper.selectByDeviceId(deviceDTO.getDeviceId());
LocalDateTime now = LocalDateTime.now();
if (existingDevice != null) {
// 设备已存在,更新信息
existingDevice.setToken(token);
existingDevice.setStatus(1); // 在线
existingDevice.setIsCurrent(1); // 设为当前设备
existingDevice.setLastActiveAt(now);
existingDevice.setIpAddress(deviceDTO.getIpAddress());
existingDevice.setLocation(deviceDTO.getLocation());
userDeviceMapper.update(existingDevice);
// 清除该用户其他设备的当前标记
userDeviceMapper.clearCurrentDevice(userId);
userDeviceMapper.setCurrentDevice(deviceDTO.getDeviceId());
UserDeviceVO vo = new UserDeviceVO();
BeanUtils.copyProperties(existingDevice, vo);
vo.setDeviceType(getDeviceTypeName(existingDevice.getDeviceType()));
vo.setIsCurrent(true);
vo.setIsOnline(true);
return vo;
}
// 新设备,插入记录
UserDevice userDevice = new UserDevice();
userDevice.setUserId(userId);
userDevice.setDeviceId(deviceDTO.getDeviceId());
userDevice.setDeviceType(deviceDTO.getDeviceType());
userDevice.setDeviceName(deviceDTO.getDeviceName());
userDevice.setDeviceBrand(deviceDTO.getDeviceBrand());
userDevice.setOsVersion(deviceDTO.getOsVersion());
userDevice.setAppVersion(deviceDTO.getAppVersion());
userDevice.setIpAddress(deviceDTO.getIpAddress());
userDevice.setLocation(deviceDTO.getLocation());
userDevice.setToken(token);
userDevice.setStatus(1); // 在线
userDevice.setIsCurrent(1); // 设为当前设备
userDevice.setLastActiveAt(now);
userDevice.setLoginAt(now);
// 清除该用户其他设备的当前标记
userDeviceMapper.clearCurrentDevice(userId);
userDeviceMapper.insert(userDevice);
UserDeviceVO vo = new UserDeviceVO();
BeanUtils.copyProperties(userDevice, vo);
vo.setDeviceType(getDeviceTypeName(userDevice.getDeviceType()));
vo.setIsCurrent(true);
vo.setIsOnline(true);
return vo;
}
@Override
public List<UserDeviceVO> getUserDevices(Long userId) {
List<UserDeviceVO> devices = userDeviceMapper.selectByUserId(userId);
// 格式化设备类型
for (UserDeviceVO device : devices) {
device.setDeviceType(getDeviceTypeName(device.getDeviceType()));
}
return devices;
}
@Override
public boolean kickDevice(Long userId, String deviceId) {
UserDevice device = userDeviceMapper.selectByDeviceId(deviceId);
// 验证设备属于该用户
if (device == null || !device.getUserId().equals(userId)) {
return false;
}
// 删除设备记录
userDeviceMapper.deleteByDeviceId(deviceId);
// 从Redis中删除该设备的登录状态
String redisKey = "login:token:" + userId;
redisTemplate.delete(redisKey);
// 如果有WebSocket连接关闭连接
wsSessionManager.removeSession(device.getToken());
return true;
}
@Override
public boolean setCurrentDevice(Long userId, String deviceId) {
UserDevice device = userDeviceMapper.selectByDeviceId(deviceId);
if (device == null || !device.getUserId().equals(userId)) {
return false;
}
// 清除所有当前设备标记
userDeviceMapper.clearCurrentDevice(userId);
// 设置新的当前设备
userDeviceMapper.setCurrentDevice(deviceId);
return true;
}
@Override
public boolean deleteDevice(Long userId, String deviceId) {
UserDevice device = userDeviceMapper.selectByDeviceId(deviceId);
if (device == null || !device.getUserId().equals(userId)) {
return false;
}
// 不能删除当前设备
if (device.getIsCurrent() == 1) {
return false;
}
userDeviceMapper.deleteByDeviceId(deviceId);
return true;
}
@Override
public void updateDeviceActive(String token) {
if (token != null && !token.isEmpty()) {
userDeviceMapper.updateLastActiveAt(token);
}
}
@Override
public UserDeviceVO getDeviceByToken(String token) {
UserDevice device = userDeviceMapper.selectByToken(token);
if (device == null) {
return null;
}
UserDeviceVO vo = new UserDeviceVO();
BeanUtils.copyProperties(device, vo);
vo.setDeviceType(getDeviceTypeName(device.getDeviceType()));
vo.setIsOnline(device.getStatus() == 1);
vo.setIsCurrent(device.getIsCurrent() == 1);
return vo;
}
@Override
public int kickOtherDevices(Long userId, String currentDeviceId) {
List<UserDeviceVO> devices = userDeviceMapper.selectByUserId(userId);
int count = 0;
for (UserDeviceVO device : devices) {
if (!device.getDeviceId().equals(currentDeviceId)) {
// 踢掉设备
userDeviceMapper.deleteByDeviceId(device.getDeviceId());
// 清除登录状态
String redisKey = "login:token:" + userId;
redisTemplate.delete(redisKey);
// 关闭WebSocket连接
wsSessionManager.removeSession(device.getDeviceId());
count++;
}
}
return count;
}
/**
* 获取设备类型名称
*/
private String getDeviceTypeName(Integer deviceType) {
if (deviceType == null) {
return "未知";
}
switch (deviceType) {
case 1: return "Android";
case 2: return "iOS";
case 3: return "Web";
case 4: return "其他";
default: return "未知";
}
}
}