feat(user): 展示用户最近使用的系统

This commit is contained in:
Zhou Weidong committed 2026-10-23 12:34:13 +08:00
1 parent 45e3d84f59
commit ab58641bbe
12 files changed
+212 -8

No files matched your search

@@ -294,6 +294,12 @@ public class AppAuthService {
/** 刷新令牌:轮换 refresh token,旧的立即失效。 */
@Transactional(rollbackFor = Exception.class)
public TokenRespVO refresh(String refreshToken) {
return refresh(refreshToken, null, null);
}
/** 刷新令牌,并在客户端带上系统信息时更新最近一次使用的系统。 */
@Transactional(rollbackFor = Exception.class)
public TokenRespVO refresh(String refreshToken, String platform, String osVersion) {
AppUserSession session = sessionMapper.selectOne(Wrappers.<AppUserSession>lambdaQuery()
.eq(AppUserSession::getRefreshTokenHash, hasher.sha256(refreshToken))
.last("limit 1"));
@@ -313,6 +319,12 @@ public class AppAuthService {
String nextRefreshToken = hasher.newRefreshToken();
session.setRefreshTokenHash(hasher.sha256(nextRefreshToken));
if (StringUtils.hasText(platform)) {
session.setPlatform(platform);
}
if (StringUtils.hasText(osVersion)) {
session.setOsVersion(osVersion);
}
session.setLastActiveAt(now);
session.setExpireAt(now.plus(tokenProvider.appRefreshTtl()));
sessionMapper.updateById(session);
@@ -11,9 +11,11 @@ import com.fruitshorts.common.pojo.PageResult;
import com.fruitshorts.user.dal.entity.AppUser;
import com.fruitshorts.user.dal.entity.AppUserBanRecord;
import com.fruitshorts.user.dal.entity.AppUserCredential;
import com.fruitshorts.user.dal.entity.AppUserSession;
import com.fruitshorts.user.dal.mapper.AppUserBanRecordMapper;
import com.fruitshorts.user.dal.mapper.AppUserCredentialMapper;
import com.fruitshorts.user.dal.mapper.AppUserMapper;
import com.fruitshorts.user.dal.mapper.AppUserSessionMapper;
import com.fruitshorts.user.enums.AppUserStatus;
import com.fruitshorts.user.vo.AppUserQueryReqVO;
import com.fruitshorts.user.vo.AppUserAdminRespVO;
@@ -26,7 +28,9 @@ import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 后台用户管理:检索、详情、状态处置。
@@ -40,6 +44,7 @@ public class AppUserAdminService {
private final AppUserMapper appUserMapper;
private final AppUserCredentialMapper credentialMapper;
private final AppUserBanRecordMapper banRecordMapper;
private final AppUserSessionMapper sessionMapper;
private final AppAuthService authService;
public PageResult<AppUserAdminRespVO> page(AppUserQueryReqVO req) {
@@ -74,7 +79,9 @@ public class AppUserAdminService {
req.getRegisterSource())
.orderByDesc(AppUser::getId));
}
return new PageResult<>(result.getTotal(), toRespList(result.getRecords()));
List<AppUserAdminRespVO> list = toRespList(result.getRecords());
fillLatestSystem(list);
return new PageResult<>(result.getTotal(), list);
}
public AppUserAdminRespVO detail(Long id) {
@@ -82,7 +89,9 @@ public class AppUserAdminService {
if (user == null) {
throw new ServiceException(ErrorCode.DATA_NOT_FOUND, "用户不存在");
}
return toAdminResp(user);
AppUserAdminRespVO vo = toAdminResp(user);
fillLatestSystem(List.of(vo));
return vo;
}
/** 修改账号状态。封禁/注销会立即踢下线。 */
@@ -154,4 +163,37 @@ public class AppUserAdminService {
vo.setId(user.getId());
return vo;
}
/** 用最近一次未吊销会话上的系统信息,供后台用户列表展示。 */
private void fillLatestSystem(List<AppUserAdminRespVO> list) {
if (list.isEmpty()) {
return;
}
List<Long> ids = new ArrayList<>(list.size());
for (AppUserAdminRespVO vo : list) {
if (vo.getId() != null) {
ids.add(vo.getId());
}
}
if (ids.isEmpty()) {
return;
}
List<AppUserSession> sessions = sessionMapper.selectList(Wrappers.<AppUserSession>lambdaQuery()
.in(AppUserSession::getUserId, ids)
.isNull(AppUserSession::getRevokedAt)
.orderByDesc(AppUserSession::getLastActiveAt)
.orderByDesc(AppUserSession::getId));
Map<Long, AppUserSession> latest = new HashMap<>();
for (AppUserSession session : sessions) {
latest.putIfAbsent(session.getUserId(), session);
}
for (AppUserAdminRespVO vo : list) {
AppUserSession session = latest.get(vo.getId());
if (session == null) {
continue;
}
vo.setPlatform(session.getPlatform());
vo.setOsVersion(session.getOsVersion());
}
}
}
@@ -16,4 +16,10 @@ import lombok.EqualsAndHashCode;
public class AppUserAdminRespVO extends AppUserRespVO {
private Long id;
/** 最近一次登录的系统:ANDROID / IOS / WEB。 */
private String platform;
/** 最近一次登录的系统版本。 */
private String osVersion;
}
@@ -11,4 +11,10 @@ public class RefreshReqVO {
@NotBlank(message = "缺少 refreshToken")
private String refreshToken;
/** 客户端系统:ANDROID / IOS / WEB。刷新时顺带更新,避免必须重新登录才有记录。 */
private String platform;
/** 系统版本,例如 Android 16。 */
private String osVersion;
}