将 dict 和 login log 迁移到共享库里

This commit is contained in:
YunaiV
2021-10-10 02:38:38 +08:00
parent 5b723d02b2
commit 03ef1fc764
57 changed files with 366 additions and 529 deletions

View File

@ -18,6 +18,11 @@
<dependencies>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-core-service</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId>

View File

@ -3,8 +3,8 @@ package cn.iocoder.yudao.userserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserServerApplication {
@SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${yudao.info.base-package} 和 ${yudao.core-service.base-package}
@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"})public class UserServerApplication {
public static void main(String[] args) {
SpringApplication.run(UserServerApplication.class, args);

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.userserver.modules.member.convert.user;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.userserver.modules.member.dal.dataobject.user.MbrUserDO;
import org.mapstruct.Mapper;
@ -10,6 +11,11 @@ public interface MbrAuthConvert {
MbrAuthConvert INSTANCE = Mappers.getMapper(MbrAuthConvert.class);
LoginUser convert(MbrUserDO bean);
LoginUser convert0(MbrUserDO bean);
default LoginUser convert(MbrUserDO bean) {
// 目的,为了设置 UserTypeEnum.MEMBER.getValue()
return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue());
}
}

View File

@ -1,20 +1,18 @@
package cn.iocoder.yudao.userserver.modules.member.service.auth.impl;
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.userserver.modules.member.controller.auth.vo.MbrAuthLoginReqVO;
import cn.iocoder.yudao.userserver.modules.member.convert.user.MbrAuthConvert;
import cn.iocoder.yudao.userserver.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants;
import cn.iocoder.yudao.userserver.modules.member.service.auth.MbrAuthService;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
import cn.iocoder.yudao.userserver.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.userserver.modules.system.enums.logger.SysLoginResultEnum;
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysUserSessionService;
import cn.iocoder.yudao.userserver.modules.system.service.logger.SysLoginLogService;
import cn.iocoder.yudao.userserver.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import cn.iocoder.yudao.userserver.modules.system.service.logger.impl.SysLoginLogServiceImpl;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
@ -29,7 +27,6 @@ import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -51,9 +48,9 @@ public class MbrAuthServiceImpl implements MbrAuthService {
@Resource
private MbrUserService userService;
@Resource
private SysLoginLogService loginLogService;
private SysLoginLogCoreService loginLogCoreService;
@Resource
private SysUserSessionService userSessionService;
private SysUserSessionCoreService userSessionCoreService;
@Override
public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
@ -72,7 +69,7 @@ public class MbrAuthServiceImpl implements MbrAuthService {
LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword());
// 缓存登录用户到 Redis 中,返回 sessionId 编号
return userSessionService.createUserSession(loginUser, userIp, userAgent);
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
}
private LoginUser login0(String username, String password) {
@ -114,7 +111,7 @@ public class MbrAuthServiceImpl implements MbrAuthService {
reqDTO.setUserAgent(ServletUtils.getUserAgent());
reqDTO.setUserIp(ServletUtils.getClientIP());
reqDTO.setResult(loginResult.getResult());
loginLogService.createLoginLog(reqDTO);
loginLogCoreService.createLoginLog(reqDTO);
// 更新最后登录时间
if (user != null && Objects.equals(SysLoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
userService.updateUserLogin(user.getId(), ServletUtils.getClientIP());

View File

@ -1,20 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.convert.dict;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SysDictDataConvert {
SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class);
DictDataRespDTO convert02(SysDictDataDO bean);
List<DictDataRespDTO> convertList03(Collection<SysDictDataDO> list);
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.convert.logger;
import cn.iocoder.yudao.userserver.modules.system.dal.mysql.logger.SysLoginLogDO;
import cn.iocoder.yudao.userserver.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface SysLoginLogConvert {
SysLoginLogConvert INSTANCE = Mappers.getMapper(SysLoginLogConvert.class);
SysLoginLogDO convert(SysLoginLogCreateReqDTO bean);
}

View File

@ -1,54 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 字典数据表
*
* @author ruoyi
*/
@TableName("sys_dict_data")
@Data
@EqualsAndHashCode(callSuper = true)
public class SysDictDataDO extends BaseDO {
/**
* 字典数据编号
*/
@TableId
private Long id;
/**
* 字典排序
*/
private Integer sort;
/**
* 字典标签
*/
private String label;
/**
* 字典值
*/
private String value;
/**
* 字典类型
*
* 冗余 {@link SysDictDataDO#getDictType()}
*/
private String dictType;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String remark;
}

View File

@ -1,10 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.logger;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.userserver.modules.system.dal.mysql.logger.SysLoginLogDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysLoginLogMapper extends BaseMapperX<SysLoginLogDO> {
}

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.dal.dataobject;

View File

@ -1,28 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Date;
@Mapper
public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> {
default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) {
return selectOne(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType)
.eq("value", value));
}
default int selectCountByDictType(String dictType) {
return selectCount("dict_type", dictType);
}
default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
return selectOne(new QueryWrapper<SysDictDataDO>().select("id")
.gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
}
}

View File

@ -1,70 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.dal.mysql.logger;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.userserver.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.userserver.modules.system.enums.logger.SysLoginResultEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 登录日志表
*
* 注意,包括登录和登出两种行为
*
* @author 芋道源码
*/
@TableName("sys_login_log")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SysLoginLogDO extends BaseDO {
/**
* 日志主键
*/
private Long id;
/**
* 日志类型
*
* 枚举 {@link SysLoginLogTypeEnum}
*/
private Integer logType;
/**
* 链路追踪编号
*/
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 用户账号
*
* 冗余,因为账号可以变更
*/
private String username;
/**
* 登录结果
*
* 枚举 {@link SysLoginResultEnum}
*/
private Integer result;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
}

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录日志的类型枚举
*/
@Getter
@AllArgsConstructor
public enum SysLoginLogTypeEnum {
LOGIN_USERNAME(100), // 使用账号登录
LOGIN_SOCIAL(101), // 使用社交登录
LOGIN_MOCK(102), // 使用 Mock 登录
LOGIN_MOBILE(103), // 使用手机登陆
LOGIN_SMS(104), // 使用短信登陆
LOGOUT_SELF(200), // 自己主动登出
LOGOUT_TIMEOUT(201), // 超时登出
LOGOUT_DELETE(202), // 强制退出
;
/**
* 日志类型
*/
private final Integer type;
}

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录结果的枚举类
*/
@Getter
@AllArgsConstructor
public enum SysLoginResultEnum {
SUCCESS(0), // 成功
BAD_CREDENTIALS(10), // 账号或密码不正确
USER_DISABLED(20), // 用户被禁用
CAPTCHA_NOT_FOUND(30), // 图片验证码不存在
CAPTCHA_CODE_ERROR(31), // 图片验证码不正确
UNKNOWN_ERROR(100), // 未知异常
;
/**
* 结果
*/
private final Integer result;
}

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.enums;

View File

@ -1,17 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.service.dict;
import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
/**
* 字典数据 Service 接口
*
* @author ruoyi
*/
public interface SysDictDataService extends DictDataFrameworkService {
/**
* 初始化字典数据的本地缓存
*/
void initLocalCache();
}

View File

@ -1,122 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.service.dict.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.userserver.modules.system.convert.dict.SysDictDataConvert;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
import cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict.SysDictDataMapper;
import cn.iocoder.yudao.userserver.modules.system.service.dict.SysDictDataService;
import com.google.common.collect.ImmutableTable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/**
* 字典数据 Service 实现类
*
* @author ruoyi
*/
@Service
@Slf4j
public class SysDictDataServiceImpl implements SysDictDataService {
/**
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
*/
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
/**
* 字典数据缓存,第二个 key 使用 label
*
* key1字典类型 dictType
* key2字典标签 label
*/
private ImmutableTable<String, String, SysDictDataDO> labelDictDataCache;
/**
* 字典数据缓存,第二个 key 使用 value
*
* key1字典类型 dictType
* key2字典值 value
*/
private ImmutableTable<String, String, SysDictDataDO> valueDictDataCache;
/**
* 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新
*/
private volatile Date maxUpdateTime;
@Resource
private SysDictDataMapper dictDataMapper;
@Override
@PostConstruct
public synchronized void initLocalCache() {
// 获取字典数据列表,如果有更新
List<SysDictDataDO> dataList = this.loadDictDataIfUpdate(maxUpdateTime);
if (CollUtil.isEmpty(dataList)) {
return;
}
// 构建缓存
ImmutableTable.Builder<String, String, SysDictDataDO> labelDictDataBuilder = ImmutableTable.builder();
ImmutableTable.Builder<String, String, SysDictDataDO> valueDictDataBuilder = ImmutableTable.builder();
dataList.forEach(dictData -> {
labelDictDataBuilder.put(dictData.getDictType(), dictData.getLabel(), dictData);
valueDictDataBuilder.put(dictData.getDictType(), dictData.getValue(), dictData);
});
labelDictDataCache = labelDictDataBuilder.build();
valueDictDataCache = valueDictDataBuilder.build();
assert dataList.size() > 0; // 断言,避免告警
maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
}
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() {
initLocalCache();
}
/**
* 如果字典数据发生变化,从数据库中获取最新的全量字典数据。
* 如果未发生变化,则返回空
*
* @param maxUpdateTime 当前字典数据的最大更新时间
* @return 字典数据列表
*/
private List<SysDictDataDO> loadDictDataIfUpdate(Date maxUpdateTime) {
// 第一步,判断是否要更新。
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
log.info("[loadDictDataIfUpdate][首次加载全量字典数据]");
} else { // 判断数据库中是否有更新的字典数据
if (!dictDataMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
return null;
}
log.info("[loadDictDataIfUpdate][增量加载全量字典数据]");
}
// 第二步,如果有更新,则从数据库加载所有字典数据
return dictDataMapper.selectList();
}
@Override
public DictDataRespDTO getDictDataFromCache(String type, String value) {
return SysDictDataConvert.INSTANCE.convert02(valueDictDataCache.get(type, value));
}
@Override
public DictDataRespDTO parseDictDataFromCache(String type, String label) {
return SysDictDataConvert.INSTANCE.convert02(labelDictDataCache.get(type, label));
}
@Override
public List<DictDataRespDTO> listDictDatasFromCache(String type) {
return SysDictDataConvert.INSTANCE.convertList03(labelDictDataCache.row(type).values());
}
}

View File

@ -1,17 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.service.logger;
import cn.iocoder.yudao.userserver.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
/**
* 登录日志 Service 接口
*/
public interface SysLoginLogService {
/**
* 创建登录日志
*
* @param reqDTO 日志信息
*/
void createLoginLog(SysLoginLogCreateReqDTO reqDTO);
}

View File

@ -1,57 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.service.logger.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* 登录日志创建 Request DTO
*
* @author 芋道源码
*/
@Data
public class SysLoginLogCreateReqDTO {
/**
* 日志类型
*/
@NotNull(message = "日志类型不能为空")
private Integer logType;
/**
* 链路追踪编号
*/
@NotEmpty(message = "链路追踪编号不能为空")
private String traceId;
/**
* 用户编号
*/
private Long userId;
/**
* 用户账号
*/
@NotBlank(message = "用户账号不能为空")
@Size(max = 30, message = "用户账号长度不能超过30个字符")
private String username;
/**
* 登录结果
*/
@NotNull(message = "登录结果不能为空")
private Integer result;
/**
* 用户 IP
*/
@NotEmpty(message = "用户 IP 不能为空")
private String userIp;
/**
* 浏览器 UserAgent
*/
@NotEmpty(message = "浏览器 UserAgent 不能为空")
private String userAgent;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.service.logger.impl;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.userserver.modules.system.convert.logger.SysLoginLogConvert;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.logger.SysLoginLogMapper;
import cn.iocoder.yudao.userserver.modules.system.dal.mysql.logger.SysLoginLogDO;
import cn.iocoder.yudao.userserver.modules.system.service.logger.SysLoginLogService;
import cn.iocoder.yudao.userserver.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 登录日志 Service 实现
*/
@Service
public class SysLoginLogServiceImpl implements SysLoginLogService {
@Resource
private SysLoginLogMapper loginLogMapper;
@Override
public void createLoginLog(SysLoginLogCreateReqDTO reqDTO) {
SysLoginLogDO loginLog = SysLoginLogConvert.INSTANCE.convert(reqDTO);
loginLog.setUserType(UserTypeEnum.MEMBER.getValue());
// 插入
loginLogMapper.insert(loginLog);
}
}

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.userserver.modules.system.service;

View File

@ -31,7 +31,7 @@ mybatis-plus:
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject
type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject, ${yudao.core-service.base-package}.modules.*.dal.dataobject
--- #################### 芋道相关配置 ####################
@ -39,6 +39,8 @@ yudao:
info:
version: 1.0.0
base-package: cn.iocoder.yudao.userserver
core-service:
base-package: cn.iocoder.yudao.coreservice
web:
api-prefix: /api
controller-package: ${yudao.info.base-package}