feat: 个人信息页面.
This commit is contained in:
@@ -25,6 +25,8 @@ public interface Const extends com.orion.lang.constant.Const, FieldConst {
|
|||||||
|
|
||||||
Integer DEFAULT_SORT = 10;
|
Integer DEFAULT_SORT = 10;
|
||||||
|
|
||||||
|
int LOGIN_HISTORY_COUNT = 30;
|
||||||
|
|
||||||
Long NONE_ID = -1L;
|
Long NONE_ID = -1L;
|
||||||
|
|
||||||
Integer DEFAULT_VERSION = 1;
|
Integer DEFAULT_VERSION = 1;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public enum ErrorCode implements CodeInfo {
|
|||||||
|
|
||||||
// -------------------- 自定义 - 业务 --------------------
|
// -------------------- 自定义 - 业务 --------------------
|
||||||
|
|
||||||
OTHER_DEVICE_LOGIN(700, "该账号于 {} 已在其他设备登陆 {}({})"),
|
OTHER_DEVICE_LOGIN(700, "该账号于 {} 已在其他设备登录 {}({})"),
|
||||||
|
|
||||||
USER_DISABLED(701, "当前用户已禁用"),
|
USER_DISABLED(701, "当前用户已禁用"),
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public interface ErrorMessage {
|
|||||||
|
|
||||||
String USERNAME_PASSWORD_ERROR = "用户名或密码错误";
|
String USERNAME_PASSWORD_ERROR = "用户名或密码错误";
|
||||||
|
|
||||||
String MAX_LOGIN_FAILED = "登陆失败次数已上限";
|
String MAX_LOGIN_FAILED = "登录失败次数已上限";
|
||||||
|
|
||||||
String HISTORY_ABSENT = "历史值不存在";
|
String HISTORY_ABSENT = "历史值不存在";
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class PrettyLogPrinterInterceptor extends AbstractLogPrinterInterceptor {
|
|||||||
if (!Strings.isEmpty(summary)) {
|
if (!Strings.isEmpty(summary)) {
|
||||||
requestLog.append("\tsummary: ").append(summary).append('\n');
|
requestLog.append("\tsummary: ").append(summary).append('\n');
|
||||||
}
|
}
|
||||||
// 登陆用户
|
// 登录用户
|
||||||
Long loginUserId = securityHolder.getLoginUserId();
|
Long loginUserId = securityHolder.getLoginUserId();
|
||||||
if (loginUserId != null) {
|
if (loginUserId != null) {
|
||||||
requestLog.append("\tuser: ").append(loginUserId).append('\n');
|
requestLog.append("\tuser: ").append(loginUserId).append('\n');
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class RowLogPrinterInterceptor extends AbstractLogPrinterInterceptor impl
|
|||||||
if (!Strings.isEmpty(summary)) {
|
if (!Strings.isEmpty(summary)) {
|
||||||
fields.put(SUMMARY, summary);
|
fields.put(SUMMARY, summary);
|
||||||
}
|
}
|
||||||
// 登陆用户
|
// 登录用户
|
||||||
fields.put(USER, securityHolder.getLoginUserId());
|
fields.put(USER, securityHolder.getLoginUserId());
|
||||||
// http
|
// http
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
|
|||||||
@@ -106,6 +106,14 @@ public class DataQuery<T> {
|
|||||||
return then;
|
return then;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataQuery<T> limit(int limit) {
|
||||||
|
return this.last(Const.LIMIT + Const.SPACE + limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataQuery<T> limit(int offset, int limit) {
|
||||||
|
return this.last(Const.LIMIT + Const.SPACE + offset + Const.COMMA + limit);
|
||||||
|
}
|
||||||
|
|
||||||
public DataQuery<T> only() {
|
public DataQuery<T> only() {
|
||||||
return this.last(Const.LIMIT_1);
|
return this.last(Const.LIMIT_1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class SecurityUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前用户id
|
* 获取当前 userId
|
||||||
*
|
*
|
||||||
* @return id
|
* @return id
|
||||||
*/
|
*/
|
||||||
@@ -78,6 +78,16 @@ public class SecurityUtils {
|
|||||||
return loginUser != null ? loginUser.getId() : null;
|
return loginUser != null ? loginUser.getId() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 username
|
||||||
|
*
|
||||||
|
* @return username
|
||||||
|
*/
|
||||||
|
public static String getLoginUsername() {
|
||||||
|
LoginUser loginUser = getLoginUser();
|
||||||
|
return loginUser != null ? loginUser.getUsername() : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置当前用户
|
* 设置当前用户
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ orion:
|
|||||||
# 下面引用了 需要注意
|
# 下面引用了 需要注意
|
||||||
field:
|
field:
|
||||||
ignore:
|
ignore:
|
||||||
- password,newPassword,useNewPassword,publicKey,privateKey
|
- password,beforePassword,newPassword,useNewPassword,publicKey,privateKey
|
||||||
- metrics
|
- metrics
|
||||||
desensitize:
|
desensitize:
|
||||||
storage:
|
storage:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
### 登陆 - admin 用户
|
### 登录 - admin 用户
|
||||||
POST {{baseUrl}}/infra/auth/login
|
POST {{baseUrl}}/infra/auth/login
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ Content-Type: application/json
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
### 登陆
|
### 登录
|
||||||
POST {{baseUrl}}/infra/auth/login
|
POST {{baseUrl}}/infra/auth/login
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import com.orion.ops.module.infra.entity.request.user.UserLoginRequest;
|
|||||||
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
|
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
|
||||||
import com.orion.ops.module.infra.entity.vo.UserLoginVO;
|
import com.orion.ops.module.infra.entity.vo.UserLoginVO;
|
||||||
import com.orion.ops.module.infra.service.AuthenticationService;
|
import com.orion.ops.module.infra.service.AuthenticationService;
|
||||||
import com.orion.ops.module.infra.service.SystemUserService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -40,12 +39,9 @@ public class AuthenticationController {
|
|||||||
@Resource
|
@Resource
|
||||||
private AuthenticationService authenticationService;
|
private AuthenticationService authenticationService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private SystemUserService systemUserService;
|
|
||||||
|
|
||||||
@OperatorLog(AuthenticationOperatorType.LOGIN)
|
@OperatorLog(AuthenticationOperatorType.LOGIN)
|
||||||
@PermitAll
|
@PermitAll
|
||||||
@Operation(summary = "登陆")
|
@Operation(summary = "登录")
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public UserLoginVO login(@Validated @RequestBody UserLoginRequest request,
|
public UserLoginVO login(@Validated @RequestBody UserLoginRequest request,
|
||||||
HttpServletRequest servletRequest) {
|
HttpServletRequest servletRequest) {
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import com.orion.lang.define.wrapper.DataGrid;
|
|||||||
import com.orion.ops.framework.common.validator.group.Page;
|
import com.orion.ops.framework.common.validator.group.Page;
|
||||||
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||||
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
||||||
|
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||||
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
||||||
|
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||||
import com.orion.ops.module.infra.service.OperatorLogService;
|
import com.orion.ops.module.infra.service.OperatorLogService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -13,12 +15,10 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志 api
|
* 操作日志 api
|
||||||
@@ -47,5 +47,22 @@ public class OperatorLogController {
|
|||||||
return operatorLogService.getOperatorLogPage(request);
|
return operatorLogService.getOperatorLogPage(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixme 权限配置
|
||||||
|
|
||||||
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
|
@GetMapping("/login-history")
|
||||||
|
@Operation(summary = "查询用户登录日志")
|
||||||
|
public List<LoginHistoryVO> getLoginHistory(@RequestParam("username") String username) {
|
||||||
|
return operatorLogService.getLoginHistory(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
|
@GetMapping("/current-login-history")
|
||||||
|
@Operation(summary = "查询当前用户登录日志")
|
||||||
|
public List<LoginHistoryVO> getCurrentLoginHistory() {
|
||||||
|
String username = SecurityUtils.getLoginUsername();
|
||||||
|
return operatorLogService.getLoginHistory(username);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
|
|||||||
import com.orion.ops.framework.common.validator.group.Page;
|
import com.orion.ops.framework.common.validator.group.Page;
|
||||||
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||||
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
||||||
|
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||||
import com.orion.ops.module.infra.define.operator.SystemUserOperatorType;
|
import com.orion.ops.module.infra.define.operator.SystemUserOperatorType;
|
||||||
import com.orion.ops.module.infra.entity.request.user.*;
|
import com.orion.ops.module.infra.entity.request.user.*;
|
||||||
@@ -95,6 +96,20 @@ public class SystemUserController {
|
|||||||
return HttpWrapper.ok();
|
return HttpWrapper.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
|
@GetMapping("/get-current")
|
||||||
|
@Operation(summary = "查询当前用户信息")
|
||||||
|
public SystemUserVO getCurrentUserInfo() {
|
||||||
|
return systemUserService.getSystemUserById(SecurityUtils.getLoginUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update-current")
|
||||||
|
@Operation(summary = "更新当前用户信息")
|
||||||
|
public Integer updateCurrentUser(@Validated @RequestBody SystemUserUpdateRequest request) {
|
||||||
|
request.setId(SecurityUtils.getLoginUserId());
|
||||||
|
return systemUserService.updateSystemUserById(request);
|
||||||
|
}
|
||||||
|
|
||||||
@IgnoreLog(IgnoreLogMode.RET)
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "通过 id 查询用户")
|
@Operation(summary = "通过 id 查询用户")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.orion.ops.module.infra.convert;
|
|||||||
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
||||||
import com.orion.ops.module.infra.entity.domain.OperatorLogDO;
|
import com.orion.ops.module.infra.entity.domain.OperatorLogDO;
|
||||||
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
||||||
|
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
@@ -25,4 +26,6 @@ public interface OperatorLogConvert {
|
|||||||
|
|
||||||
OperatorLogVO to(OperatorLogDO domain);
|
OperatorLogVO to(OperatorLogDO domain);
|
||||||
|
|
||||||
|
LoginHistoryVO toLoginHistory(OperatorLogDO domain);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ public interface UserCacheKeyDefine {
|
|||||||
|
|
||||||
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyBuilder()
|
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyBuilder()
|
||||||
.key("user:failed:{}")
|
.key("user:failed:{}")
|
||||||
.desc("用户登陆失败次数 ${username}")
|
.desc("用户登录失败次数 ${username}")
|
||||||
.timeout(3, TimeUnit.DAYS)
|
.timeout(3, TimeUnit.DAYS)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
CacheKeyDefine LOGIN_TOKEN = new CacheKeyBuilder()
|
CacheKeyDefine LOGIN_TOKEN = new CacheKeyBuilder()
|
||||||
.key("user:token:{}:{}")
|
.key("user:token:{}:{}")
|
||||||
.desc("用户登陆 token ${id} ${time}")
|
.desc("用户登录 token ${id} ${time}")
|
||||||
.type(LoginTokenDTO.class)
|
.type(LoginTokenDTO.class)
|
||||||
.timeout(24, TimeUnit.HOURS)
|
.timeout(24, TimeUnit.HOURS)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class AuthenticationOperatorType extends InitializingOperatorTypes {
|
|||||||
@Override
|
@Override
|
||||||
public OperatorType[] types() {
|
public OperatorType[] types() {
|
||||||
return new OperatorType[]{
|
return new OperatorType[]{
|
||||||
new OperatorType(L, LOGIN, "登陆系统"),
|
new OperatorType(L, LOGIN, "登录系统"),
|
||||||
new OperatorType(L, LOGOUT, "登出系统"),
|
new OperatorType(L, LOGOUT, "登出系统"),
|
||||||
new OperatorType(L, UPDATE_PASSWORD, "修改密码"),
|
new OperatorType(L, UPDATE_PASSWORD, "修改密码"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆 token 缓存
|
* 登录 token 缓存
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
@@ -28,7 +28,7 @@ public class LoginTokenDTO {
|
|||||||
*
|
*
|
||||||
* @see com.orion.ops.module.infra.enums.LoginTokenStatusEnum
|
* @see com.orion.ops.module.infra.enums.LoginTokenStatusEnum
|
||||||
*/
|
*/
|
||||||
private Integer tokenStatus;
|
private Integer status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已续签次数
|
* 已续签次数
|
||||||
@@ -36,18 +36,43 @@ public class LoginTokenDTO {
|
|||||||
private Integer refreshCount;
|
private Integer refreshCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆时间/其他设备登陆时间
|
* 原始登录身份
|
||||||
*/
|
*/
|
||||||
private Long loginTime;
|
private Identity origin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆 ip/其他设备登陆 ip
|
* 覆盖登录身份
|
||||||
*/
|
*/
|
||||||
private String ip;
|
private Identity override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆地址/其他设备登陆地址
|
* 身份信息
|
||||||
*/
|
*/
|
||||||
private String location;
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Identity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始登录时间
|
||||||
|
*/
|
||||||
|
private Long loginTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前设备登录地址
|
||||||
|
*/
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前设备登录地址
|
||||||
|
*/
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前设备 userAgent
|
||||||
|
*/
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ public class OperatorLogQueryRequest extends PageRequest {
|
|||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
@Schema(description = "用户名")
|
||||||
|
private String username;
|
||||||
|
|
||||||
@Size(max = 32)
|
@Size(max = 32)
|
||||||
@Schema(description = "模块")
|
@Schema(description = "模块")
|
||||||
private String module;
|
private String module;
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import lombok.Data;
|
|||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆请求
|
* 登录请求
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2023/7/13 22:16
|
* @since 2023/7/13 22:16
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Schema(name = "UserLoginRequest", description = "登陆请求")
|
@Schema(name = "UserLoginRequest", description = "登录请求")
|
||||||
public class UserLoginRequest {
|
public class UserLoginRequest {
|
||||||
|
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.orion.ops.module.infra.entity.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录日志 视图响应对象
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023-10-10 17:08
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(name = "LoginHistoryVO", description = "登录日志 视图响应对象")
|
||||||
|
public class LoginHistoryVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "请求ip")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "请求地址")
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
@Schema(description = "userAgent")
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
@Schema(description = "操作结果 0失败 1成功")
|
||||||
|
private Integer result;
|
||||||
|
|
||||||
|
@Schema(description = "错误信息")
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import lombok.Data;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户登陆响应
|
* 用户登录响应
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
@@ -20,7 +20,7 @@ import lombok.NoArgsConstructor;
|
|||||||
@Schema(name = "SystemUserVO", description = "用户 视图响应对象")
|
@Schema(name = "SystemUserVO", description = "用户 视图响应对象")
|
||||||
public class UserLoginVO {
|
public class UserLoginVO {
|
||||||
|
|
||||||
@Schema(description = "登陆 token")
|
@Schema(description = "登录 token")
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.orion.ops.module.infra.enums;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆 token 状态
|
* 登录 token 状态
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
@@ -18,9 +18,9 @@ public enum LoginTokenStatusEnum {
|
|||||||
OK(0),
|
OK(0),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已在其他设备登陆
|
* 已在其他设备登录
|
||||||
*/
|
*/
|
||||||
OTHER_DEVICE(1, "已在其他设备登陆"),
|
OTHER_DEVICE(1, "已在其他设备登录"),
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
|||||||
}
|
}
|
||||||
// 检查 token 状态
|
// 检查 token 状态
|
||||||
this.checkTokenStatus(tokenInfo);
|
this.checkTokenStatus(tokenInfo);
|
||||||
// 获取登陆信息
|
// 获取登录信息
|
||||||
LoginUser user = authenticationService.getLoginUser(tokenInfo.getId());
|
LoginUser user = authenticationService.getLoginUser(tokenInfo.getId());
|
||||||
// 检查用户状态
|
// 检查用户状态
|
||||||
UserStatusEnum.checkUserStatus(user.getStatus());
|
UserStatusEnum.checkUserStatus(user.getStatus());
|
||||||
@@ -70,17 +70,18 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
|||||||
* @param loginToken loginToken
|
* @param loginToken loginToken
|
||||||
*/
|
*/
|
||||||
private void checkTokenStatus(LoginTokenDTO loginToken) {
|
private void checkTokenStatus(LoginTokenDTO loginToken) {
|
||||||
Integer tokenStatus = loginToken.getTokenStatus();
|
Integer tokenStatus = loginToken.getStatus();
|
||||||
// 正常状态
|
// 正常状态
|
||||||
if (LoginTokenStatusEnum.OK.getStatus().equals(tokenStatus)) {
|
if (LoginTokenStatusEnum.OK.getStatus().equals(tokenStatus)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 其他设备登陆
|
// 其他设备登录
|
||||||
if (LoginTokenStatusEnum.OTHER_DEVICE.getStatus().equals(tokenStatus)) {
|
if (LoginTokenStatusEnum.OTHER_DEVICE.getStatus().equals(tokenStatus)) {
|
||||||
|
LoginTokenDTO.Identity override = loginToken.getOverride();
|
||||||
throw ErrorCode.OTHER_DEVICE_LOGIN.exception(
|
throw ErrorCode.OTHER_DEVICE_LOGIN.exception(
|
||||||
Dates.format(new Date(loginToken.getLoginTime()), Dates.MD_HM),
|
Dates.format(new Date(override.getLoginTime()), Dates.MD_HM),
|
||||||
loginToken.getIp(),
|
override.getAddress(),
|
||||||
loginToken.getLocation());
|
override.getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
public interface AuthenticationService {
|
public interface AuthenticationService {
|
||||||
|
|
||||||
// TODO 配置化
|
// TODO 配置化
|
||||||
// 允许多端登陆
|
// 允许多端登录
|
||||||
boolean allowMultiDevice = true;
|
boolean allowMultiDevice = true;
|
||||||
// 允许凭证续签
|
// 允许凭证续签
|
||||||
boolean allowRefresh = true;
|
boolean allowRefresh = true;
|
||||||
@@ -28,7 +28,7 @@ public interface AuthenticationService {
|
|||||||
int maxFailedLoginCount = 5;
|
int maxFailedLoginCount = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆
|
* 登录
|
||||||
*
|
*
|
||||||
* @param request request
|
* @param request request
|
||||||
* @param servletRequest servletRequest
|
* @param servletRequest servletRequest
|
||||||
@@ -51,7 +51,7 @@ public interface AuthenticationService {
|
|||||||
void updatePassword(UserUpdatePasswordRequest request);
|
void updatePassword(UserUpdatePasswordRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取登陆用户信息
|
* 获取登录用户信息
|
||||||
*
|
*
|
||||||
* @param userId userId
|
* @param userId userId
|
||||||
* @return loginUser
|
* @return loginUser
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package com.orion.ops.module.infra.service;
|
|||||||
import com.orion.lang.define.wrapper.DataGrid;
|
import com.orion.lang.define.wrapper.DataGrid;
|
||||||
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
||||||
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
||||||
|
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志 服务类
|
* 操作日志 服务类
|
||||||
*
|
*
|
||||||
@@ -29,4 +32,12 @@ public interface OperatorLogService {
|
|||||||
*/
|
*/
|
||||||
DataGrid<OperatorLogVO> getOperatorLogPage(OperatorLogQueryRequest request);
|
DataGrid<OperatorLogVO> getOperatorLogPage(OperatorLogQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户登录日志
|
||||||
|
*
|
||||||
|
* @param username username
|
||||||
|
* @return rows
|
||||||
|
*/
|
||||||
|
List<LoginHistoryVO> getLoginHistory(String username);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserLoginVO login(UserLoginRequest request, HttpServletRequest servletRequest) {
|
public UserLoginVO login(UserLoginRequest request, HttpServletRequest servletRequest) {
|
||||||
// 登陆前检查
|
// 登录前检查
|
||||||
this.preCheckLogin(request);
|
this.preCheckLogin(request);
|
||||||
// 获取登陆用户
|
// 获取登录用户
|
||||||
LambdaQueryWrapper<SystemUserDO> wrapper = systemUserDAO.wrapper()
|
LambdaQueryWrapper<SystemUserDO> wrapper = systemUserDAO.wrapper()
|
||||||
.eq(SystemUserDO::getUsername, request.getUsername());
|
.eq(SystemUserDO::getUsername, request.getUsername());
|
||||||
SystemUserDO user = systemUserDAO.of(wrapper).getOne();
|
SystemUserDO user = systemUserDAO.of(wrapper).getOne();
|
||||||
@@ -90,17 +90,18 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
this.deleteUserCache(user);
|
this.deleteUserCache(user);
|
||||||
// 重设用户缓存
|
// 重设用户缓存
|
||||||
this.setUserCache(user);
|
this.setUserCache(user);
|
||||||
// 获取登陆 ip
|
// 获取登录信息
|
||||||
String remoteAddr = Servlets.getRemoteAddr(servletRequest);
|
String remoteAddr = Servlets.getRemoteAddr(servletRequest);
|
||||||
String location = IpUtils.getLocation(remoteAddr);
|
String location = IpUtils.getLocation(remoteAddr);
|
||||||
|
String userAgent = Servlets.getUserAgent(servletRequest);
|
||||||
long current = System.currentTimeMillis();
|
long current = System.currentTimeMillis();
|
||||||
// 不允许多端登陆
|
// 不允许多端登录
|
||||||
if (!allowMultiDevice) {
|
if (!allowMultiDevice) {
|
||||||
// 无效化其他缓存
|
// 无效化其他缓存
|
||||||
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location);
|
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location, userAgent);
|
||||||
}
|
}
|
||||||
// 生成 loginToken
|
// 生成 loginToken
|
||||||
String token = this.generatorLoginToken(user, current, remoteAddr, location);
|
String token = this.generatorLoginToken(user, current, remoteAddr, location, userAgent);
|
||||||
return UserLoginVO.builder()
|
return UserLoginVO.builder()
|
||||||
.token(token)
|
.token(token)
|
||||||
.build();
|
.build();
|
||||||
@@ -108,7 +109,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout(HttpServletRequest request) {
|
public void logout(HttpServletRequest request) {
|
||||||
// 获取登陆 token
|
// 获取登录 token
|
||||||
String loginToken = SecurityUtils.obtainAuthorization(request);
|
String loginToken = SecurityUtils.obtainAuthorization(request);
|
||||||
if (loginToken == null) {
|
if (loginToken == null) {
|
||||||
return;
|
return;
|
||||||
@@ -156,12 +157,12 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoginTokenDTO getLoginTokenInfo(String loginToken, boolean checkRefresh) {
|
public LoginTokenDTO getLoginTokenInfo(String loginToken, boolean checkRefresh) {
|
||||||
// 获取登陆 key pair
|
// 获取登录 key pair
|
||||||
Pair<Long, Long> pair = this.getLoginTokenPair(loginToken);
|
Pair<Long, Long> pair = this.getLoginTokenPair(loginToken);
|
||||||
if (pair == null) {
|
if (pair == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 获取登陆 key value
|
// 获取登录 key value
|
||||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(pair.getKey(), pair.getValue());
|
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(pair.getKey(), pair.getValue());
|
||||||
String loginCache = redisTemplate.opsForValue().get(loginKey);
|
String loginCache = redisTemplate.opsForValue().get(loginKey);
|
||||||
if (loginCache != null) {
|
if (loginCache != null) {
|
||||||
@@ -181,7 +182,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
LoginTokenDTO refresh = JSON.parseObject(refreshCache, LoginTokenDTO.class);
|
LoginTokenDTO refresh = JSON.parseObject(refreshCache, LoginTokenDTO.class);
|
||||||
int refreshCount = refresh.getRefreshCount() + 1;
|
int refreshCount = refresh.getRefreshCount() + 1;
|
||||||
refresh.setRefreshCount(refreshCount);
|
refresh.setRefreshCount(refreshCount);
|
||||||
// 设置登陆缓存
|
// 设置登录缓存
|
||||||
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, refresh);
|
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, refresh);
|
||||||
if (refreshCount < maxRefreshCount) {
|
if (refreshCount < maxRefreshCount) {
|
||||||
// 小于续签最大次数 则再次设置 refreshToken
|
// 小于续签最大次数 则再次设置 refreshToken
|
||||||
@@ -213,7 +214,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆预检查
|
* 登录预检查
|
||||||
*
|
*
|
||||||
* @param request request
|
* @param request request
|
||||||
*/
|
*/
|
||||||
@@ -222,7 +223,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
if (request.getPassword().length() != Const.MD5_LEN) {
|
if (request.getPassword().length() != Const.MD5_LEN) {
|
||||||
throw Exceptions.argument(ErrorMessage.USERNAME_PASSWORD_ERROR);
|
throw Exceptions.argument(ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||||
}
|
}
|
||||||
// 检查登陆失败次数
|
// 检查登录失败次数
|
||||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||||
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
|
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
|
||||||
if (failedCount != null && Integer.parseInt(failedCount) >= maxFailedLoginCount) {
|
if (failedCount != null && Integer.parseInt(failedCount) >= maxFailedLoginCount) {
|
||||||
@@ -243,7 +244,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
if (user != null && user.getPassword().equals(Signatures.md5(request.getPassword()))) {
|
if (user != null && user.getPassword().equals(Signatures.md5(request.getPassword()))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// 刷新登陆失败缓存
|
// 刷新登录失败缓存
|
||||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||||
Long failedLoginCount = redisTemplate.opsForValue().increment(failedCountKey);
|
Long failedLoginCount = redisTemplate.opsForValue().increment(failedCountKey);
|
||||||
RedisUtils.setExpire(failedCountKey, UserCacheKeyDefine.LOGIN_FAILED_COUNT);
|
RedisUtils.setExpire(failedCountKey, UserCacheKeyDefine.LOGIN_FAILED_COUNT);
|
||||||
@@ -293,7 +294,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
private void deleteUserCache(SystemUserDO user) {
|
private void deleteUserCache(SystemUserDO user) {
|
||||||
// 用户信息缓存
|
// 用户信息缓存
|
||||||
String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
|
String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
|
||||||
// 登陆失败次数缓存
|
// 登录失败次数缓存
|
||||||
String loginFailedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
String loginFailedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
||||||
// 删除缓存
|
// 删除缓存
|
||||||
redisTemplate.delete(Lists.of(userInfoKey, loginFailedCountKey));
|
redisTemplate.delete(Lists.of(userInfoKey, loginFailedCountKey));
|
||||||
@@ -324,34 +325,34 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 无效化其他登陆信息
|
* 无效化其他登录信息
|
||||||
*
|
*
|
||||||
* @param id id
|
* @param id id
|
||||||
* @param loginTime loginTime
|
* @param loginTime loginTime
|
||||||
* @param remoteAddr remoteAddr
|
* @param remoteAddr remoteAddr
|
||||||
* @param location location
|
* @param location location
|
||||||
|
* @param userAgent userAgent
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
private void invalidOtherDeviceToken(Long id, long loginTime, String remoteAddr, String location) {
|
private void invalidOtherDeviceToken(Long id, long loginTime,
|
||||||
|
String remoteAddr, String location, String userAgent) {
|
||||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*");
|
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*");
|
||||||
// 获取登陆信息
|
// 获取登录信息
|
||||||
Set<String> loginKeyList = RedisUtils.scanKeys(loginKey);
|
Set<String> loginKeyList = RedisUtils.scanKeys(loginKey);
|
||||||
if (!loginKeyList.isEmpty()) {
|
if (!loginKeyList.isEmpty()) {
|
||||||
// 获取有效登陆信息
|
// 获取有效登录信息
|
||||||
List<LoginTokenDTO> loginTokenInfoList = redisTemplate.opsForValue()
|
List<LoginTokenDTO> loginTokenInfoList = redisTemplate.opsForValue()
|
||||||
.multiGet(loginKeyList)
|
.multiGet(loginKeyList)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(s -> JSON.parseObject(s, LoginTokenDTO.class))
|
.map(s -> JSON.parseObject(s, LoginTokenDTO.class))
|
||||||
.filter(s -> LoginTokenStatusEnum.OK.getStatus().equals(s.getTokenStatus()))
|
.filter(s -> LoginTokenStatusEnum.OK.getStatus().equals(s.getStatus()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// 修改登陆信息
|
// 修改登录信息
|
||||||
for (LoginTokenDTO loginTokenInfo : loginTokenInfoList) {
|
for (LoginTokenDTO loginTokenInfo : loginTokenInfoList) {
|
||||||
String deviceLoginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTokenInfo.getLoginTime());
|
String deviceLoginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTokenInfo.getOrigin().getLoginTime());
|
||||||
loginTokenInfo.setTokenStatus(LoginTokenStatusEnum.OTHER_DEVICE.getStatus());
|
loginTokenInfo.setStatus(LoginTokenStatusEnum.OTHER_DEVICE.getStatus());
|
||||||
loginTokenInfo.setLoginTime(loginTime);
|
loginTokenInfo.setOverride(new LoginTokenDTO.Identity(loginTime, remoteAddr, location, userAgent));
|
||||||
loginTokenInfo.setIp(remoteAddr);
|
|
||||||
loginTokenInfo.setLocation(location);
|
|
||||||
RedisStrings.setJson(deviceLoginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginTokenInfo);
|
RedisStrings.setJson(deviceLoginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginTokenInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,20 +373,19 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||||||
* @param loginTime loginTime
|
* @param loginTime loginTime
|
||||||
* @param remoteAddr remoteAddr
|
* @param remoteAddr remoteAddr
|
||||||
* @param location location
|
* @param location location
|
||||||
|
* @param userAgent userAgent
|
||||||
* @return loginToken
|
* @return loginToken
|
||||||
*/
|
*/
|
||||||
private String generatorLoginToken(SystemUserDO user, long loginTime,
|
private String generatorLoginToken(SystemUserDO user, long loginTime,
|
||||||
String remoteAddr, String location) {
|
String remoteAddr, String location, String userAgent) {
|
||||||
Long id = user.getId();
|
Long id = user.getId();
|
||||||
// 生成 loginToken
|
// 生成 loginToken
|
||||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTime);
|
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTime);
|
||||||
LoginTokenDTO loginValue = LoginTokenDTO.builder()
|
LoginTokenDTO loginValue = LoginTokenDTO.builder()
|
||||||
.id(id)
|
.id(id)
|
||||||
.tokenStatus(LoginTokenStatusEnum.OK.getStatus())
|
.status(LoginTokenStatusEnum.OK.getStatus())
|
||||||
.refreshCount(0)
|
.refreshCount(0)
|
||||||
.ip(remoteAddr)
|
.origin(new LoginTokenDTO.Identity(loginTime, remoteAddr, location, userAgent))
|
||||||
.loginTime(loginTime)
|
|
||||||
.location(location)
|
|
||||||
.build();
|
.build();
|
||||||
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginValue);
|
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginValue);
|
||||||
// 生成 refreshToken
|
// 生成 refreshToken
|
||||||
|
|||||||
@@ -3,16 +3,20 @@ package com.orion.ops.module.infra.service.impl;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.orion.lang.define.wrapper.DataGrid;
|
import com.orion.lang.define.wrapper.DataGrid;
|
||||||
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
||||||
|
import com.orion.ops.framework.common.constant.Const;
|
||||||
import com.orion.ops.module.infra.convert.OperatorLogConvert;
|
import com.orion.ops.module.infra.convert.OperatorLogConvert;
|
||||||
import com.orion.ops.module.infra.dao.OperatorLogDAO;
|
import com.orion.ops.module.infra.dao.OperatorLogDAO;
|
||||||
|
import com.orion.ops.module.infra.define.operator.AuthenticationOperatorType;
|
||||||
import com.orion.ops.module.infra.entity.domain.OperatorLogDO;
|
import com.orion.ops.module.infra.entity.domain.OperatorLogDO;
|
||||||
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
||||||
|
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||||
import com.orion.ops.module.infra.service.OperatorLogService;
|
import com.orion.ops.module.infra.service.OperatorLogService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志 服务实现类
|
* 操作日志 服务实现类
|
||||||
@@ -46,6 +50,19 @@ public class OperatorLogServiceImpl implements OperatorLogService {
|
|||||||
.dataGrid(OperatorLogConvert.MAPPER::to);
|
.dataGrid(OperatorLogConvert.MAPPER::to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LoginHistoryVO> getLoginHistory(String username) {
|
||||||
|
// 条件
|
||||||
|
OperatorLogQueryRequest request = new OperatorLogQueryRequest();
|
||||||
|
request.setUsername(username);
|
||||||
|
request.setType(AuthenticationOperatorType.LOGIN);
|
||||||
|
LambdaQueryWrapper<OperatorLogDO> wrapper = this.buildQueryWrapper(request);
|
||||||
|
// 查询
|
||||||
|
return operatorLogDAO.of(wrapper)
|
||||||
|
.limit(Const.LOGIN_HISTORY_COUNT)
|
||||||
|
.list(OperatorLogConvert.MAPPER::toLoginHistory);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建查询 wrapper
|
* 构建查询 wrapper
|
||||||
*
|
*
|
||||||
@@ -55,12 +72,14 @@ public class OperatorLogServiceImpl implements OperatorLogService {
|
|||||||
private LambdaQueryWrapper<OperatorLogDO> buildQueryWrapper(OperatorLogQueryRequest request) {
|
private LambdaQueryWrapper<OperatorLogDO> buildQueryWrapper(OperatorLogQueryRequest request) {
|
||||||
return operatorLogDAO.wrapper()
|
return operatorLogDAO.wrapper()
|
||||||
.eq(OperatorLogDO::getUserId, request.getUserId())
|
.eq(OperatorLogDO::getUserId, request.getUserId())
|
||||||
|
.eq(OperatorLogDO::getUsername, request.getUsername())
|
||||||
.eq(OperatorLogDO::getRiskLevel, request.getRiskLevel())
|
.eq(OperatorLogDO::getRiskLevel, request.getRiskLevel())
|
||||||
.eq(OperatorLogDO::getModule, request.getModule())
|
.eq(OperatorLogDO::getModule, request.getModule())
|
||||||
.eq(OperatorLogDO::getType, request.getType())
|
.eq(OperatorLogDO::getType, request.getType())
|
||||||
.eq(OperatorLogDO::getResult, request.getResult())
|
.eq(OperatorLogDO::getResult, request.getResult())
|
||||||
.ge(OperatorLogDO::getStartTime, request.getStartTimeStart())
|
.ge(OperatorLogDO::getStartTime, request.getStartTimeStart())
|
||||||
.le(OperatorLogDO::getStartTime, request.getStartTimeEnd());
|
.le(OperatorLogDO::getStartTime, request.getStartTimeEnd())
|
||||||
|
.orderByDesc(OperatorLogDO::getId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class SystemUserServiceImpl implements SystemUserService {
|
|||||||
// 更新用户
|
// 更新用户
|
||||||
int effect = systemUserDAO.updateById(updateRecord);
|
int effect = systemUserDAO.updateById(updateRecord);
|
||||||
log.info("SystemUserService-updateUserStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
log.info("SystemUserService-updateUserStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
||||||
// 如果之前是锁定则删除登陆失败次数缓存
|
// 如果之前是锁定则删除登录失败次数缓存
|
||||||
if (UserStatusEnum.LOCKED.getStatus().equals(record.getStatus())) {
|
if (UserStatusEnum.LOCKED.getStatus().equals(record.getStatus())) {
|
||||||
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
||||||
}
|
}
|
||||||
@@ -224,9 +224,9 @@ public class SystemUserServiceImpl implements SystemUserService {
|
|||||||
update.setPassword(Signatures.md5(request.getPassword()));
|
update.setPassword(Signatures.md5(request.getPassword()));
|
||||||
int effect = systemUserDAO.updateById(update);
|
int effect = systemUserDAO.updateById(update);
|
||||||
log.info("SystemUserService-resetPassword record: {}, effect: {}", JSON.toJSONString(update), effect);
|
log.info("SystemUserService-resetPassword record: {}, effect: {}", JSON.toJSONString(update), effect);
|
||||||
// 删除登陆失败次数缓存
|
// 删除登录失败次数缓存
|
||||||
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
||||||
// 删除登陆缓存
|
// 删除登录缓存
|
||||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*");
|
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*");
|
||||||
Set<String> loginKeyList = RedisUtils.scanKeys(loginKey);
|
Set<String> loginKeyList = RedisUtils.scanKeys(loginKey);
|
||||||
if (!loginKeyList.isEmpty()) {
|
if (!loginKeyList.isEmpty()) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆请求
|
* 登录请求
|
||||||
*/
|
*/
|
||||||
export interface LoginRequest {
|
export interface LoginRequest {
|
||||||
username?: string;
|
username?: string;
|
||||||
@@ -9,7 +9,7 @@ export interface LoginRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆响应
|
* 登录响应
|
||||||
*/
|
*/
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
token: string;
|
token: string;
|
||||||
@@ -24,7 +24,7 @@ export interface UserUpdatePasswordRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登陆
|
* 登录
|
||||||
*/
|
*/
|
||||||
export function login(data: LoginRequest) {
|
export function login(data: LoginRequest) {
|
||||||
return axios.post<LoginResponse>('/infra/auth/login', data);
|
return axios.post<LoginResponse>('/infra/auth/login', data);
|
||||||
|
|||||||
75
orion-ops-ui/src/api/user/operator-log.ts
Normal file
75
orion-ops-ui/src/api/user/operator-log.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import type { DataGrid, Pagination } from '@/types/global';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志查询参数
|
||||||
|
*/
|
||||||
|
export interface OperatorLogQueryRequest extends Pagination {
|
||||||
|
userId?: number;
|
||||||
|
username?: string;
|
||||||
|
module?: string;
|
||||||
|
type?: string;
|
||||||
|
riskLevel?: string;
|
||||||
|
result?: number;
|
||||||
|
startTimeStart?: string;
|
||||||
|
startTimeEnd?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志查询响应
|
||||||
|
*/
|
||||||
|
export interface OperatorLogQueryResponse {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
username: string;
|
||||||
|
traceId: string;
|
||||||
|
address: string;
|
||||||
|
location: string;
|
||||||
|
userAgent: string;
|
||||||
|
riskLevel: string;
|
||||||
|
module: string;
|
||||||
|
type: string;
|
||||||
|
logInfo: string;
|
||||||
|
extra: string;
|
||||||
|
result: number;
|
||||||
|
errorMessage: string;
|
||||||
|
returnValue: string;
|
||||||
|
duration: number;
|
||||||
|
startTime: number;
|
||||||
|
endTime: number;
|
||||||
|
createTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录日志查询响应
|
||||||
|
*/
|
||||||
|
export interface LoginHistoryQueryResponse {
|
||||||
|
id: number;
|
||||||
|
address: string;
|
||||||
|
location: string;
|
||||||
|
userAgent: string;
|
||||||
|
result: number;
|
||||||
|
errorMessage: string;
|
||||||
|
createTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页操作日志
|
||||||
|
*/
|
||||||
|
export function getOperatorLogPage(request: OperatorLogQueryRequest) {
|
||||||
|
return axios.post<DataGrid<OperatorLogQueryResponse>>('/infra/operator-log/query', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询登录日志
|
||||||
|
*/
|
||||||
|
export function getLoginHistory(username: string) {
|
||||||
|
return axios.get<LoginHistoryQueryResponse[]>('/infra/operator-log/login-history', { params: { username } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前用户登录日志
|
||||||
|
*/
|
||||||
|
export function getCurrentLoginHistory() {
|
||||||
|
return axios.get<LoginHistoryQueryResponse[]>('/infra/operator-log/current-login-history');
|
||||||
|
}
|
||||||
@@ -12,8 +12,6 @@ export interface UserCreateRequest {
|
|||||||
avatar?: string;
|
avatar?: string;
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
status?: number;
|
|
||||||
lastLoginTime?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +91,20 @@ export function resetUserPassword(request: UserUpdateRequest) {
|
|||||||
return axios.put('/infra/system-user/reset-password', request);
|
return axios.put('/infra/system-user/reset-password', request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前用户
|
||||||
|
*/
|
||||||
|
export function getCurrentUser() {
|
||||||
|
return axios.get<UserQueryResponse>('/infra/system-user/get-current');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新当前用户
|
||||||
|
*/
|
||||||
|
export function updateCurrentUser(request: UserUpdateRequest) {
|
||||||
|
return axios.put('/infra/system-user/update-current', request);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过 id 查询用户
|
* 通过 id 查询用户
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -154,7 +154,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<!-- 个人中心 -->
|
<!-- 个人中心 -->
|
||||||
<a-doption>
|
<a-doption>
|
||||||
<a-space @click="$router.push({ name: 'userMine' })">
|
<a-space @click="$router.push({ name: 'userInfo' })">
|
||||||
<icon-user />
|
<icon-user />
|
||||||
<span>个人中心</span>
|
<span>个人中心</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ const USER: AppRouteRecordRaw = {
|
|||||||
component: () => import('@/views/user/user/index.vue'),
|
component: () => import('@/views/user/user/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'userMine',
|
name: 'userInfo',
|
||||||
path: '/user/mine',
|
path: '/user/info',
|
||||||
component: () => import('@/views/user/mine/index.vue'),
|
component: () => import('@/views/user/info/index.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ export default defineStore('user', {
|
|||||||
username: loginForm.username,
|
username: loginForm.username,
|
||||||
password: md5(loginForm.password as string),
|
password: md5(loginForm.password as string),
|
||||||
};
|
};
|
||||||
// 执行登陆
|
// 执行登录
|
||||||
const res = await userLogin(loginRequest);
|
const res = await userLogin(loginRequest);
|
||||||
// 设置登陆 token
|
// 设置登录 token
|
||||||
setToken(res.data.token);
|
setToken(res.data.token);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
clearToken();
|
clearToken();
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
if (!errors) {
|
if (!errors) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
// 执行登陆
|
// 执行登录
|
||||||
await userStore.login(values);
|
await userStore.login(values);
|
||||||
// 跳转路由
|
// 跳转路由
|
||||||
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
||||||
|
|||||||
@@ -76,6 +76,10 @@
|
|||||||
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||||
@page-size-change="(size) => fetchTableData(1, size)"
|
@page-size-change="(size) => fetchTableData(1, size)"
|
||||||
:bordered="false">
|
:bordered="false">
|
||||||
|
<!-- 配置项 -->
|
||||||
|
<template #keyName="{record}">
|
||||||
|
{{ record.keyName }}<span style="margin: 0 4px;">-</span>{{ record.keyDescription }}
|
||||||
|
</template>
|
||||||
<!-- 值 -->
|
<!-- 值 -->
|
||||||
<template #value="{ record }">
|
<template #value="{ record }">
|
||||||
<span class="copy-left" title="复制" @click="copy(record.value)">
|
<span class="copy-left" title="复制" @click="copy(record.value)">
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ const columns = [
|
|||||||
slotName: 'keyName',
|
slotName: 'keyName',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true
|
||||||
render: ({ record }) => {
|
|
||||||
return `${record.keyName} - ${record.keyDescription}`;
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
title: '配置描述',
|
title: '配置描述',
|
||||||
dataIndex: 'label',
|
dataIndex: 'label',
|
||||||
|
|||||||
131
orion-ops-ui/src/views/user/info/components/login-history.vue
Normal file
131
orion-ops-ui/src/views/user/info/components/login-history.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" class="main-container">
|
||||||
|
<span class="extra-message">只展示最近登录的 30 条历史记录</span>
|
||||||
|
<a-timeline>
|
||||||
|
<a-timeline-item v-for="item in list"
|
||||||
|
:key="item.id">
|
||||||
|
<!-- 图标 -->
|
||||||
|
<template #dot>
|
||||||
|
<div class="icon-container">
|
||||||
|
<icon-desktop />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 日志行 -->
|
||||||
|
<div class="log-line">
|
||||||
|
<!-- 地址行 -->
|
||||||
|
<span class="address-line">
|
||||||
|
<span class="mr8">{{ item.address }}</span>
|
||||||
|
<span>{{ item.location }}</span>
|
||||||
|
</span>
|
||||||
|
<!-- 错误信息行 -->
|
||||||
|
<span class="error-line" v-if="item.result === ResultStatus.FAILED">
|
||||||
|
登录失败: {{ item.errorMessage }}
|
||||||
|
</span>
|
||||||
|
<!-- 时间行 -->
|
||||||
|
<span class="time-line">
|
||||||
|
{{ dateFormat(new Date(item.createTime)) }}
|
||||||
|
</span>
|
||||||
|
<!-- ua -->
|
||||||
|
<span class="ua-line">
|
||||||
|
{{ item.userAgent }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-timeline-item>
|
||||||
|
</a-timeline>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'login-history'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { LoginHistoryQueryResponse } from '@/api/user/operator-log';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useUserStore } from '@/store';
|
||||||
|
import { ResultStatus } from '../types/const';
|
||||||
|
import { getCurrentLoginHistory } from '@/api/user/operator-log';
|
||||||
|
import { dateFormat } from '@/utils';
|
||||||
|
|
||||||
|
const list = ref<LoginHistoryQueryResponse[]>([]);
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
|
||||||
|
// 查询操作日志
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { data } = await getCurrentLoginHistory();
|
||||||
|
list.value = data;
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.main-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200px;
|
||||||
|
padding-left: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-message {
|
||||||
|
margin-bottom: 38px;
|
||||||
|
margin-left: -20px;
|
||||||
|
display: block;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: var(--color-fill-4);
|
||||||
|
font-size: 28px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-timeline-item-content-wrapper) {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 44px;
|
||||||
|
margin-top: -22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-timeline-item) {
|
||||||
|
padding-bottom: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.address-line {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-line, .ua-line, .error-line {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-line {
|
||||||
|
color: rgb(var(--danger-6));
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
112
orion-ops-ui/src/views/user/info/components/user-info.vue
Normal file
112
orion-ops-ui/src/views/user/info/components/user-info.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" style="width: 400px;">
|
||||||
|
<!-- 头像 -->
|
||||||
|
<div class="avatar-container">
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<a-avatar :size="88"
|
||||||
|
:style="{ backgroundColor: '#3370ff' }">
|
||||||
|
{{ nickname }}
|
||||||
|
</a-avatar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="right"
|
||||||
|
size="medium"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
:label-col-props="{ span: 6 }"
|
||||||
|
:wrapper-col-props="{ span: 18 }"
|
||||||
|
:rules="formRules">
|
||||||
|
<!-- 用户名 -->
|
||||||
|
<a-form-item field="username" label="用户名">
|
||||||
|
<a-input v-model="formModel.username" disabled />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 花名 -->
|
||||||
|
<a-form-item field="nickname" label="花名">
|
||||||
|
<a-input v-model="formModel.nickname" placeholder="请输入花名" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 手机号 -->
|
||||||
|
<a-form-item field="mobile" label="手机号">
|
||||||
|
<a-input v-model="formModel.mobile" placeholder="请输入手机号" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 邮箱 -->
|
||||||
|
<a-form-item field="email" label="邮箱">
|
||||||
|
<a-input v-model="formModel.email" placeholder="请输入邮箱" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<div class="handler-container">
|
||||||
|
<a-button type="primary" @click="save">保存</a-button>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'user-info'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { UserUpdateRequest } from '@/api/user/user';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import { computed, ref, onMounted } from 'vue';
|
||||||
|
import formRules from '../../user/types/form.rules';
|
||||||
|
import { useUserStore } from '@/store';
|
||||||
|
import { getCurrentUser, updateCurrentUser } from '@/api/user/user';
|
||||||
|
import { pick } from 'lodash';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const formModel = ref<UserUpdateRequest>({});
|
||||||
|
|
||||||
|
// 用户名
|
||||||
|
const nickname = computed(() => userStore.nickname?.substring(0, 1));
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
const save = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await updateCurrentUser(formModel.value);
|
||||||
|
userStore.nickname = formModel.value.nickname;
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载用户信息
|
||||||
|
onMounted(async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data } = await getCurrentUser();
|
||||||
|
formModel.value = pick(data, 'id', 'username', 'nickname', 'mobile', 'email');
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.avatar-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 4px 0;
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
width: calc(100% / 24 * 18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-container {
|
||||||
|
display: flex;
|
||||||
|
margin-left: calc(100% / 24 * 6);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
orion-ops-ui/src/views/user/info/index.vue
Normal file
55
orion-ops-ui/src/views/user/info/index.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tabs-container">
|
||||||
|
<a-tabs type="rounded"
|
||||||
|
size="medium"
|
||||||
|
position="left"
|
||||||
|
:lazy-load="true"
|
||||||
|
:destroy-on-hide="true">
|
||||||
|
<!-- 个人信息 -->
|
||||||
|
<a-tab-pane key="1" title="个人信息">
|
||||||
|
<user-info />
|
||||||
|
</a-tab-pane>
|
||||||
|
<!-- 登录日志 -->
|
||||||
|
<a-tab-pane key="2" title="登录日志">
|
||||||
|
<login-history />
|
||||||
|
</a-tab-pane>
|
||||||
|
<!-- 登录设备 -->
|
||||||
|
<a-tab-pane key="3" title="登录设备">
|
||||||
|
<login-history />
|
||||||
|
</a-tab-pane>
|
||||||
|
<!-- 操作日志 -->
|
||||||
|
<a-tab-pane key="4" title="操作日志">
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'userInfo'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import UserInfo from './components/user-info.vue';
|
||||||
|
import LoginHistory from './components/login-history.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.tabs-container {
|
||||||
|
background: #FFFFFF;
|
||||||
|
margin: 16px 16px 0 16px;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tabs-pane) {
|
||||||
|
border-left: 1px var(--color-neutral-3) solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tabs-tab-title) {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
7
orion-ops-ui/src/views/user/info/types/const.ts
Normal file
7
orion-ops-ui/src/views/user/info/types/const.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// 结果状态
|
||||||
|
export const ResultStatus = {
|
||||||
|
// 失败
|
||||||
|
FAILED: 0,
|
||||||
|
// 成功
|
||||||
|
SUCCESS: 1,
|
||||||
|
};
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-row class="layout-container">
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-card class="general-card">
|
|
||||||
<template #title>
|
|
||||||
用户信息
|
|
||||||
</template>
|
|
||||||
<a-spin :loading="loading" style="width: 100%">
|
|
||||||
<a-form :model="formModel"
|
|
||||||
ref="formRef"
|
|
||||||
label-align="right"
|
|
||||||
:label-col-props="{ span: 6 }"
|
|
||||||
:wrapper-col-props="{ span: 16 }"
|
|
||||||
:rules="formRules">
|
|
||||||
<!-- 用户名 -->
|
|
||||||
<a-form-item field="username" label="用户名">
|
|
||||||
<a-input v-model="formModel.username" disabled />
|
|
||||||
</a-form-item>
|
|
||||||
<!-- 花名 -->
|
|
||||||
<a-form-item field="nickname" label="花名">
|
|
||||||
<a-input v-model="formModel.nickname" placeholder="请输入花名" />
|
|
||||||
</a-form-item>
|
|
||||||
<!-- 手机号 -->
|
|
||||||
<a-form-item field="mobile" label="手机号">
|
|
||||||
<a-input v-model="formModel.mobile" placeholder="请输入手机号" />
|
|
||||||
</a-form-item>
|
|
||||||
<!-- 邮箱 -->
|
|
||||||
<a-form-item field="email" label="邮箱">
|
|
||||||
<a-input v-model="formModel.email" placeholder="请输入邮箱" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-spin>
|
|
||||||
</a-card>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { UserUpdateRequest } from '@/api/user/user';
|
|
||||||
import useLoading from '@/hooks/loading';
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import formRules from '../user/types/form.rules';
|
|
||||||
|
|
||||||
const { loading, setLoading } = useLoading();
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const formModel = ref<UserUpdateRequest>({});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -114,7 +114,7 @@ INSERT INTO `dict_value` VALUES (60, 1, 'operatorLogModule', 'infra:dict-value',
|
|||||||
INSERT INTO `dict_value` VALUES (61, 1, 'operatorLogModule', 'asset:host', '主机操作', '{}', 2000, '2023-10-31 10:48:16', '2023-10-31 10:53:54', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (61, 1, 'operatorLogModule', 'asset:host', '主机操作', '{}', 2000, '2023-10-31 10:48:16', '2023-10-31 10:53:54', '1', '1', 0);
|
||||||
INSERT INTO `dict_value` VALUES (62, 1, 'operatorLogModule', 'asset:host-key', '主机秘钥', '{}', 2020, '2023-10-31 10:48:17', '2023-10-31 10:54:34', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (62, 1, 'operatorLogModule', 'asset:host-key', '主机秘钥', '{}', 2020, '2023-10-31 10:48:17', '2023-10-31 10:54:34', '1', '1', 0);
|
||||||
INSERT INTO `dict_value` VALUES (63, 1, 'operatorLogModule', 'asset:host-identity', '主机身份', '{}', 2030, '2023-10-31 10:48:19', '2023-10-31 10:54:26', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (63, 1, 'operatorLogModule', 'asset:host-identity', '主机身份', '{}', 2030, '2023-10-31 10:48:19', '2023-10-31 10:54:26', '1', '1', 0);
|
||||||
INSERT INTO `dict_value` VALUES (64, 2, 'operatorLogType', 'authentication:login', '登陆系统', '{}', 10, '2023-10-31 10:55:26', '2023-10-31 11:05:41', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (64, 2, 'operatorLogType', 'authentication:login', '登录系统', '{}', 10, '2023-10-31 10:55:26', '2023-10-31 11:05:41', '1', '1', 0);
|
||||||
INSERT INTO `dict_value` VALUES (65, 2, 'operatorLogType', 'authentication:logout', '登出系统', '{}', 20, '2023-10-31 10:55:27', '2023-10-31 11:05:41', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (65, 2, 'operatorLogType', 'authentication:logout', '登出系统', '{}', 20, '2023-10-31 10:55:27', '2023-10-31 11:05:41', '1', '1', 0);
|
||||||
INSERT INTO `dict_value` VALUES (66, 2, 'operatorLogType', 'authentication:update-password', '修改密码', '{}', 30, '2023-10-31 10:55:30', '2023-10-31 11:05:41', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (66, 2, 'operatorLogType', 'authentication:update-password', '修改密码', '{}', 30, '2023-10-31 10:55:30', '2023-10-31 11:05:41', '1', '1', 0);
|
||||||
INSERT INTO `dict_value` VALUES (67, 2, 'operatorLogType', 'system-user:create', '创建用户', '{}', 10, '2023-10-31 10:55:42', '2023-10-31 11:05:41', '1', '1', 0);
|
INSERT INTO `dict_value` VALUES (67, 2, 'operatorLogType', 'system-user:create', '创建用户', '{}', 10, '2023-10-31 10:55:42', '2023-10-31 11:05:41', '1', '1', 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user