From 07ff9a5ff29eee75728538315f33dc17dd96da54 Mon Sep 17 00:00:00 2001 From: ziin Date: Thu, 11 Dec 2025 20:16:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(login):=20=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 KeyboardUserLoginLog 实体、Mapper、Service 及 XML,扩展 Apple 与普通登录接口,自动记录 IP、UA、平台、OS 及新用户标识。 --- .../keyborad/controller/UserController.java | 9 +- .../mapper/KeyboardUserLoginLogMapper.java | 12 +++ .../model/entity/KeyboardUserLoginLog.java | 89 +++++++++++++++++++ .../yolo/keyborad/service/IAppleService.java | 4 +- .../service/KeyboardUserLoginLogService.java | 23 +++++ .../yolo/keyborad/service/UserService.java | 3 +- .../service/impl/AppleServiceImpl.java | 58 ++++++++++-- .../impl/KeyboardUserLoginLogServiceImpl.java | 33 +++++++ .../service/impl/UserServiceImpl.java | 50 ++++++++++- .../mapper/KeyboardUserLoginLogMapper.xml | 23 +++++ 10 files changed, 286 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/yolo/keyborad/mapper/KeyboardUserLoginLogMapper.java create mode 100644 src/main/java/com/yolo/keyborad/model/entity/KeyboardUserLoginLog.java create mode 100644 src/main/java/com/yolo/keyborad/service/KeyboardUserLoginLogService.java create mode 100644 src/main/java/com/yolo/keyborad/service/impl/KeyboardUserLoginLogServiceImpl.java create mode 100644 src/main/resources/mapper/KeyboardUserLoginLogMapper.xml diff --git a/src/main/java/com/yolo/keyborad/controller/UserController.java b/src/main/java/com/yolo/keyborad/controller/UserController.java index ac0f14b..413e43d 100644 --- a/src/main/java/com/yolo/keyborad/controller/UserController.java +++ b/src/main/java/com/yolo/keyborad/controller/UserController.java @@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; @@ -47,8 +48,8 @@ public class UserController { @PostMapping("/appleLogin") @Operation(summary = "苹果登录", description = "苹果登录接口") @Parameter(name = "code", required = true, description = "苹果登录凭证", example = "123456") - public BaseResponse appleLogin(@RequestBody AppleLoginReq appleLoginReq) throws Exception { - return ResultUtils.success(appleService.login(appleLoginReq.getIdentityToken())); + public BaseResponse appleLogin(@RequestBody AppleLoginReq appleLoginReq, HttpServletRequest request) throws Exception { + return ResultUtils.success(appleService.login(appleLoginReq.getIdentityToken(), request)); } @GetMapping("/logout") @@ -60,8 +61,8 @@ public class UserController { @PostMapping("/login") @Operation(summary = "登录", description = "登录接口") - public BaseResponse login(@RequestBody UserLoginDTO userLoginDTO) { - return ResultUtils.success(userService.login(userLoginDTO)); + public BaseResponse login(@RequestBody UserLoginDTO userLoginDTO, HttpServletRequest request) { + return ResultUtils.success(userService.login(userLoginDTO, request)); } @PostMapping("/updateInfo") diff --git a/src/main/java/com/yolo/keyborad/mapper/KeyboardUserLoginLogMapper.java b/src/main/java/com/yolo/keyborad/mapper/KeyboardUserLoginLogMapper.java new file mode 100644 index 0000000..1c7026a --- /dev/null +++ b/src/main/java/com/yolo/keyborad/mapper/KeyboardUserLoginLogMapper.java @@ -0,0 +1,12 @@ +package com.yolo.keyborad.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yolo.keyborad.model.entity.KeyboardUserLoginLog; + +/* +* @author: ziin +* @date: 2025/12/11 20:09 +*/ + +public interface KeyboardUserLoginLogMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/model/entity/KeyboardUserLoginLog.java b/src/main/java/com/yolo/keyborad/model/entity/KeyboardUserLoginLog.java new file mode 100644 index 0000000..e7e590c --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/entity/KeyboardUserLoginLog.java @@ -0,0 +1,89 @@ +package com.yolo.keyborad.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.Date; +import lombok.Data; + +/* +* @author: ziin +* @date: 2025/12/11 20:09 +*/ + +@Schema +@Data +@TableName(value = "keyboard_user_login_log") +public class KeyboardUserLoginLog { + /** + * 主键 + */ + @TableId(value = "id", type = IdType.AUTO) + @Schema(description="主键") + private Long id; + + /** + * 用户 ID,关联到 keyboard_user 表 + */ + @TableField(value = "user_id") + @Schema(description="用户 ID,关联到 keyboard_user 表") + private Long userId; + + /** + * 登录时间 + */ + @TableField(value = "login_time") + @Schema(description="登录时间") + private Date loginTime; + + /** + * 登录的 IP 地址 + */ + @TableField(value = "ip_address") + @Schema(description="登录的 IP 地址") + private String ipAddress; + + /** + * 用户设备信息 + */ + @TableField(value = "device_info") + @Schema(description="用户设备信息") + private String deviceInfo; + + /** + * 操作系统 + */ + @TableField(value = "os") + @Schema(description="操作系统") + private String os; + + /** + * 设备平台:iOS 或 Android + */ + @TableField(value = "platform") + @Schema(description="设备平台:iOS 或 Android") + private String platform; + + /** + * 登录状态,成功或失败 + */ + @TableField(value = "\"status\"") + @Schema(description="登录状态,成功或失败") + private String status; + + /** + * 记录创建时间 + */ + @TableField(value = "created_at") + @Schema(description="记录创建时间") + private Date createdAt; + + /** + * 记录更新时间 + */ + @TableField(value = "updated_at") + @Schema(description="记录更新时间") + private Date updatedAt; +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/service/IAppleService.java b/src/main/java/com/yolo/keyborad/service/IAppleService.java index ab8640c..60adca3 100644 --- a/src/main/java/com/yolo/keyborad/service/IAppleService.java +++ b/src/main/java/com/yolo/keyborad/service/IAppleService.java @@ -1,6 +1,7 @@ package com.yolo.keyborad.service; import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO; +import jakarta.servlet.http.HttpServletRequest; /** * Apple相关API @@ -14,6 +15,7 @@ public interface IAppleService { * 登录 * * @param identityToken JWT身份令牌 + * @param request HTTP请求 */ - KeyboardUserRespVO login(String identityToken) throws Exception; + KeyboardUserRespVO login(String identityToken, HttpServletRequest request) throws Exception; } diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardUserLoginLogService.java b/src/main/java/com/yolo/keyborad/service/KeyboardUserLoginLogService.java new file mode 100644 index 0000000..51ce384 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/KeyboardUserLoginLogService.java @@ -0,0 +1,23 @@ +package com.yolo.keyborad.service; + +import com.yolo.keyborad.model.entity.KeyboardUserLoginLog; +import com.baomidou.mybatisplus.extension.service.IService; + /* +* @author: ziin +* @date: 2025/12/11 20:09 +*/ + +public interface KeyboardUserLoginLogService extends IService{ + + /** + * 记录用户登录信息 + * @param userId 用户ID + * @param ipAddress IP地址 + * @param deviceInfo 设备信息 + * @param os 操作系统 + * @param platform 平台(iOS/Android) + * @param status 登录状态 + */ + void recordLoginLog(Long userId, String ipAddress, String deviceInfo, String os, String platform, String status); + +} diff --git a/src/main/java/com/yolo/keyborad/service/UserService.java b/src/main/java/com/yolo/keyborad/service/UserService.java index 9691041..31fc8a9 100644 --- a/src/main/java/com/yolo/keyborad/service/UserService.java +++ b/src/main/java/com/yolo/keyborad/service/UserService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.yolo.keyborad.model.dto.user.*; import com.yolo.keyborad.model.entity.KeyboardUser; import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO; +import jakarta.servlet.http.HttpServletRequest; /* * @author: ziin @@ -15,7 +16,7 @@ public interface UserService extends IService { KeyboardUser createUserWithSubjectId(String sub); - KeyboardUserRespVO login(UserLoginDTO userLoginDTO); + KeyboardUserRespVO login(UserLoginDTO userLoginDTO, HttpServletRequest request); Boolean updateUserInfo(KeyboardUserReq keyboardUser); diff --git a/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java index 3357d7b..c2a4edf 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java @@ -11,11 +11,11 @@ import com.yolo.keyborad.exception.BusinessException; import com.yolo.keyborad.model.entity.KeyboardUser; import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO; import com.yolo.keyborad.service.IAppleService; -import com.yolo.keyborad.service.KeyboardCharacterService; +import com.yolo.keyborad.service.KeyboardUserLoginLogService; import com.yolo.keyborad.service.UserService; +import jakarta.servlet.http.HttpServletRequest; import io.jsonwebtoken.*; import jakarta.annotation.Resource; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -40,13 +40,17 @@ public class AppleServiceImpl implements IAppleService { @Resource private UserService userService; + @Resource + private KeyboardUserLoginLogService loginLogService; + /** * 登录 * * @param identityToken JWT身份令牌 + * @param request HTTP请求 */ @Override - public KeyboardUserRespVO login(String identityToken) throws Exception { + public KeyboardUserRespVO login(String identityToken, HttpServletRequest request) throws Exception { // 1. 清理一下 token,防止前后多了引号/空格 identityToken = identityToken.trim(); @@ -88,13 +92,51 @@ public class AppleServiceImpl implements IAppleService { // 返回用户标识符 if (result) { KeyboardUser user = userService.selectUserWithSubjectId(sub); + boolean isNewUser = false; if (user == null) { - KeyboardUser newUser = userService.createUserWithSubjectId(sub); - KeyboardUserRespVO keyboardUserRespVO = BeanUtil.copyProperties(newUser, KeyboardUserRespVO.class); - StpUtil.login(newUser.getId()); - keyboardUserRespVO.setToken(StpUtil.getTokenValueByLoginId(newUser.getId())); - return keyboardUserRespVO; + user = userService.createUserWithSubjectId(sub); + isNewUser = true; } + + // 记录登录日志 + try { + String ipAddress = request.getRemoteAddr(); + String userAgent = request.getHeader("User-Agent"); + String platform = "Unknown"; + String os = "Unknown"; + + if (userAgent != null) { + if (userAgent.contains("iOS")) { + platform = "iOS"; + } else if (userAgent.contains("Android")) { + platform = "Android"; + } + + if (userAgent.contains("Windows")) { + os = "Windows"; + } else if (userAgent.contains("Mac OS")) { + os = "Mac OS"; + } else if (userAgent.contains("Linux")) { + os = "Linux"; + } else if (userAgent.contains("iOS")) { + os = "iOS"; + } else if (userAgent.contains("Android")) { + os = "Android"; + } + } + + loginLogService.recordLoginLog( + user.getId(), + ipAddress, + userAgent, + os, + platform, + isNewUser ? "APPLE_NEW_USER" : "SUCCESS" + ); + } catch (Exception e) { + log.error("记录Apple登录日志失败", e); + } + KeyboardUserRespVO keyboardUserRespVO = BeanUtil.copyProperties(user, KeyboardUserRespVO.class); StpUtil.login(user.getId()); keyboardUserRespVO.setToken(StpUtil.getTokenValueByLoginId(user.getId())); diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardUserLoginLogServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardUserLoginLogServiceImpl.java new file mode 100644 index 0000000..6388ecd --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardUserLoginLogServiceImpl.java @@ -0,0 +1,33 @@ +package com.yolo.keyborad.service.impl; + +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yolo.keyborad.mapper.KeyboardUserLoginLogMapper; +import com.yolo.keyborad.model.entity.KeyboardUserLoginLog; +import com.yolo.keyborad.service.KeyboardUserLoginLogService; +/* +* @author: ziin +* @date: 2025/12/11 20:09 +*/ + +@Service +public class KeyboardUserLoginLogServiceImpl extends ServiceImpl implements KeyboardUserLoginLogService{ + + @Override + public void recordLoginLog(Long userId, String ipAddress, String deviceInfo, String os, String platform, String status) { + KeyboardUserLoginLog loginLog = new KeyboardUserLoginLog(); + loginLog.setUserId(userId); + loginLog.setIpAddress(ipAddress); + loginLog.setDeviceInfo(deviceInfo); + loginLog.setOs(os); + loginLog.setPlatform(platform); + loginLog.setStatus(status); + loginLog.setLoginTime(new java.util.Date()); + loginLog.setCreatedAt(new java.util.Date()); + loginLog.setUpdatedAt(new java.util.Date()); + this.save(loginLog); + } + +} diff --git a/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java index 4f1d01e..7a31c50 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java @@ -15,15 +15,14 @@ import com.yolo.keyborad.model.entity.KeyboardUser; import com.yolo.keyborad.model.entity.KeyboardUserWallet; import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO; import com.yolo.keyborad.service.KeyboardCharacterService; +import com.yolo.keyborad.service.KeyboardUserLoginLogService; import com.yolo.keyborad.service.KeyboardUserWalletService; import com.yolo.keyborad.service.UserService; +import jakarta.servlet.http.HttpServletRequest; import com.yolo.keyborad.utils.RedisUtil; import com.yolo.keyborad.utils.SendMailUtils; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.units.qual.K; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -58,6 +57,9 @@ public class UserServiceImpl extends ServiceImpl() .eq(KeyboardUser::getEmail, userLoginDTO.getMail()) @@ -101,6 +103,46 @@ public class UserServiceImpl extends ServiceImpl + + + + + + + + + + + + + + + + + + + id, user_id, login_time, ip_address, device_info, os, platform, "status", created_at, + updated_at + + \ No newline at end of file