添加用户登录历史记录表

This commit is contained in:
2023-11-24 09:35:02 +08:00
parent 8f711031bb
commit 9c3d8b3540
15 changed files with 638 additions and 75 deletions

View File

@@ -7,6 +7,7 @@ import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@UtilityClass
@@ -16,6 +17,9 @@ public class Ctx {
private static ConfigurableApplicationContext context;
public static String getUserId() {
if (!isLoggedIn()) {
return null;
}
return getAuthentication().getUser().getId();
}
@@ -25,26 +29,37 @@ public class Ctx {
}
public static String getSessionId() {
if (!isLoggedIn()) {
return null;
}
return getAuthentication().getSessionId();
}
private static SessionService.MyAuthentication getAuthentication() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if( ! (authentication instanceof SessionService.MyAuthentication))
return null;
return (SessionService.MyAuthentication)
SecurityContextHolder.getContext().getAuthentication();
authentication;
}
public static boolean isOrgAdmin() {
return getAuthentication().getUser().getOrgAdmin();
return isLoggedIn() && getAuthentication().getUser().getOrgAdmin();
}
public static boolean isSysAdmin() {
return getAuthentication().getUser().getSysAdmin();
return isLoggedIn() && getAuthentication().getUser().getSysAdmin();
}
public static UserDto currentUser() {
return getAuthentication().getUser();
if (!isLoggedIn()) {
return null;
}
return getAuthentication().getUser();
}
public static void setContext(ConfigurableApplicationContext context) {

View File

@@ -52,7 +52,7 @@ public class LoginUserController {
@PostMapping("/logout")
public Boolean logout(@RequestBody IdRequest request) {
this.service.logout(request);
this.service.adminLogout(request);
return true;
}
}

View File

@@ -11,6 +11,7 @@ import cn.lihongjie.coal.loginUser.dto.UpdateLoginUserDto;
import cn.lihongjie.coal.loginUser.entity.LoginUserEntity;
import cn.lihongjie.coal.loginUser.mapper.LoginUserMapper;
import cn.lihongjie.coal.loginUser.repository.LoginUserRepository;
import cn.lihongjie.coal.loginUserHis.service.LoginUserHisService;
import cn.lihongjie.coal.session.SessionService;
import cn.lihongjie.coal.sysconfig.service.SysConfigService;
import cn.lihongjie.coal.user.service.UserService;
@@ -144,6 +145,8 @@ class LoginUserService extends BaseService<LoginUserEntity, LoginUserRepository>
this.save(entity);
}
@Autowired LoginUserHisService loginUserHisService;
public void deleteExpireSession() {
List<LoginUserEntity> list = this.repository.findAllByExpireTimeBefore(LocalDateTime.now());
@@ -151,6 +154,7 @@ class LoginUserService extends BaseService<LoginUserEntity, LoginUserRepository>
log.info("删除过期会话 {} 个", list.size());
for (LoginUserEntity user : list) {
this.deleteLogin(user.getId());
loginUserHisService.logExpired(user.getId());
}
}
}
@@ -232,4 +236,11 @@ class LoginUserService extends BaseService<LoginUserEntity, LoginUserRepository>
}
}
}
public void adminLogout(IdRequest request) {
logout(request);
loginUserHisService.logAdminLogout(request.getIds());
}
}

View File

@@ -0,0 +1,60 @@
package cn.lihongjie.coal.loginUserHis.controller;
import cn.lihongjie.coal.annotation.SysLog;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.loginUserHis.dto.CreateLoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.dto.LoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.dto.UpdateLoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.service.LoginUserHisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/loginUserHis")
@SysLog(
module = "用户登录历史"
)
@Slf4j
public class LoginUserHisController {
@Autowired
private LoginUserHisService service;
@PostMapping("/create")
public LoginUserHisDto create(@RequestBody CreateLoginUserHisDto request) {
return this.service.create(request)
;
}
@PostMapping("/update")
public LoginUserHisDto update(@RequestBody UpdateLoginUserHisDto request) {
return this.service.update(request)
;
}
@PostMapping("/delete")
public Object delete(@RequestBody IdRequest request) {
this.service.delete(request);
return true
;
}
@PostMapping("/getById")
public LoginUserHisDto getById(@RequestBody IdRequest request) {
return this.service.getById(request.getId())
;
}
@PostMapping("/list")
public Page<LoginUserHisDto> list(@RequestBody CommonQuery request) {
return this.service.list(request)
;
}
}

View File

@@ -0,0 +1,56 @@
package cn.lihongjie.coal.loginUserHis.dto;
import cn.lihongjie.coal.base.dto.CommonDto;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.Formula;
import java.time.LocalDateTime;
@Data
public class CreateLoginUserHisDto extends CommonDto {
private String user;
private String userName;
private String ip;
private String location;
private String ua;
private String captcha;
@Comment("登录状态")
private String status;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.status'\n"
+ " and i.code = status)")
private String statusName;
@Comment("session id")
private String sessionId;
@Comment("登录时间")
private LocalDateTime loginTime;
@Comment("登出时间")
private LocalDateTime logoutTime;
@Comment("登出类型")
private String logoutType;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.logoutType'\n"
+ " and i.code = logout_type)")
private String logoutTypeName;
}

View File

@@ -0,0 +1,60 @@
package cn.lihongjie.coal.loginUserHis.dto;
import cn.lihongjie.coal.base.dto.CommonDto;
import cn.lihongjie.coal.user.dto.UserDto;
import jakarta.persistence.ManyToOne;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.Formula;
import java.time.LocalDateTime;
@Data
public class LoginUserHisDto extends CommonDto {
@ManyToOne
private UserDto user;
private String userName;
private String ip;
private String location;
private String ua;
private String captcha;
@Comment("登录状态")
private String status;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.status'\n"
+ " and i.code = status)")
private String statusName;
@Comment("session id")
private String sessionId;
@Comment("登录时间")
private LocalDateTime loginTime;
@Comment("登出时间")
private LocalDateTime logoutTime;
@Comment("登出类型")
private String logoutType;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.logoutType'\n"
+ " and i.code = logout_type)")
private String logoutTypeName;
}

View File

@@ -0,0 +1,55 @@
package cn.lihongjie.coal.loginUserHis.dto;
import cn.lihongjie.coal.base.dto.CommonDto;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.Formula;
import java.time.LocalDateTime;
@Data
public class UpdateLoginUserHisDto extends CommonDto {
private String user;
private String userName;
private String ip;
private String location;
private String ua;
private String captcha;
@Comment("登录状态")
private String status;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.status'\n"
+ " and i.code = status)")
private String statusName;
@Comment("session id")
private String sessionId;
@Comment("登录时间")
private LocalDateTime loginTime;
@Comment("登出时间")
private LocalDateTime logoutTime;
@Comment("登出类型")
private String logoutType;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.logoutType'\n"
+ " and i.code = logout_type)")
private String logoutTypeName;
}

View File

@@ -0,0 +1,72 @@
package cn.lihongjie.coal.loginUserHis.entity;
import cn.lihongjie.coal.base.entity.CommonEntity;
import cn.lihongjie.coal.user.entity.UserEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.Formula;
import java.time.LocalDateTime;
@Data
@Entity
public class LoginUserHisEntity extends CommonEntity {
@ManyToOne
private UserEntity user;
private String userName;
private String ip;
private String location;
private String ua;
private String captcha;
@Comment("登录状态")
private String loginStatus;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.loginStatus'\n"
+ " and i.code = login_status)")
private String loginStatusName;
@Comment("session id")
private String sessionId;
@Comment("登录时间")
private LocalDateTime loginTime;
@Comment("登出时间")
private LocalDateTime logoutTime;
@Comment("登出类型")
private String logoutType;
@Formula(
"(select i.name\n"
+ "from t_dictionary d,\n"
+ " t_dictionary_item i\n"
+ "where d.id = i.dictionary_id\n"
+ " and d.code = 'loginUserHis.logoutType'\n"
+ " and i.code = logout_type)")
private String logoutTypeName;
}

View File

@@ -0,0 +1,19 @@
package cn.lihongjie.coal.loginUserHis.mapper;
import cn.lihongjie.coal.base.mapper.BaseMapper;
import cn.lihongjie.coal.base.mapper.CommonMapper;
import cn.lihongjie.coal.loginUserHis.dto.CreateLoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.dto.LoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.dto.UpdateLoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.entity.LoginUserHisEntity;
import org.mapstruct.Mapper;
import org.mapstruct.control.DeepClone;
@Mapper(
componentModel = org.mapstruct.MappingConstants.ComponentModel.SPRING,
uses = {CommonMapper.class},
mappingControl = DeepClone.class
)
public interface LoginUserHisMapper extends BaseMapper<LoginUserHisEntity, LoginUserHisDto, CreateLoginUserHisDto, UpdateLoginUserHisDto> {
}

View File

@@ -0,0 +1,10 @@
package cn.lihongjie.coal.loginUserHis.repository;
import cn.lihongjie.coal.base.dao.BaseRepository;
import cn.lihongjie.coal.loginUserHis.entity.LoginUserHisEntity;
import org.springframework.stereotype.Repository;
@Repository
public interface LoginUserHisRepository extends BaseRepository<LoginUserHisEntity> {
}

View File

@@ -0,0 +1,124 @@
package cn.lihongjie.coal.loginUserHis.service;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.loginUserHis.dto.CreateLoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.dto.LoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.dto.UpdateLoginUserHisDto;
import cn.lihongjie.coal.loginUserHis.entity.LoginUserHisEntity;
import cn.lihongjie.coal.loginUserHis.mapper.LoginUserHisMapper;
import cn.lihongjie.coal.loginUserHis.repository.LoginUserHisRepository;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
@Slf4j
@Transactional
public class LoginUserHisService extends BaseService<LoginUserHisEntity, LoginUserHisRepository> {
@Autowired
private LoginUserHisRepository repository;
@Autowired
private LoginUserHisMapper mapper;
@Autowired
private ConversionService conversionService;
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public LoginUserHisEntity save(LoginUserHisEntity entity) {
return super.save(entity);
}
public LoginUserHisDto create(CreateLoginUserHisDto request) {
LoginUserHisEntity entity = mapper.toEntity(request);
this.repository.save(entity);
return getById(entity.getId())
;
}
public LoginUserHisDto update(UpdateLoginUserHisDto request) {
LoginUserHisEntity entity = this.repository.get(request.getId());
this.mapper.updateEntity(entity, request);
this.repository.save(entity);
return getById(entity.getId())
;
}
public void delete(IdRequest request) {
this.repository.deleteAllById(request.getIds())
;
}
public LoginUserHisDto getById(String id) {
LoginUserHisEntity entity = repository.get(id);
return mapper.toDto(entity)
;
}
public Page<LoginUserHisDto> list(CommonQuery query) {
Page<LoginUserHisEntity> page = repository.findAll(query.specification(conversionService), PageRequest.of(query.getPageNo(), query.getPageSize(), Sort.by(query.getOrders())));
return page.map(this.mapper::toDto)
;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logExpired(final String sessionId) {
logoutWithType(List.of(sessionId), "1");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logUserLogout(String sessionId) {
logoutWithType(List.of(sessionId), "0");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAdminLogout(List<String> ids) {
logoutWithType(ids, "2");
}
private void logoutWithType(List<String> ids, String logoutType) {
List<LoginUserHisEntity> his = findAll(new Specification<LoginUserHisEntity>() {
@Override
public Predicate toPredicate(Root<LoginUserHisEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return root.get("sessionId").in(ids);
}
});
for (LoginUserHisEntity hi : his) {
hi.setLogoutType(logoutType);
hi.setLogoutTime(LocalDateTime.now());
save(hi);
}
}
}

View File

@@ -8,7 +8,6 @@ import cn.lihongjie.coal.user.dto.UserDto;
import cn.lihongjie.coal.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -42,10 +41,8 @@ public class LoginController extends BaseController {
public Object logout() {
if (Ctx.isLoggedIn()) {
this.service.logout(
((SessionService.MyAuthentication)
SecurityContextHolder.getContext().getAuthentication())
.getSessionId());
this.service.userLogout(
);
}
return true;
}

View File

@@ -8,6 +8,8 @@ import cn.lihongjie.coal.ip.IpQueryService;
import cn.lihongjie.coal.loginUser.dto.LoginUserDto;
import cn.lihongjie.coal.loginUser.entity.LoginUserEntity;
import cn.lihongjie.coal.loginUser.service.LoginUserService;
import cn.lihongjie.coal.loginUserHis.entity.LoginUserHisEntity;
import cn.lihongjie.coal.loginUserHis.service.LoginUserHisService;
import cn.lihongjie.coal.organization.entity.OrganizationEntity;
import cn.lihongjie.coal.organization.service.OrganizationService;
import cn.lihongjie.coal.sysconfig.service.SysConfigService;
@@ -37,6 +39,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -44,8 +47,7 @@ import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@Transactional
public
class SessionService {
public class SessionService {
@Autowired UserService userService;
@@ -87,72 +89,104 @@ class SessionService {
@Autowired IpQueryService ipQueryService;
@Autowired LoginUserHisService loginUserHisService;
@SneakyThrows
public void login(LoginDto dto) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
UserEntity user = null;
if (sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_CAPTCHA)) {
String captchaId = dto.getCaptchaId();
if (captchaId == null){
throw new BizException("验证码错误, 请刷新验证码重试");
}
String expectCaptcha = stringRedisTemplate.opsForValue().get(captchaId);
if (expectCaptcha == null) {
throw new BizException("验证码已失效");
}
if (!StringUtils.equals(expectCaptcha, dto.getCaptcha())) {
stringRedisTemplate.opsForValue().getAndDelete(captchaId);
throw new BizException("验证码错误, 请刷新验证码重试");
}
}
UserEntity user =
userService
.findByUsername(dto.getUsername())
.orElseThrow(() -> new BizException("用户名或者密码错误"));
if (user.isDisabled()) {
throw new BizException("用户被禁用");
}
OrganizationEntity organization = organizationService.get(user.getOrganizationId());
if (organization.isDisabled()) {
throw new BizException("用户所在机构被禁用");
}
if (!userService.isValidPassword(dto.getPassword(), user.getPassword())) {
throw new BizException("用户名或者密码错误");
}
LoginUserEntity entity = new LoginUserEntity();
entity.setUser(user);
entity.setCaptcha(dto.getCaptcha());
entity.setIp(RequestUtils.getIp(request));
entity.setUa(RequestUtils.getUa(request));
try {
entity.setLocation(ipQueryService.query(entity.getIp()));
if (sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_CAPTCHA)) {
String captchaId = dto.getCaptchaId();
if (captchaId == null) {
throw new BizException("验证码错误, 请刷新验证码重试");
}
String expectCaptcha = stringRedisTemplate.opsForValue().get(captchaId);
if (expectCaptcha == null) {
throw new BizException("验证码已失效");
}
if (!StringUtils.equals(expectCaptcha, dto.getCaptcha())) {
stringRedisTemplate.opsForValue().getAndDelete(captchaId);
throw new BizException("验证码错误, 请刷新验证码重试");
}
}
user =
userService
.findByUsername(dto.getUsername())
.orElseThrow(() -> new BizException("用户名或者密码错误"));
if (user.isDisabled()) {
throw new BizException("用户被禁用");
}
OrganizationEntity organization = organizationService.get(user.getOrganizationId());
if (organization.isDisabled()) {
throw new BizException("用户所在机构被禁用");
}
if (!userService.isValidPassword(dto.getPassword(), user.getPassword())) {
throw new BizException("用户名或者密码错误");
}
LoginUserEntity entity = new LoginUserEntity();
entity.setUser(user);
entity.setCaptcha(dto.getCaptcha());
entity.setIp(RequestUtils.getIp(request));
entity.setUa(RequestUtils.getUa(request));
try {
entity.setLocation(ipQueryService.query(entity.getIp()));
} catch (Exception e) {
log.warn("查询ip地址失败 {}", entity.getIp(), e);
}
loginUserService.newLogin(entity);
LoginUserDto loginUserDto = loginUserService.getFromCache(entity);
String sessionId = entity.getId();
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(
new MyAuthentication(loginUserDto, loginUserDto.getUser(), sessionId));
SecurityContextHolder.setContext(context);
LoginUserHisEntity his = new LoginUserHisEntity();
his.setIp(RequestUtils.getIp(request));
his.setUa(RequestUtils.getUa(request));
his.setCaptcha(dto.getCaptcha());
his.setUserName(dto.getUsername());
his.setLoginStatus("1");
his.setLocation(ipQueryService.query(his.getIp()));
his.setLoginTime(LocalDateTime.now());
his.setUser(user);
his.setSessionId(sessionId);
loginUserHisService.save(his);
} catch (Exception e) {
log.warn("查询ip地址失败 {}", entity.getIp(), e);
LoginUserHisEntity his = new LoginUserHisEntity();
his.setIp(RequestUtils.getIp(request));
his.setUa(RequestUtils.getUa(request));
his.setCaptcha(dto.getCaptcha());
his.setUserName(dto.getUsername());
his.setLoginStatus("1");
his.setLocation(ipQueryService.query(his.getIp()));
his.setLoginTime(LocalDateTime.now());
his.setUser(user);
his.setRemarks(e.getMessage());
loginUserHisService.save(his);
throw e;
}
loginUserService.newLogin(entity);
LoginUserDto loginUserDto = loginUserService.getFromCache(entity);
String sessionId = entity.getId();
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(
new MyAuthentication(loginUserDto, loginUserDto.getUser(), sessionId));
SecurityContextHolder.setContext(context);
}
@SneakyThrows
@@ -210,6 +244,24 @@ class SessionService {
loginUserService.logout(new IdRequest(sessionId));
}
public void userLogout() {
loginUserService.logout(
new IdRequest(
((SessionService.MyAuthentication)
SecurityContextHolder.getContext().getAuthentication())
.getSessionId()));
loginUserHisService.logUserLogout(
((SessionService.MyAuthentication)
SecurityContextHolder.getContext().getAuthentication())
.getSessionId());
}
public void anonymousSession() {
SecurityContext context = SecurityContextHolder.createEmptyContext();

View File

@@ -55,8 +55,7 @@ import java.util.stream.Collectors;
@Service
@Slf4j
@Transactional
public
class UserService extends BaseService<UserEntity, UserRepository> {
public class UserService extends BaseService<UserEntity, UserRepository> {
@Autowired UserRepository repository;
@@ -178,15 +177,11 @@ class UserService extends BaseService<UserEntity, UserRepository> {
public void resetPwd(String userId, String password) {
UserEntity user =
repository.findById(userId).orElseThrow(() -> new BizException("用户不存在"));
UserEntity user = repository.findById(userId).orElseThrow(() -> new BizException("用户不存在"));
user.setPassword(passwordEncoder.encode(password));
repository.save(user);
}
public Page<UserDto> list(CommonQuery query) {
@@ -308,6 +303,7 @@ class UserService extends BaseService<UserEntity, UserRepository> {
}
public void clearUserCache(String id) {
if (StringUtils.isEmpty(id)) return;
try {
cacheManager.getCache(Constants.CACHE_USER_RESOURCES).evict(id);

View File

@@ -1703,6 +1703,42 @@
}
]
},
{
"code": "loginUserHis.loginStatus",
"name": "登录状态",
"item": [
{
"code": "0",
"name": "成功"
},
{
"code": "1",
"name": "失败"
}
]
},
{
"code": "loginUserHis.logoutType",
"name": "登出类型",
"item": [
{
"code": "0",
"name": "主动退出"
},
{
"code": "1",
"name": "会话过期"
},
{
"code": "2",
"name": "管理员踢出"
}
]
},
{
"code": "permission.type",
"name": "权限类型",