|
|
@@ -1,6 +1,8 @@
|
|
|
package com.sckw.system.service;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.http.useragent.UserAgent;
|
|
|
+import cn.hutool.http.useragent.UserAgentUtil;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.sckw.core.common.enums.enums.DictEnum;
|
|
|
@@ -12,31 +14,39 @@ import com.sckw.core.model.enums.ClientTypeEnum;
|
|
|
import com.sckw.core.model.enums.SystemTypeEnum;
|
|
|
import com.sckw.core.utils.*;
|
|
|
import com.sckw.core.web.constant.HttpStatus;
|
|
|
+import com.sckw.core.web.constant.RequestConstant;
|
|
|
import com.sckw.core.web.context.LoginUserHolder;
|
|
|
+import com.sckw.core.web.model.LoginUserInfo;
|
|
|
+import com.sckw.core.web.request.RequestUtil;
|
|
|
import com.sckw.excel.utils.ExcelUtil;
|
|
|
import com.sckw.redis.constant.RedisConstant;
|
|
|
import com.sckw.redis.utils.RedissonUtils;
|
|
|
import com.sckw.system.api.model.dto.req.RegisterReqDto;
|
|
|
import com.sckw.system.api.model.dto.req.UpdatePasswordReqDto;
|
|
|
import com.sckw.system.api.model.dto.res.AreaTreeFrontResDto;
|
|
|
+import com.sckw.system.api.model.dto.res.EntCacheResDto;
|
|
|
import com.sckw.system.api.model.dto.res.KwsUserResDto;
|
|
|
import com.sckw.system.api.model.dto.res.RegisterResDto;
|
|
|
import com.sckw.system.api.model.dto.res.SysDictResDto;
|
|
|
import com.sckw.system.dao.*;
|
|
|
import com.sckw.system.dubbo.RemoteSystemServiceImpl;
|
|
|
import com.sckw.system.model.*;
|
|
|
+import com.sckw.system.model.LoginLogs;
|
|
|
import com.sckw.system.api.model.dto.res.RoleInfoDto;
|
|
|
import com.sckw.system.model.report.KwsUserExcel;
|
|
|
import com.sckw.system.model.vo.req.*;
|
|
|
import com.sckw.system.model.vo.res.KwsUserResVo;
|
|
|
import com.sckw.system.model.vo.res.KwsUserSystemTypeVo;
|
|
|
+import com.sckw.system.model.vo.res.LoginResVo;
|
|
|
import com.sckw.system.model.vo.res.SalesResp;
|
|
|
import com.sckw.system.model.vo.res.SwitchAccountResVo;
|
|
|
import com.sckw.system.model.vo.res.UserInfoResVo;
|
|
|
import com.sckw.system.repository.KwsRoleRepository;
|
|
|
import com.sckw.system.repository.KwsUserRepository;
|
|
|
import com.sckw.system.repository.KwsUserRoleRepository;
|
|
|
+import com.sckw.system.repository.LoginLogsService;
|
|
|
import com.sckw.transport.api.dubbo.TransportRemoteStatisticsService;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
@@ -98,6 +108,8 @@ public class KwsUserService {
|
|
|
|
|
|
private final KwsUserRepository kwsUserRepository;
|
|
|
|
|
|
+ private final LoginLogsService loginLogsService;
|
|
|
+
|
|
|
@DubboReference(version = "1.0.0", group = "design", check = false)
|
|
|
private TransportRemoteStatisticsService transportStatisticsService;
|
|
|
|
|
|
@@ -875,6 +887,7 @@ public class KwsUserService {
|
|
|
String account = kwsUser.getAccount();
|
|
|
String currentPwd = kwsUser.getPassword();
|
|
|
String salt = kwsUser.getSalt();
|
|
|
+ oldPassword = PasswordUtils.md5(oldPassword);
|
|
|
checkPassword(account, oldPassword, currentPwd, salt);
|
|
|
|
|
|
// 3. 校验新密码是否与旧密码相同
|
|
|
@@ -883,20 +896,108 @@ public class KwsUserService {
|
|
|
}
|
|
|
|
|
|
// 4. 修改密码
|
|
|
+ newPassword = PasswordUtils.md5(newPassword);
|
|
|
updatePwd(newPassword, kwsUser);
|
|
|
|
|
|
log.info("密码修改成功,用户ID: {}", userId);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 用户登录(叉车APP)
|
|
|
+ *
|
|
|
+ * @param account 账号
|
|
|
+ * @param password 密码
|
|
|
+ * @param systemType 系统类型
|
|
|
+ * @param request HTTP请求(用于记录登录日志)
|
|
|
+ * @return LoginResVo
|
|
|
+ */
|
|
|
+ public LoginResVo login(String account, String password, Integer systemType, HttpServletRequest request) {
|
|
|
+ log.info("用户登录,账号: {}, 系统类型: {}", account, systemType);
|
|
|
+
|
|
|
+ // 1. 查询用户信息
|
|
|
+ KwsUser kwsUser = getUserByAccount(account, systemType);
|
|
|
+ if (Objects.isNull(kwsUser)) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "账号不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 检查用户状态
|
|
|
+ if (kwsUser.getDelFlag() != Global.NO) {
|
|
|
+ throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "账号已删除");
|
|
|
+ }
|
|
|
+ if (kwsUser.getStatus() != null && kwsUser.getStatus() == Global.YES) {
|
|
|
+ throw new SystemException(HttpStatus.CODE_10301, "账号已被锁定");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 校验密码
|
|
|
+ checkPassword(account, password, kwsUser.getPassword(), kwsUser.getSalt());
|
|
|
+
|
|
|
+ // 4. 查询用户角色
|
|
|
+ List<RoleInfoDto> roleInfoList = kwsUserRoleDao.queryRoleList(kwsUser.getId());
|
|
|
+ String roleName = "";
|
|
|
+ Long roleId = null;
|
|
|
+ if (CollUtil.isNotEmpty(roleInfoList)) {
|
|
|
+ roleName = roleInfoList.stream()
|
|
|
+ .map(RoleInfoDto::getRoleName)
|
|
|
+ .collect(Collectors.joining(","));
|
|
|
+ roleId = roleInfoList.get(0).getRoleId();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 获取客户端类型
|
|
|
+ String clientType = request != null ? request.getHeader(RequestConstant.CLIENT_TYPE) : null;
|
|
|
+ if (StringUtils.isBlank(clientType)) {
|
|
|
+ clientType = ClientTypeEnum.app.getValue(); // 默认使用app
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 生成token并缓存
|
|
|
+ if (request != null) {
|
|
|
+ generateToken(kwsUser.getId(), account, clientType, systemType);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 缓存用户登录信息
|
|
|
+ if (request != null) {
|
|
|
+ saveUserToCache(kwsUser, systemType, clientType, roleId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 缓存企业信息
|
|
|
+ if (request != null && kwsUser.getEntId() != null) {
|
|
|
+ saveEntToCache(kwsUser.getEntId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 9. 缓存菜单权限
|
|
|
+ if (request != null && roleId != null) {
|
|
|
+ saveMenusToCache(kwsUser.getId(), systemType, roleId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 10. 组装返回结果
|
|
|
+ LoginResVo result = LoginResVo.builder()
|
|
|
+ .userId(kwsUser.getId())
|
|
|
+ .name(kwsUser.getName())
|
|
|
+ .account(kwsUser.getAccount())
|
|
|
+ .phone(kwsUser.getPhone())
|
|
|
+ .photo(kwsUser.getPhoto())
|
|
|
+ .roleName(roleName)
|
|
|
+ .systemType(kwsUser.getSystemType())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 11. 记录登录日志(首次登录)
|
|
|
+ if (request != null) {
|
|
|
+ saveLoginLogs(kwsUser.getId(), request, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("登录成功,用户ID: {}", kwsUser.getId());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 切换账号(叉车APP)
|
|
|
*
|
|
|
* @param account 账号
|
|
|
* @param password 密码
|
|
|
* @param systemType 系统类型
|
|
|
+ * @param request HTTP请求(可选,用于记录登录日志)
|
|
|
* @return SwitchAccountResVo
|
|
|
*/
|
|
|
- public SwitchAccountResVo switchAccount(String account, String password, Integer systemType) {
|
|
|
+ public SwitchAccountResVo switchAccount(String account, String password, Integer systemType, HttpServletRequest request) {
|
|
|
log.info("切换账号,账号: {}, 系统类型: {}", account, systemType);
|
|
|
|
|
|
// 1. 查询用户信息
|
|
|
@@ -926,8 +1027,7 @@ public class KwsUserService {
|
|
|
}
|
|
|
|
|
|
// 5. 组装返回结果
|
|
|
- log.info("切换账号成功,用户ID: {}", kwsUser.getId());
|
|
|
- return SwitchAccountResVo.builder()
|
|
|
+ SwitchAccountResVo result = SwitchAccountResVo.builder()
|
|
|
.userId(kwsUser.getId())
|
|
|
.name(kwsUser.getName())
|
|
|
.account(kwsUser.getAccount())
|
|
|
@@ -936,6 +1036,14 @@ public class KwsUserService {
|
|
|
.roleName(roleName)
|
|
|
.systemType(kwsUser.getSystemType())
|
|
|
.build();
|
|
|
+
|
|
|
+ // 6. 记录切换账号登录日志
|
|
|
+ if (request != null) {
|
|
|
+ saveLoginLogs(kwsUser.getId(), request, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("切换账号成功,用户ID: {}", kwsUser.getId());
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -964,4 +1072,154 @@ public class KwsUserService {
|
|
|
|
|
|
log.info("退出登录成功,用户ID: {}", userId);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成token并缓存
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param account 账号
|
|
|
+ * @param clientType 客户端类型
|
|
|
+ * @param systemType 系统类型
|
|
|
+ * @return token
|
|
|
+ */
|
|
|
+ private String generateToken(Long userId, String account, String clientType, Integer systemType) {
|
|
|
+ Map<String, Object> info = new HashMap<>(Global.NUMERICAL_SIXTEEN);
|
|
|
+ info.put("userId", userId);
|
|
|
+ info.put("account", account);
|
|
|
+ info.put("clientType", clientType);
|
|
|
+ info.put("systemType", systemType);
|
|
|
+ String key = Global.getFullUserTokenKey(clientType, userId);
|
|
|
+ String token = EncryUtil.encryV1(Global.PRI_KEY, JSON.toJSONString(info));
|
|
|
+ int expireTime = ClientTypeEnum.expireTime(clientType);
|
|
|
+ RedissonUtils.putString(key, token, expireTime);
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存用户登录信息
|
|
|
+ * @param kwsUser 用户信息
|
|
|
+ * @param systemType 系统类型
|
|
|
+ * @param clientType 客户端类型
|
|
|
+ * @param roleId 角色ID
|
|
|
+ */
|
|
|
+ private void saveUserToCache(KwsUser kwsUser, Integer systemType, String clientType, Long roleId) {
|
|
|
+ try {
|
|
|
+ LoginUserInfo loginUserInfo = new LoginUserInfo();
|
|
|
+ loginUserInfo.setId(kwsUser.getId());
|
|
|
+ loginUserInfo.setSystemType(systemType);
|
|
|
+ loginUserInfo.setClientType(clientType);
|
|
|
+ loginUserInfo.setAccount(kwsUser.getAccount());
|
|
|
+ loginUserInfo.setUserName(kwsUser.getName());
|
|
|
+ loginUserInfo.setPhone(kwsUser.getPhone());
|
|
|
+ loginUserInfo.setStatus(kwsUser.getStatus());
|
|
|
+ loginUserInfo.setIsMain(kwsUser.getIsMain() != null ? kwsUser.getIsMain() : Global.NO);
|
|
|
+ loginUserInfo.setEntId(kwsUser.getEntId());
|
|
|
+ loginUserInfo.setUseRoleId(roleId);
|
|
|
+ loginUserInfo.setUseEntId(kwsUser.getEntId());
|
|
|
+
|
|
|
+ // 查询企业信息
|
|
|
+ if (kwsUser.getEntId() != null) {
|
|
|
+ EntCacheResDto enterprise = remoteSystemService.queryEntDetails(kwsUser.getEntId());
|
|
|
+ if (enterprise != null) {
|
|
|
+ loginUserInfo.setEntName(enterprise.getFirmName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询数据权限(暂时不实现,需要时可以通过RemoteUserService调用)
|
|
|
+ // 如果需要数据权限,可以通过Dubbo调用RemoteUserService.queryAuthUserList
|
|
|
+
|
|
|
+ String key = Global.getFullUserLoginKey(systemType, kwsUser.getId());
|
|
|
+ RedissonUtils.putString(key, JSON.toJSONString(loginUserInfo), Global.APP_TOKEN_EXPIRE);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("缓存用户登录信息失败,用户ID: {}", kwsUser.getId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存企业信息
|
|
|
+ * @param entId 企业ID
|
|
|
+ */
|
|
|
+ private void saveEntToCache(Long entId) {
|
|
|
+ try {
|
|
|
+ EntCacheResDto enterprise = remoteSystemService.queryEntDetails(entId);
|
|
|
+ if (enterprise != null) {
|
|
|
+ String key = Global.getFullUserEntKey(entId);
|
|
|
+ RedissonUtils.putString(key, JSON.toJSONString(enterprise), Global.APP_TOKEN_EXPIRE);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("缓存企业信息失败,企业ID: {}", entId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存菜单权限
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param systemType 系统类型
|
|
|
+ * @param roleId 角色ID
|
|
|
+ */
|
|
|
+ private void saveMenusToCache(Long userId, Integer systemType, Long roleId) {
|
|
|
+ try {
|
|
|
+ // 使用本地服务查询菜单
|
|
|
+ com.sckw.system.model.pojo.FindMenuTreePojo findMenuTreePojo = new com.sckw.system.model.pojo.FindMenuTreePojo();
|
|
|
+ findMenuTreePojo.setRoleIds(Collections.singletonList(roleId));
|
|
|
+
|
|
|
+ List<com.sckw.system.model.vo.res.KwsMenuResVo> kwsMenuResVos = kwsMenuService.findList(findMenuTreePojo);
|
|
|
+ if (CollectionUtils.isEmpty(kwsMenuResVos)) {
|
|
|
+ RedissonUtils.delete(Global.REDIS_SYS_MENU_PREFIX + systemType + Global.COLON + userId);
|
|
|
+ log.warn("未查询到角色{}的菜单权限", roleId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> menus = new ArrayList<>();
|
|
|
+ for (com.sckw.system.model.vo.res.KwsMenuResVo menuResVo : kwsMenuResVos) {
|
|
|
+ String links = menuResVo.getLinks();
|
|
|
+ if (StringUtils.isNotBlank(links)) {
|
|
|
+ menus.addAll(Arrays.asList(links.split(",")));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ RedissonUtils.putSet(Global.REDIS_SYS_MENU_PREFIX + userId, menus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("缓存菜单权限失败,用户ID: {}, 角色ID: {}", userId, roleId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存登录日志
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param request HTTP请求
|
|
|
+ * @param loginType 登录类型: 1-首次登录, 2-切换账号登录
|
|
|
+ */
|
|
|
+ public void saveLoginLogs(Long userId, HttpServletRequest request, Integer loginType) {
|
|
|
+ try {
|
|
|
+ // 获取登录IP
|
|
|
+ String loginIp = RequestUtil.getClientIp(request);
|
|
|
+
|
|
|
+ // 获取设备信息
|
|
|
+ String userAgentStr = request.getHeader("User-Agent");
|
|
|
+ String deviceInfo = "";
|
|
|
+ if (userAgentStr != null && !userAgentStr.isEmpty()) {
|
|
|
+ try {
|
|
|
+ UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
|
|
|
+ String browser = userAgent.getBrowser().getName();
|
|
|
+ String browserVersion = userAgent.getVersion();
|
|
|
+ String os = userAgent.getOs().getName();
|
|
|
+ String device = userAgent.getPlatform().getName();
|
|
|
+ deviceInfo = String.format("%s %s / %s / %s", browser, browserVersion, os, device);
|
|
|
+ } catch (Exception e) {
|
|
|
+ deviceInfo = userAgentStr.length() > 100 ? userAgentStr.substring(0, 100) : userAgentStr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LoginLogs loginLogs = new LoginLogs();
|
|
|
+ loginLogs.setId(new IdWorker(1).nextId());
|
|
|
+ loginLogs.setUserId(userId);
|
|
|
+ loginLogs.setLoginIp(loginIp != null ? loginIp : "");
|
|
|
+ loginLogs.setDeviceInfo(deviceInfo);
|
|
|
+ loginLogs.setLoginType(loginType);
|
|
|
+ loginLogs.setLoginTime(new Date());
|
|
|
+ loginLogsService.save(loginLogs);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 记录日志失败不影响主流程
|
|
|
+ log.error("保存登录日志失败,用户ID: {}", userId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|