Преглед на файлове

提供网页登录相关接口

mgz12 преди 6 месеца
родител
ревизия
fbe6cce0db
променени са 16 файла, в които са добавени 1534 реда и са изтрити 17 реда
  1. 618 0
      application-webadmin/src/main/java/com/tourism/webadmin/back/controller/LoginToWebController.java
  2. 5 0
      application-webadmin/src/main/java/com/tourism/webadmin/back/service/CaptchaService.java
  3. 6 0
      application-webadmin/src/main/java/com/tourism/webadmin/back/service/SmsService.java
  4. 22 0
      application-webadmin/src/main/java/com/tourism/webadmin/back/service/impl/CaptchaServiceImpl.java
  5. 41 0
      application-webadmin/src/main/java/com/tourism/webadmin/back/service/impl/SmsServiceImpl.java
  6. 189 0
      application-webadmin/src/main/java/com/tourism/webadmin/upms/dao/SysUserWebMapper.java
  7. 288 0
      application-webadmin/src/main/java/com/tourism/webadmin/upms/dao/mapper/SysUserWebMapper.xml
  8. 68 0
      application-webadmin/src/main/java/com/tourism/webadmin/upms/dto/SysUserWebDto.java
  9. 98 0
      application-webadmin/src/main/java/com/tourism/webadmin/upms/model/SysUserWeb.java
  10. 44 0
      application-webadmin/src/main/java/com/tourism/webadmin/upms/service/SysUserWebService.java
  11. 109 0
      application-webadmin/src/main/java/com/tourism/webadmin/upms/service/impl/SysUserWebServiceImpl.java
  12. 2 0
      common/common-core/src/main/java/com/tourism/common/core/constant/ApplicationConstant.java
  13. 2 0
      common/common-core/src/main/java/com/tourism/common/core/constant/ErrorCodeEnum.java
  14. 23 16
      common/common-core/src/main/java/com/tourism/common/core/util/RsaUtil.java
  15. 8 0
      common/common-log/src/main/java/com/tourism/common/log/model/constant/SysOperationLogType.java
  16. 11 1
      common/common-satoken/src/main/java/com/tourism/common/satoken/util/SaTokenUtil.java

+ 618 - 0
application-webadmin/src/main/java/com/tourism/webadmin/back/controller/LoginToWebController.java

@@ -0,0 +1,618 @@
+package com.tourism.webadmin.back.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.BooleanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.tourism.common.core.annotation.DisableDataFilter;
+import com.tourism.common.core.annotation.MyRequestBody;
+import com.tourism.common.core.constant.ApplicationConstant;
+import com.tourism.common.core.constant.ErrorCodeEnum;
+import com.tourism.common.core.object.LoginUserInfo;
+import com.tourism.common.core.object.ResponseResult;
+import com.tourism.common.core.object.TokenData;
+import com.tourism.common.core.upload.BaseUpDownloader;
+import com.tourism.common.core.upload.UpDownloaderFactory;
+import com.tourism.common.core.upload.UploadStoreInfo;
+import com.tourism.common.core.util.*;
+import com.tourism.common.flow.online.service.FlowOnlineOperationService;
+import com.tourism.common.log.annotation.OperationLog;
+import com.tourism.common.log.model.constant.SysOperationLogType;
+import com.tourism.common.mobile.model.MobileEntry;
+import com.tourism.common.online.service.OnlineOperationService;
+import com.tourism.common.redis.cache.SessionCacheHelper;
+import com.tourism.common.report.service.ReportOperationService;
+import com.tourism.common.satoken.util.SaTokenUtil;
+import com.tourism.webadmin.back.service.SmsService;
+import com.tourism.webadmin.config.ApplicationConfig;
+import com.tourism.webadmin.upms.dto.SysUserWebDto;
+import com.tourism.webadmin.upms.model.*;
+import com.tourism.webadmin.upms.model.constant.SysMenuType;
+import com.tourism.webadmin.upms.model.constant.SysOnlineMenuPermType;
+import com.tourism.webadmin.upms.model.constant.SysUserStatus;
+import com.tourism.webadmin.upms.service.*;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RSet;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 登录接口控制器类。
+ *
+ * @author 吃饭睡觉
+ * @date 2024-09-06
+ */
+@Tag(name = "网页用户登录接口")
+@DisableDataFilter
+@Slf4j
+@RestController
+@RequestMapping("/admin/web/login")
+public class LoginToWebController {
+
+
+    @Autowired
+    private SysUserWebService sysUserWebService;
+    @Autowired
+    private SysDataPermService sysDataPermService;
+    @Autowired
+    private OnlineOperationService onlineOperationService;
+    @Autowired
+    private FlowOnlineOperationService flowOnlineOperationService;
+    @Autowired
+    private ReportOperationService reportOperationService;
+    @Autowired
+    private ApplicationConfig appConfig;
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
+    private SessionCacheHelper cacheHelper;
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    @Autowired
+    private UpDownloaderFactory upDownloaderFactory;
+    @Autowired
+    private SaTokenUtil saTokenUtil;
+    @Autowired
+    private SmsService smsService;
+
+    private static final String IS_ADMIN = "isAdmin";
+    private static final String SHOW_NAME_FIELD = "showName";
+    private static final String SHOW_ORDER_FIELD = "showOrder";
+    private static final String HEAD_IMAGE_URL_FIELD = "headImageUrl";
+
+    /**
+     * 用户注册
+     * @return
+     * @throws UnsupportedEncodingException
+     */
+    @SaIgnore
+    @OperationLog(type = SysOperationLogType.Register, saveResponse = false)
+    @Transactional(rollbackFor = Exception.class)
+    @PostMapping("/doRegister")
+    public ResponseResult doRegister(@RequestBody SysUserWebDto sysUserWebDto) {
+
+        if (MyCommonUtil.existBlankArgument(sysUserWebDto.getLoginName(), sysUserWebDto.getPassword())) {
+            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
+        }
+
+        // 验证短信验证码
+        if (!smsService.validateSmsCode(sysUserWebDto.getSmsCode(),sysUserWebDto.getMobile())) {
+            return ResponseResult.error(ErrorCodeEnum.SMSCODE_ERR);
+        }
+
+        //存储用户信息
+        SysUserWeb sysUserWeb = new SysUserWeb();
+        BeanUtil.copyProperties(sysUserWebDto,sysUserWeb);
+        sysUserWebService.saveNew(sysUserWeb);
+
+        return ResponseResult.success();
+    }
+
+    private ResponseResult<SysUserWeb> verifyAndHandleLoginUser(
+            String loginName, String password) throws UnsupportedEncodingException {
+        String errorMessage;
+        SysUserWeb user = sysUserWebService.getSysUserByLoginName(loginName);
+        password = URLDecoder.decode(password, StandardCharsets.UTF_8.name());
+
+        // NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。
+        // 执行RsaUtil工具类中的main函数,可以生成新的公钥和私钥。
+        password = RsaUtil.decrypt(password, ApplicationConstant.PRIVATE_KEY);
+        if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
+            return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD);
+        }
+        if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) {
+            errorMessage = "登录失败,用户账号被锁定!";
+            return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage);
+        }
+        if (BooleanUtil.isTrue(appConfig.getExcludeLogin())) {
+            String deviceType = MyCommonUtil.getDeviceTypeWithString();
+            LoginUserInfo userInfo = BeanUtil.copyProperties(user, LoginUserInfo.class);
+            String loginId = SaTokenUtil.makeLoginIdByWeb(userInfo);
+            StpUtil.kickout(loginId, deviceType);
+        }
+        return ResponseResult.success(user);
+    }
+
+    /**
+     * 登录接口。
+     *
+     * @param loginName 登录名。
+     * @param password  密码。
+     * @return 应答结果对象,其中包括Token数据,以及菜单列表。
+     */
+    @SaIgnore
+    @OperationLog(type = SysOperationLogType.LOGIN_WEB, saveResponse = false)
+    @PostMapping("/doLogin")
+    public ResponseResult<JSONObject> doLogin(
+            @MyRequestBody String loginName,
+            @MyRequestBody String password) throws UnsupportedEncodingException {
+        if (MyCommonUtil.existBlankArgument(loginName, password)) {
+            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
+        }
+        ResponseResult<SysUserWeb> verifyResult = this.verifyAndHandleLoginUser(loginName, password);
+        if (!verifyResult.isSuccess()) {
+            return ResponseResult.errorFrom(verifyResult);
+        }
+        JSONObject jsonData = this.buildLoginDataAndLogin(verifyResult.getData());
+        return ResponseResult.success(jsonData);
+    }
+
+    /**
+     * 手机号登录接口。
+     * @return 应答结果对象,其中包括Token数据,以及菜单列表。
+     */
+    @SaIgnore
+    @OperationLog(type = SysOperationLogType.LOGIN_WEB, saveResponse = false)
+    @PostMapping("/doLoginByPhone")
+    public ResponseResult<JSONObject> doLoginByPhone(
+            @MyRequestBody String mobile,
+            @MyRequestBody String smsCode) throws UnsupportedEncodingException {
+        String errorMessage;
+        if (MyCommonUtil.existBlankArgument(mobile, smsCode)) {
+            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
+        }
+        //查询手机号是否存在
+        SysUserWeb sysUserByMobile = sysUserWebService.getSysUserByMobile(mobile);
+        if (ObjectUtils.isEmpty(sysUserByMobile)){
+            return ResponseResult.error(ErrorCodeEnum.INVALID_MOBILE_CODE);
+        }
+
+        // 验证短信验证码
+        if (!smsService.validateSmsCode(smsCode,mobile)) {
+            return ResponseResult.error(ErrorCodeEnum.SMSCODE_ERR);
+        }
+
+        if (sysUserByMobile.getUserStatus() == SysUserStatus.STATUS_LOCKED) {
+            errorMessage = "登录失败,用户账号被锁定!";
+            return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage);
+        }
+        if (BooleanUtil.isTrue(appConfig.getExcludeLogin())) {
+            String deviceType = MyCommonUtil.getDeviceTypeWithString();
+            LoginUserInfo userInfo = BeanUtil.copyProperties(sysUserByMobile, LoginUserInfo.class);
+            String loginId = SaTokenUtil.makeLoginIdByWeb(userInfo);
+            StpUtil.kickout(loginId, deviceType);
+        }
+        JSONObject jsonData = this.buildLoginDataAndLogin(sysUserByMobile);
+        return ResponseResult.success(jsonData);
+    }
+
+
+
+
+
+    /**
+     * 登录移动端接口。
+     *
+     * @param loginName 登录名。
+     * @param password  密码。
+     * @return 应答结果对象,其中包括Token数据,以及菜单列表。
+     */
+//    @Parameter(name = "loginName", example = "admin")
+//    @Parameter(name = "password", example = "IP3ccke3GhH45iGHB5qP9p7iZw6xUyj28Ju10rnBiPKOI35sc%2BjI7%2FdsjOkHWMfUwGYGfz8ik31HC2Ruk%2Fhkd9f6RPULTHj7VpFdNdde2P9M4mQQnFBAiPM7VT9iW3RyCtPlJexQ3nAiA09OqG%2F0sIf1kcyveSrulxembARDbDo%3D")
+//    @SaIgnore
+//    @OperationLog(type = SysOperationLogType.LOGIN_MOBILE, saveResponse = false)
+//    @PostMapping("/doMobileLogin")
+//    public ResponseResult<JSONObject> doMobileLogin(
+//            @MyRequestBody String loginName,
+//            @MyRequestBody String password) throws UnsupportedEncodingException {
+//        if (MyCommonUtil.existBlankArgument(loginName, password)) {
+//            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
+//        }
+//        ResponseResult<SysUser> verifyResult = this.verifyAndHandleLoginUser(loginName, password);
+//        if (!verifyResult.isSuccess()) {
+//            return ResponseResult.errorFrom(verifyResult);
+//        }
+//        JSONObject jsonData = this.buildMobileLoginDataAndLogin(verifyResult.getData());
+//        return ResponseResult.success(jsonData);
+//    }
+
+    /**
+     * 登出操作。同时将Session相关的信息从缓存中删除。
+     *
+     * @return 应答结果对象。
+     */
+    @OperationLog(type = SysOperationLogType.LOGOUT)
+    @PostMapping("/doLogout")
+    public ResponseResult<Void> doLogout() {
+        String sessionId = TokenData.takeFromRequest().getSessionId();
+        redissonClient.getBucket(TokenData.takeFromRequest().getMySessionId()).deleteAsync();
+        redissonClient.getBucket(RedisKeyUtil.makeSessionPermCodeKey(sessionId)).deleteAsync();
+        redissonClient.getBucket(RedisKeyUtil.makeSessionPermIdKey(sessionId)).deleteAsync();
+        sysDataPermService.removeDataPermCache(sessionId);
+        cacheHelper.removeAllSessionCache(sessionId);
+        StpUtil.logout();
+        return ResponseResult.success();
+    }
+
+    /**
+     * 在登录之后,通过token再次获取登录信息。
+     * 用于在当前浏览器登录系统后,在新tab页中可以免密登录。
+     *
+     * @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。
+     */
+    @GetMapping("/getLoginInfo")
+    public ResponseResult<JSONObject> getLoginInfo() {
+        TokenData tokenData = TokenData.takeFromRequest();
+        JSONObject jsonData = new JSONObject();
+        jsonData.put(SHOW_NAME_FIELD, tokenData.getShowName());
+        jsonData.put(IS_ADMIN, tokenData.getIsAdmin());
+        if (StrUtil.isNotBlank(tokenData.getHeadImageUrl())) {
+            jsonData.put(HEAD_IMAGE_URL_FIELD, tokenData.getHeadImageUrl());
+        }
+        return ResponseResult.success(jsonData);
+    }
+
+    /**
+     * 用户修改自己的密码。
+     *
+     * @param oldPass 原有密码。
+     * @param newPass 新密码。
+     * @return 应答结果对象。
+     */
+    @PostMapping("/changePassword")
+    public ResponseResult<Void> changePassword(
+            @MyRequestBody String oldPass, @MyRequestBody String newPass) throws UnsupportedEncodingException {
+        //校验密码是否为空
+        if (MyCommonUtil.existBlankArgument(newPass, oldPass)) {
+            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
+        }
+
+        TokenData tokenData = TokenData.takeFromRequest();
+        SysUserWeb user = sysUserWebService.getById(tokenData.getUserId());
+        oldPass = URLDecoder.decode(oldPass, StandardCharsets.UTF_8.name());
+        // NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。
+        // 执行RsaUtil工具类中的main函数,可以生成新的公钥和私钥。
+        oldPass = RsaUtil.decrypt(oldPass, ApplicationConstant.PRIVATE_KEY);
+        if (user == null || !passwordEncoder.matches(oldPass, user.getPassword())) {
+            return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD);
+        }
+        newPass = URLDecoder.decode(newPass, StandardCharsets.UTF_8.name());
+        newPass = RsaUtil.decrypt(newPass, ApplicationConstant.PRIVATE_KEY);
+        if (!sysUserWebService.changePassword(tokenData.getUserId(), newPass)) {
+            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
+        }
+        return ResponseResult.success();
+    }
+
+    /**
+     * 忘记密码。
+     * @return 应答结果对象。
+     */
+    @PostMapping("/findPassword")
+    public ResponseResult<String> findPassword(
+            @MyRequestBody String smsCode, @MyRequestBody String mobile) throws UnsupportedEncodingException {
+        //校验是否为空
+        if (MyCommonUtil.existBlankArgument(smsCode, mobile)) {
+            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
+        }
+
+        SysUserWeb sysUserByMobile = sysUserWebService.getSysUserByMobile(mobile);
+//        if (ObjectUtils.isEmpty(sysUserByMobile)){
+//            return ResponseResult.error(ErrorCodeEnum.INVALID_MOBILE_CODE);
+//        }
+
+        // 验证短信验证码
+        if (!smsService.validateSmsCode(smsCode,mobile)) {
+            return ResponseResult.error(ErrorCodeEnum.SMSCODE_ERR);
+        }
+
+        return ResponseResult.success(sysUserByMobile.getPassword());
+    }
+
+
+    /**
+     * 上传并修改用户头像。
+     *
+     * @param uploadFile 上传的头像文件。
+     */
+//    @PostMapping("/changeHeadImage")
+//    public void changeHeadImage(@RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {
+//        UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, HEAD_IMAGE_URL_FIELD);
+//        BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
+//        UploadResponseInfo responseInfo = upDownloader.doUpload(null,
+//                appConfig.getUploadFileBaseDir(), SysUser.class.getSimpleName(), HEAD_IMAGE_URL_FIELD, true, uploadFile);
+//        if (BooleanUtil.isTrue(responseInfo.getUploadFailed())) {
+//            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
+//                    ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage()));
+//            return;
+//        }
+//        responseInfo.setDownloadUri("/admin/upms/login/downloadHeadImage");
+//        String newHeadImage = JSONArray.toJSONString(CollUtil.newArrayList(responseInfo));
+//        if (!sysUserService.changeHeadImage(TokenData.takeFromRequest().getUserId(), newHeadImage)) {
+//            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN, ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST));
+//            return;
+//        }
+//        ResponseResult.output(ResponseResult.success(responseInfo));
+//    }
+
+    /**
+     * 下载用户头像。
+     *
+     * @param filename 文件名。如果没有提供该参数,就从当前记录的指定字段中读取。
+     * @param response Http 应答对象。
+     */
+    @GetMapping("/downloadHeadImage")
+    public void downloadHeadImage(String filename, HttpServletResponse response) {
+        try {
+            UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, HEAD_IMAGE_URL_FIELD);
+            BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
+            upDownloader.doDownload(appConfig.getUploadFileBaseDir(),
+                    SysUser.class.getSimpleName(), HEAD_IMAGE_URL_FIELD, filename, true, response);
+        } catch (Exception e) {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            log.error(e.getMessage(), e);
+        }
+    }
+
+//    private ResponseResult<SysUser> verifyAndHandleLoginUser(
+//            String loginName, String password) throws UnsupportedEncodingException {
+//        String errorMessage;
+//        SysUser user = sysUserService.getSysUserByLoginName(loginName);
+//        password = URLDecoder.decode(password, StandardCharsets.UTF_8.name());
+//        // NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。
+//        // 执行RsaUtil工具类中的main函数,可以生成新的公钥和私钥。
+//        password = RsaUtil.decrypt(password, ApplicationConstant.PRIVATE_KEY);
+//        if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
+//            return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD);
+//        }
+//        if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) {
+//            errorMessage = "登录失败,用户账号被锁定!";
+//            return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage);
+//        }
+//        if (BooleanUtil.isTrue(appConfig.getExcludeLogin())) {
+//            String deviceType = MyCommonUtil.getDeviceTypeWithString();
+//            LoginUserInfo userInfo = BeanUtil.copyProperties(user, LoginUserInfo.class);
+//            String loginId = SaTokenUtil.makeLoginId(userInfo);
+//            StpUtil.kickout(loginId, deviceType);
+//        }
+//        return ResponseResult.success(user);
+//    }
+
+    private JSONObject buildLoginDataAndLogin(SysUserWeb user) {
+        TokenData tokenData = this.loginAndCreateToken(user);
+        // 这里手动将TokenData存入request,便于OperationLogAspect统一处理操作日志。
+        TokenData.addToRequest(tokenData);
+        JSONObject jsonData = this.createResponseData(user);
+        Collection<SysMenu> allMenuList;
+        return jsonData;
+    }
+
+    private JSONObject buildMobileLoginDataAndLogin(SysUserWeb user) {
+        TokenData tokenData = this.loginAndCreateToken(user);
+        // 这里手动将TokenData存入request,便于OperationLogAspect统一处理操作日志。
+        TokenData.addToRequest(tokenData);
+        JSONObject jsonData = this.createResponseData(user);
+        List<MobileEntry> mobileEntryList;
+
+        return jsonData;
+    }
+
+    private TokenData loginAndCreateToken(SysUserWeb user) {
+        String deviceType = MyCommonUtil.getDeviceTypeWithString();
+        LoginUserInfo userInfo = BeanUtil.copyProperties(user, LoginUserInfo.class);
+        String loginId = SaTokenUtil.makeLoginIdByWeb(userInfo);
+        StpUtil.login(loginId, deviceType);
+        SaSession session = StpUtil.getTokenSession();
+        TokenData tokenData = this.buildTokenData(user, session.getId(), StpUtil.getLoginDevice());
+        String mySessionId = RedisKeyUtil.getSessionIdPrefix(tokenData, user.getLoginName()) + MyCommonUtil.generateUuid();
+        tokenData.setMySessionId(mySessionId);
+        tokenData.setToken(session.getToken());
+        redissonClient.getBucket(mySessionId)
+                .set(JSON.toJSONString(tokenData), appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS);
+        session.set(TokenData.REQUEST_ATTRIBUTE_NAME, tokenData);
+        return tokenData;
+    }
+
+    private JSONObject createResponseData(SysUserWeb user) {
+        JSONObject jsonData = new JSONObject();
+        jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, StpUtil.getTokenValue());
+        jsonData.put(SHOW_NAME_FIELD, user.getShowName());
+        if (StrUtil.isNotBlank(user.getHeadImageUrl())) {
+            jsonData.put(HEAD_IMAGE_URL_FIELD, user.getHeadImageUrl());
+        }
+        return jsonData;
+    }
+
+    private void appendResponseMenuAndPermCodeData(
+            JSONObject responseData, Collection<SysMenu> allMenuList, Collection<String> menuCodeList) {
+        allMenuList.stream()
+                .filter(m -> m.getExtraObject() != null && StrUtil.isNotBlank(m.getExtraObject().getMenuCode()))
+                .forEach(m -> CollUtil.addAll(menuCodeList, m.getExtraObject().getMenuCode()));
+        List<SysMenu> menuList = allMenuList.stream()
+                .filter(m -> m.getMenuType() <= SysMenuType.TYPE_MENU).collect(Collectors.toList());
+        responseData.put("menuList", menuList);
+        responseData.put("permCodeList", menuCodeList);
+    }
+
+    private TokenData buildTokenData(SysUserWeb user, String sessionId, String deviceType) {
+        TokenData tokenData = new TokenData();
+        tokenData.setSessionId(sessionId);
+        tokenData.setUserId(user.getUserId());
+        tokenData.setLoginName(user.getLoginName());
+        tokenData.setShowName(user.getShowName());
+        tokenData.setLoginIp(IpUtil.getRemoteIpAddress(ContextUtil.getHttpRequest()));
+        tokenData.setLoginTime(new Date());
+        tokenData.setDeviceType(deviceType);
+        tokenData.setHeadImageUrl(user.getHeadImageUrl());
+        return tokenData;
+    }
+
+    private void putUserSysPermCache(String sessionId, Collection<String> permUrlSet) {
+        if (CollUtil.isEmpty(permUrlSet)) {
+            return;
+        }
+        String sessionPermKey = RedisKeyUtil.makeSessionPermIdKey(sessionId);
+        RSet<String> redisPermSet = redissonClient.getSet(sessionPermKey);
+        redisPermSet.addAll(permUrlSet);
+        redisPermSet.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS);
+    }
+
+    private void putUserSysPermCodeCache(String sessionId, Collection<String> permCodeSet) {
+        if (CollUtil.isEmpty(permCodeSet)) {
+            return;
+        }
+        String sessionPermCodeKey = RedisKeyUtil.makeSessionPermCodeKey(sessionId);
+        RSet<String> redisPermSet = redissonClient.getSet(sessionPermCodeKey);
+        redisPermSet.addAll(permCodeSet);
+        redisPermSet.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS);
+    }
+
+    private OnlinePermData getOnlineMenuPermData(Collection<SysMenu> allMenuList) {
+        List<SysMenu> onlineMenuList = allMenuList.stream()
+                .filter(m -> m.getOnlineFormId() != null && m.getMenuType().equals(SysMenuType.TYPE_BUTTON))
+                .collect(Collectors.toList());
+        if (CollUtil.isEmpty(onlineMenuList)) {
+            return new OnlinePermData();
+        }
+        Set<Long> formIds = allMenuList.stream()
+                .filter(m -> m.getOnlineFormId() != null
+                        && m.getOnlineFlowEntryId() == null
+                        && m.getMenuType().equals(SysMenuType.TYPE_MENU))
+                .map(SysMenu::getOnlineFormId)
+                .collect(Collectors.toSet());
+        Set<Long> viewFormIds = onlineMenuList.stream()
+                .filter(m -> m.getOnlineMenuPermType() == SysOnlineMenuPermType.TYPE_VIEW)
+                .map(SysMenu::getOnlineFormId)
+                .collect(Collectors.toSet());
+        Set<Long> editFormIds = onlineMenuList.stream()
+                .filter(m -> m.getOnlineMenuPermType() == SysOnlineMenuPermType.TYPE_EDIT)
+                .map(SysMenu::getOnlineFormId)
+                .collect(Collectors.toSet());
+        Map<String, Object> permDataMap =
+                onlineOperationService.calculatePermData(formIds, viewFormIds, editFormIds);
+        OnlinePermData permData = BeanUtil.mapToBean(permDataMap, OnlinePermData.class, false, null);
+        permData.permUrlSet.addAll(permData.onlineWhitelistUrls);
+        return permData;
+    }
+
+    private OnlinePermData getFlowOnlineMenuPermData(Collection<SysMenu> allMenuList) {
+        List<SysMenu> flowOnlineMenuList = allMenuList.stream()
+                .filter(m -> m.getOnlineFlowEntryId() != null).collect(Collectors.toList());
+        Set<Long> entryIds = flowOnlineMenuList.stream()
+                .map(SysMenu::getOnlineFlowEntryId).collect(Collectors.toSet());
+        List<Map<String, Object>> flowPermDataList = flowOnlineOperationService.calculatePermData(entryIds);
+        List<OnlineFlowPermData> flowOnlinePermDataList =
+                MyModelUtil.mapToBeanList(flowPermDataList, OnlineFlowPermData.class);
+        OnlinePermData permData = new OnlinePermData();
+        flowOnlinePermDataList.forEach(perm -> {
+            permData.permCodeSet.addAll(perm.getPermCodeList());
+            permData.permUrlSet.addAll(perm.getPermList());
+        });
+        return permData;
+    }
+
+    private Set<String> getReportMenuPermData(Collection<SysMenu> allMenuList) {
+        Set<String> permSet = new HashSet<>();
+        List<SysMenu> reportMenuList = allMenuList.stream()
+                .filter(m -> m.getReportPageId() != null).collect(Collectors.toList());
+        if (CollUtil.isEmpty(reportMenuList)) {
+            return permSet;
+        }
+        Set<Long> pageIds = reportMenuList.stream().map(SysMenu::getReportPageId).collect(Collectors.toSet());
+        Map<Long, Set<String>> reportPermDataMap = reportOperationService.calculatePermData(pageIds);
+        for (Long pageId : pageIds) {
+            CollUtil.addAll(permSet, reportPermDataMap.get(pageId));
+        }
+        return permSet;
+    }
+
+    private Set<String> getOnlineMobileEntryPermData(Collection<MobileEntry> mobileEntryList) {
+        List<MobileEntry> onlineMobileEntryList = mobileEntryList.stream()
+                .filter(m -> m.getExtraObject() != null
+                        && m.getExtraObject().getOnlineFormId() != null
+                        && m.getExtraObject().getOnlineFlowEntryId() == null)
+                .collect(Collectors.toList());
+        if (CollUtil.isEmpty(onlineMobileEntryList)) {
+            return CollUtil.newHashSet();
+        }
+        Set<Long> onlineFormIds = onlineMobileEntryList.stream()
+                .map(m -> m.getExtraObject().getOnlineFormId()).collect(Collectors.toSet());
+        Map<String, Object> permDataMap =
+                onlineOperationService.calculatePermData(onlineFormIds, onlineFormIds, onlineFormIds);
+        OnlinePermData permData = BeanUtil.mapToBean(permDataMap, OnlinePermData.class, false, null);
+        permData.permUrlSet.addAll(permData.onlineWhitelistUrls);
+        return permData.permUrlSet;
+    }
+
+    private Set<String> getFlowOnlineMobileEntryPermData(Collection<MobileEntry> mobileEntryList) {
+        List<MobileEntry> flowOnlineMobileEntryList = mobileEntryList.stream()
+                .filter(m -> m.getExtraData() != null && m.getExtraObject().getOnlineFlowEntryId() != null)
+                .collect(Collectors.toList());
+        Set<Long> entryIds = flowOnlineMobileEntryList.stream()
+                .map(m -> m.getExtraObject().getOnlineFlowEntryId()).collect(Collectors.toSet());
+        List<Map<String, Object>> flowPermDataList = flowOnlineOperationService.calculatePermData(entryIds);
+        List<OnlineFlowPermData> flowOnlinePermDataList =
+                MyModelUtil.mapToBeanList(flowPermDataList, OnlineFlowPermData.class);
+        Set<String> permSet = new HashSet<>();
+        flowOnlinePermDataList.forEach(perm -> permSet.addAll(perm.getPermList()));
+        return permSet;
+    }
+
+    private Set<String> getReportMobileEntryPermData(Collection<MobileEntry> mobileEntryList) {
+        Set<String> permSet = new HashSet<>();
+        List<MobileEntry> reportMobileEntryList = mobileEntryList.stream()
+                .filter(m -> m.getExtraObject() != null && m.getExtraObject().getReportPageId() != null)
+                .collect(Collectors.toList());
+        if (CollUtil.isEmpty(reportMobileEntryList)) {
+            return permSet;
+        }
+        Set<Long> pageIds = reportMobileEntryList.stream()
+                .map(m -> m.getExtraObject().getReportPageId()).collect(Collectors.toSet());
+        Map<Long, Set<String>> reportPermDataMap = reportOperationService.calculatePermData(pageIds);
+        for (Long pageId : pageIds) {
+            CollUtil.addAll(permSet, reportPermDataMap.get(pageId));
+        }
+        return permSet;
+    }
+
+    static class OnlinePermData {
+        public final Set<String> permCodeSet = new HashSet<>();
+        public final Set<String> permUrlSet = new HashSet<>();
+        public final List<String> onlineWhitelistUrls = new LinkedList<>();
+    }
+
+    @Data
+    static class OnlineFlowPermData {
+        private List<String> permCodeList;
+        private List<String> permList;
+    }
+}

+ 5 - 0
application-webadmin/src/main/java/com/tourism/webadmin/back/service/CaptchaService.java

@@ -0,0 +1,5 @@
+package com.tourism.webadmin.back.service;
+
+public interface CaptchaService {
+    Boolean validateCaptcha(String captchaCode,String mobile);
+}

+ 6 - 0
application-webadmin/src/main/java/com/tourism/webadmin/back/service/SmsService.java

@@ -0,0 +1,6 @@
+package com.tourism.webadmin.back.service;
+
+public interface SmsService {
+
+    Boolean validateSmsCode(String smsCode ,String phone);
+}

+ 22 - 0
application-webadmin/src/main/java/com/tourism/webadmin/back/service/impl/CaptchaServiceImpl.java

@@ -0,0 +1,22 @@
+package com.tourism.webadmin.back.service.impl;
+
+import com.tourism.webadmin.back.service.CaptchaService;
+
+public class CaptchaServiceImpl implements CaptchaService {
+
+    /**
+     * 校验图形验证码
+     * @param captchaCode
+     * @return
+     */
+    @Override
+    public Boolean validateCaptcha(String captchaCode,String mobile) {
+        //校验图形验证码
+
+        //发送手机验证码
+
+        //存redis验证码
+
+        return null;
+    }
+}

+ 41 - 0
application-webadmin/src/main/java/com/tourism/webadmin/back/service/impl/SmsServiceImpl.java

@@ -0,0 +1,41 @@
+package com.tourism.webadmin.back.service.impl;
+
+import com.tourism.common.core.constant.ApplicationConstant;
+import com.tourism.common.core.util.RsaUtil;
+import com.tourism.webadmin.back.service.SmsService;
+import io.lettuce.core.RedisClient;
+import io.lettuce.core.api.StatefulRedisConnection;
+import io.lettuce.core.api.sync.RedisCommands;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+@Slf4j
+@Service("smsService")
+public class SmsServiceImpl implements SmsService {
+
+    /**
+     * 校验短信验证码
+     * @param smsCode
+     * @return
+     */
+    @Override
+    public Boolean validateSmsCode(String smsCode,String phone) {
+        RedisClient client = RedisClient.create("redis://localhost");
+        // 创建连接
+        StatefulRedisConnection<String, String> connection = client.connect();
+        // 创建命令对象
+        RedisCommands<String, String> commands = connection.sync();
+        // 通过键获取值
+        String value = commands.get(phone);
+
+
+        if (ObjectUtils.isEmpty(value) || !value.equals(smsCode)){
+            return false;
+        }
+        // 关闭连接
+        connection.close();
+        client.shutdown();
+        return true;
+    }
+}

+ 189 - 0
application-webadmin/src/main/java/com/tourism/webadmin/upms/dao/SysUserWebMapper.java

@@ -0,0 +1,189 @@
+package com.tourism.webadmin.upms.dao;
+
+import com.tourism.common.core.base.dao.BaseDaoMapper;
+import com.tourism.webadmin.upms.model.SysUser;
+import com.tourism.webadmin.upms.model.SysUserWeb;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.*;
+
+/**
+ * 用户管理数据操作访问接口。
+ *
+ * @author 吃饭睡觉
+ * @date 2024-09-06
+ */
+public interface SysUserWebMapper extends BaseDaoMapper<SysUserWeb> {
+
+    /**
+     * 批量插入对象列表。
+     *
+     * @param sysUserList 新增对象列表。
+     */
+    void insertList(List<SysUser> sysUserList);
+
+    /**
+     * 获取过滤后的对象列表。
+     *
+     * @param sysUserFilter 主表过滤对象。
+     * @param orderBy 排序字符串,order by从句的参数。
+     * @return 对象列表。
+     */
+    List<SysUser> getSysUserList(
+            @Param("sysUserFilter") SysUser sysUserFilter, @Param("orderBy") String orderBy);
+
+    /**
+     * 根据部门Id集合,获取关联的用户列表。
+     *
+     * @param deptIds       关联的部门Id集合。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和部门Id集合关联的用户列表。
+     */
+    List<SysUser> getSysUserListByDeptIds(
+            @Param("deptIds") Set<Long> deptIds,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据登录名集合,获取关联的用户列表。
+     * @param loginNames    登录名集合。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和登录名集合关联的用户列表。
+     */
+    List<SysUser> getSysUserListByLoginNames(
+            @Param("loginNames") List<String> loginNames,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据角色Id,获取关联的用户列表。
+     *
+     * @param roleId        关联的角色Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和角色Id关联的用户列表。
+     */
+    List<SysUser> getSysUserListByRoleId(
+            @Param("roleId") Long roleId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据角色Id集合,获取去重后的用户Id列表。
+     *
+     * @param roleIds       关联的角色Id集合。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和角色Id集合关联的去重后的用户Id列表。
+     */
+    List<Long> getUserIdListByRoleIds(
+            @Param("roleIds") Set<Long> roleIds,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据角色Id,获取和当前角色Id没有建立多对多关联关系的用户列表。
+     *
+     * @param roleId        关联的角色Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy order by从句的参数。
+     * @return 和RoleId没有建立关联关系的用户列表。
+     */
+    List<SysUser> getNotInSysUserListByRoleId(
+            @Param("roleId") Long roleId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据数据权限Id,获取关联的用户列表。
+     *
+     * @param dataPermId    关联的数据权限Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy order by从句的参数。
+     * @return 和DataPermId关联的用户列表。
+     */
+    List<SysUser> getSysUserListByDataPermId(
+            @Param("dataPermId") Long dataPermId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据数据权限Id,获取和当前数据权限Id没有建立多对多关联关系的用户列表。
+     *
+     * @param dataPermId    关联的数据权限Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy order by从句的参数。
+     * @return 和DataPermId没有建立关联关系的用户列表。
+     */
+    List<SysUser> getNotInSysUserListByDataPermId(
+            @Param("dataPermId") Long dataPermId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据部门岗位Id集合,获取关联的去重后的用户Id列表。
+     *
+     * @param deptPostIds   关联的部门岗位Id集合。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和部门岗位Id集合关联的去重后的用户Id列表。
+     */
+    List<Long> getUserIdListByDeptPostIds(
+            @Param("deptPostIds") Set<Long> deptPostIds,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据部门岗位Id,获取关联的用户列表。
+     *
+     * @param deptPostId    关联的部门岗位Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和部门岗位Id关联的用户列表。
+     */
+    List<SysUser> getSysUserListByDeptPostId(
+            @Param("deptPostId") Long deptPostId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据部门岗位Id,获取和当前部门岗位Id没有建立多对多关联关系的用户列表。
+     *
+     * @param deptPostId    关联的部门岗位Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和deptPostId没有建立关联关系的用户列表。
+     */
+    List<SysUser> getNotInSysUserListByDeptPostId(
+            @Param("deptPostId") Long deptPostId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据岗位Id集合,获取关联的去重后的用户Id列表。
+     *
+     * @param postIds       关联的岗位Id集合。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和岗位Id集合关联的去重后的用户Id列表。
+     */
+    List<Long> getUserIdListByPostIds(
+            @Param("postIds") Set<Long> postIds,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+
+    /**
+     * 根据岗位Id,获取关联的用户列表。
+     *
+     * @param postId        关联的岗位Id。
+     * @param sysUserFilter 用户过滤条件对象。
+     * @param orderBy       order by从句的参数。
+     * @return 和岗位Id关联的用户列表。
+     */
+    List<SysUser> getSysUserListByPostId(
+            @Param("postId") Long postId,
+            @Param("sysUserFilter") SysUser sysUserFilter,
+            @Param("orderBy") String orderBy);
+}

+ 288 - 0
application-webadmin/src/main/java/com/tourism/webadmin/upms/dao/mapper/SysUserWebMapper.xml

@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.tourism.webadmin.upms.dao.SysUserWebMapper">
+    <resultMap id="BaseResultMap" type="com.tourism.webadmin.upms.model.SysUserWeb">
+        <id column="user_id" jdbcType="BIGINT" property="userId"/>
+        <result column="login_name" jdbcType="VARCHAR" property="loginName"/>
+        <result column="password" jdbcType="VARCHAR" property="password"/>
+        <result column="show_name" jdbcType="VARCHAR" property="showName"/>
+        <result column="head_image_url" jdbcType="VARCHAR" property="headImageUrl"/>
+        <result column="user_status" jdbcType="INTEGER" property="userStatus"/>
+        <result column="email" jdbcType="VARCHAR" property="email"/>
+        <result column="mobile" jdbcType="VARCHAR" property="mobile"/>
+        <result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
+        <result column="update_user_id" jdbcType="BIGINT" property="updateUserId"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
+        <result column="deleted_flag" jdbcType="INTEGER" property="deletedFlag"/>
+    </resultMap>
+
+    <insert id="insertList">
+        INSERT INTO sys_user_web
+            (user_id,
+            login_name,
+            password,
+            show_name,
+            head_image_url,
+            user_status,
+            email,
+            mobile,
+            create_user_id,
+            update_user_id,
+            create_time,
+            update_time,
+            deleted_flag)
+        VALUES
+        <foreach collection="list" index="index" item="item" separator="," >
+            (#{item.userId},
+            #{item.loginName},
+            #{item.password},
+            #{item.showName},
+            #{item.headImageUrl},
+            #{item.userStatus},
+            #{item.email},
+            #{item.mobile},
+            #{item.createUserId},
+            #{item.updateUserId},
+            #{item.createTime},
+            #{item.updateTime},
+            #{item.deletedFlag})
+        </foreach>
+    </insert>
+
+    <!-- 如果有逻辑删除字段过滤,请写到这里 -->
+    <sql id="filterRef">
+        <!-- 这里必须加上全包名,否则当filterRef被其他Mapper.xml包含引用的时候,就会调用Mapper.xml中的该SQL片段 -->
+        <include refid="com.tourism.webadmin.upms.dao.SysUserMapper.inputFilterRef"/>
+        AND sys_user.deleted_flag = ${@com.tourism.common.core.constant.GlobalDeletedFlag@NORMAL}
+    </sql>
+
+    <!-- 这里仅包含调用接口输入的主表过滤条件 -->
+    <sql id="inputFilterRef">
+        <if test="sysUserFilter != null">
+            <if test="sysUserFilter.loginName != null and sysUserFilter.loginName != ''">
+                <bind name = "safeSysUserLoginName" value = "'%' + sysUserFilter.loginName + '%'" />
+                AND sys_user.login_name LIKE #{safeSysUserLoginName}
+            </if>
+            <if test="sysUserFilter.deptId != null">
+                AND (EXISTS (SELECT 1 FROM sys_dept_relation WHERE
+                        sys_dept_relation.parent_dept_id = #{sysUserFilter.deptId}
+                        AND sys_user.dept_id = sys_dept_relation.dept_id))
+            </if>
+            <if test="sysUserFilter.showName != null and sysUserFilter.showName != ''">
+                <bind name = "safeSysUserShowName" value = "'%' + sysUserFilter.showName + '%'" />
+                AND sys_user.show_name LIKE #{safeSysUserShowName}
+            </if>
+            <if test="sysUserFilter.userStatus != null">
+                AND sys_user.user_status = #{sysUserFilter.userStatus}
+            </if>
+            <if test="sysUserFilter.createTimeStart != null and sysUserFilter.createTimeStart != ''">
+                AND sys_user.create_time &gt;= #{sysUserFilter.createTimeStart}
+            </if>
+            <if test="sysUserFilter.createTimeEnd != null and sysUserFilter.createTimeEnd != ''">
+                AND sys_user.create_time &lt;= #{sysUserFilter.createTimeEnd}
+            </if>
+        </if>
+    </sql>
+
+    <select id="getSysUserList" resultMap="BaseResultMap" parameterType="com.tourism.webadmin.upms.model.SysUser">
+        SELECT * FROM sys_user
+        <where>
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getSysUserListByDeptIds" resultMap="BaseResultMap">
+        SELECT * FROM sys_user
+        <where>
+            <if test="deptIds != null">
+                AND (EXISTS (SELECT 1 FROM sys_dept_relation WHERE
+                        sys_dept_relation.parent_dept_id IN
+                        <foreach collection="deptIds" item="item" separator="," open="(" close=")">
+                            #{item}
+                        </foreach>
+                        AND sys_user.dept_id = sys_dept_relation.dept_id))
+            </if>
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getSysUserListByLoginNames" resultMap="BaseResultMap">
+        SELECT * FROM sys_user
+        <where>
+            <if test="loginNames != null">
+                AND sys_user.login_name IN
+                <foreach collection="loginNames" item="item" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getUserIdListByRoleIds" resultType="java.lang.Long">
+        SELECT
+            DISTINCT sys_user.user_id
+        FROM
+            sys_user_role,
+            sys_user
+        <where>
+            AND sys_user_role.role_id IN
+            <foreach collection="roleIds" item="item" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+            AND sys_user_role.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getSysUserListByRoleId" resultMap="BaseResultMap">
+        SELECT
+            sys_user.*
+        FROM
+            sys_user_role,
+            sys_user
+        <where>
+            AND sys_user_role.role_id = #{roleId}
+            AND sys_user_role.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getNotInSysUserListByRoleId" resultMap="BaseResultMap">
+        SELECT * FROM sys_user
+        <where>
+            NOT EXISTS (SELECT * FROM sys_user_role
+                WHERE sys_user_role.role_id = #{roleId} AND sys_user_role.user_id = sys_user.user_id)
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getSysUserListByDataPermId" resultMap="BaseResultMap">
+        SELECT
+            sys_user.*
+        FROM
+            sys_data_perm_user,
+            sys_user
+        <where>
+            AND sys_data_perm_user.data_perm_id = #{dataPermId}
+            AND sys_data_perm_user.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getNotInSysUserListByDataPermId" resultMap="BaseResultMap">
+        SELECT * FROM sys_user
+        <where>
+            NOT EXISTS (SELECT * FROM sys_data_perm_user
+                WHERE sys_data_perm_user.data_perm_id = #{dataPermId} AND sys_data_perm_user.user_id = sys_user.user_id)
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getUserIdListByDeptPostIds" resultType="java.lang.Long">
+        SELECT
+            DISTINCT sys_user.user_id
+        FROM
+            sys_user_post,
+            sys_user
+        <where>
+            AND sys_user_post.dept_post_id IN
+            <foreach collection="deptPostIds" item="item" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+            AND sys_user_post.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getSysUserListByDeptPostId" resultMap="BaseResultMap">
+        SELECT
+            sys_user.*
+        FROM
+            sys_user_post,
+            sys_user
+        <where>
+            AND sys_user_post.dept_post_id = #{deptPostId}
+            AND sys_user_post.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getNotInSysUserListByDeptPostId" resultMap="BaseResultMap">
+        SELECT * FROM sys_user
+        <where>
+            NOT EXISTS (SELECT * FROM sys_user_post
+                WHERE sys_user_post.dept_post_id = #{deptPostId} AND sys_user_post.user_id = sys_user.user_id)
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getUserIdListByPostIds" resultType="java.lang.Long">
+        SELECT
+            DISTINCT sys_user.user_id
+        FROM
+            sys_user_post,
+            sys_user
+        <where>
+            AND sys_user_post.post_id IN
+            <foreach collection="postIds" item="item" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+            AND sys_user_post.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+
+    <select id="getSysUserListByPostId" resultMap="BaseResultMap">
+        SELECT
+            sys_user.*
+        FROM
+            sys_user_post,
+            sys_user
+        <where>
+            AND sys_user_post.post_id = #{postId}
+            AND sys_user_post.user_id = sys_user.user_id
+            <include refid="filterRef"/>
+        </where>
+        <if test="orderBy != null and orderBy != ''">
+            ORDER BY ${orderBy}
+        </if>
+    </select>
+</mapper>

+ 68 - 0
application-webadmin/src/main/java/com/tourism/webadmin/upms/dto/SysUserWebDto.java

@@ -0,0 +1,68 @@
+package com.tourism.webadmin.upms.dto;
+
+import com.tourism.common.core.validator.AddGroup;
+import com.tourism.common.core.validator.UpdateGroup;
+import com.tourism.common.core.validator.ConstDictRef;
+import com.tourism.webadmin.upms.model.constant.SysUserType;
+import com.tourism.webadmin.upms.model.constant.SysUserStatus;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.*;
+
+/**
+ * 用户管理Dto对象。
+ *
+ * @author 吃饭睡觉
+ * @date 2024-09-06
+ */
+@Schema(description = "SysUserWebDto对象")
+@Data
+public class SysUserWebDto {
+
+    /**
+     * 登录用户名。
+     */
+    @NotBlank(message = "数据验证失败,登录用户名不能为空!")
+    private String loginName;
+
+    /**
+     * 用户密码。
+     */
+    @NotBlank(message = "数据验证失败,用户密码不能为空!", groups = {AddGroup.class})
+    private String password;
+
+
+    /**
+     * 用户显示名称。
+     */
+    @NotBlank(message = "数据验证失败,用户显示名称不能为空!")
+    private String showName;
+
+    /**
+     * 用户头像的Url。
+     */
+    @Schema(description = "用户头像的Url。")
+    private String headImageUrl;
+
+    /**
+     * 用户邮箱。
+     */
+    @Schema(description = "用户邮箱。")
+    private String email;
+
+    /**
+     * 用户手机。
+     */
+    @Schema(description = "用户手机。")
+    @NotBlank(message = "数据验证失败,用户手机不能为空!")
+    private String mobile;
+
+    /**
+     * 短信验证码。
+     */
+    @Schema(description = "短信验证码。")
+    @NotBlank(message = "数据验证失败,短信验证码不能为空!")
+    private String smsCode;
+}

+ 98 - 0
application-webadmin/src/main/java/com/tourism/webadmin/upms/model/SysUserWeb.java

@@ -0,0 +1,98 @@
+package com.tourism.webadmin.upms.model;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.tourism.webadmin.upms.model.constant.SysUserType;
+import com.tourism.webadmin.upms.model.constant.SysUserStatus;
+import com.tourism.common.core.upload.UploadStoreTypeEnum;
+import com.tourism.common.core.annotation.*;
+import com.tourism.common.core.base.model.BaseModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Map;
+import java.util.List;
+
+/**
+ * 用户管理实体对象。
+ *
+ * @author 吃饭睡觉
+ * @date 2024-09-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName(value = "sys_user_web")
+public class SysUserWeb extends BaseModel {
+
+    /**
+     * 用户Id。
+     */
+    @TableId(value = "user_id")
+    private Long userId;
+
+    /**
+     * 登录用户名。
+     */
+    @TableField(value = "login_name")
+    private String loginName;
+
+    /**
+     * 用户密码。
+     */
+    @TableField(value = "password")
+    private String password;
+
+    /**
+     * 用户显示名称。
+     */
+    @TableField(value = "show_name")
+    private String showName;
+
+    /**
+     * 用户头像的Url。
+     */
+    @UploadFlagColumn(storeType = UploadStoreTypeEnum.LOCAL_SYSTEM)
+    @TableField(value = "head_image_url")
+    private String headImageUrl;
+
+    /**
+     * 用户状态(0: 正常 1: 锁定)。
+     */
+    @TableField(value = "user_status")
+    private Integer userStatus;
+
+    /**
+     * 用户邮箱。
+     */
+    @TableField(value = "email")
+    private String email;
+
+    /**
+     * 用户手机。
+     */
+    @TableField(value = "mobile")
+    private String mobile;
+
+    /**
+     * 逻辑删除标记字段(1: 正常 -1: 已删除)。
+     */
+    @TableLogic
+    @TableField(value = "deleted_flag")
+    private Integer deletedFlag;
+
+    /**
+     * createTime 范围过滤起始值(>=)。
+     */
+    @TableField(exist = false)
+    private String createTimeStart;
+
+    /**
+     * createTime 范围过滤结束值(<=)。
+     */
+    @TableField(exist = false)
+    private String createTimeEnd;
+
+    /**
+     * 国家
+     */
+    private String country;
+}

+ 44 - 0
application-webadmin/src/main/java/com/tourism/webadmin/upms/service/SysUserWebService.java

@@ -0,0 +1,44 @@
+package com.tourism.webadmin.upms.service;
+
+import com.tourism.common.core.base.service.IBaseService;
+import com.tourism.common.core.object.CallResult;
+import com.tourism.webadmin.upms.model.SysUser;
+import com.tourism.webadmin.upms.model.SysUserWeb;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 用户管理数据操作服务接口。
+ *
+ * @author 吃饭睡觉
+ * @date 2024-09-06
+ */
+public interface SysUserWebService extends IBaseService<SysUserWeb, Long> {
+
+    /**
+     * 获取指定登录名的用户对象。
+     *
+     * @param loginName 指定登录用户名。
+     * @return 用户对象。
+     */
+    SysUserWeb getSysUserByLoginName(String loginName);
+
+
+    SysUserWeb getSysUserByMobile(String mobile);
+
+    /**
+     * 保存新增的用户对象。
+     *
+     * @param user          新增的用户对象。
+     * @return 新增后的用户对象。
+     */
+    SysUserWeb saveNew(SysUserWeb user);
+    /**
+     * 修改用户密码。
+     * @param userId  用户主键Id。
+     * @param newPass 新密码。
+     * @return 成功返回true,否则false。
+     */
+    boolean changePassword(Long userId, String newPass);
+}

+ 109 - 0
application-webadmin/src/main/java/com/tourism/webadmin/upms/service/impl/SysUserWebServiceImpl.java

@@ -0,0 +1,109 @@
+package com.tourism.webadmin.upms.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.*;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.github.pagehelper.page.PageMethod;
+import com.tourism.webadmin.upms.service.*;
+import com.tourism.webadmin.upms.dao.*;
+import com.tourism.webadmin.upms.model.*;
+import com.tourism.webadmin.upms.model.constant.SysUserStatus;
+import com.tourism.common.ext.util.BizWidgetDatasourceExtHelper;
+import com.tourism.common.ext.base.BizWidgetDatasource;
+import com.tourism.common.ext.constant.BizWidgetDatasourceType;
+import com.tourism.common.core.base.dao.BaseDaoMapper;
+import com.tourism.common.core.constant.UserFilterGroup;
+import com.tourism.common.core.constant.GlobalDeletedFlag;
+import com.tourism.common.core.object.*;
+import com.tourism.common.core.base.service.BaseService;
+import com.tourism.common.core.util.MyModelUtil;
+import com.tourism.common.core.util.MyPageUtil;
+import com.tourism.common.sequence.wrapper.IdGeneratorWrapper;
+import com.github.pagehelper.Page;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import jakarta.annotation.PostConstruct;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 用户管理数据操作服务类。
+ *
+ * @author 吃饭睡觉
+ * @date 2024-09-06
+ */
+@Slf4j
+@Service("sysUserWebService")
+public class SysUserWebServiceImpl extends BaseService<SysUserWeb, Long> implements SysUserWebService, BizWidgetDatasource {
+
+    @Autowired
+    private SysUserWebMapper sysUserWebMapper;
+    @Autowired
+    private IdGeneratorWrapper idGenerator;
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    /**
+     * 获取指定登录名的用户对象。
+     *
+     * @param loginName 指定登录用户名。
+     * @return 用户对象。
+     */
+    @Override
+    public SysUserWeb getSysUserByLoginName(String loginName) {
+        SysUserWeb filter = new SysUserWeb();
+        filter.setLoginName(loginName);
+        return sysUserWebMapper.selectOne(new QueryWrapper<>(filter));
+    }
+
+    @Override
+    public SysUserWeb getSysUserByMobile(String mobile) {
+        SysUserWeb filter = new SysUserWeb();
+        filter.setMobile(mobile);
+        return sysUserWebMapper.selectOne(new QueryWrapper<>(filter));
+    }
+
+    @Override
+    public SysUserWeb saveNew(SysUserWeb user) {
+        user.setUserId(idGenerator.nextLongId());
+        user.setPassword(user.getPassword());
+        user.setUserStatus(SysUserStatus.STATUS_NORMAL);
+        user.setDeletedFlag(GlobalDeletedFlag.NORMAL);
+        user.setCreateUserId(user.getUserId());
+        user.setCreateTime(new Date());
+        user.setUpdateUserId(user.getUserId());
+        user.setUpdateTime(new Date());
+        sysUserWebMapper.insert(user);
+        return user;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean changePassword(Long userId, String newPass) {
+        SysUserWeb updatedUser = new SysUserWeb();
+        updatedUser.setUserId(userId);
+        updatedUser.setPassword(passwordEncoder.encode(newPass));
+        return sysUserWebMapper.updateById(updatedUser) == 1;
+    }
+    @Override
+    protected BaseDaoMapper<SysUserWeb> mapper() {
+        return null;
+    }
+
+    @Override
+    public MyPageData<Map<String, Object>> getDataList(String widgetType, Map<String, Object> filter, MyOrderParam orderParam, MyPageParam pageParam) {
+        return null;
+    }
+
+    @Override
+    public List<Map<String, Object>> getDataListWithInList(String widgetType, String fieldName, List<String> fieldValues) {
+        return null;
+    }
+}

+ 2 - 0
common/common-core/src/main/java/com/tourism/common/core/constant/ApplicationConstant.java

@@ -145,6 +145,8 @@ public final class ApplicationConstant {
      */
     public static final String PRIVATE_KEY =
             "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKkLhAydtOtA4WuIkkIIUVaGWu4ElOEAQF9GTulHHWOwCHI1UvcKolvS1G+mdsKcmGtEAQ92AUde/kDRGu8Wn7kLDtCgUfo72soHz7Qfv5pVB4ohMxQd/9cxeKjKbDoirhB9Z3xGF20zUozp4ZPLxpTtI7azr0xzUtd5+D/HfLDrAgMBAAECgYEApESZhDz4YyeAJiPnpJ06lS8oS2VOWzsIUs0av5uoloeoHXtt7Lx7u2kroHeNrl3Hy2yg7ypH4dgQkGHin3VHrVAgjG3TxhgBXIqqntzzk2AGJKBeIIkRX86uTvtKZyp3flUgcwcGmpepAHS1V1DPY3aVYvbcqAmoL6DX6VYN0NECQQDQUitMdC76lEtAr5/ywS0nrZJDo6U7eQ7ywx/eiJ+YmrSye8oorlAj1VBWG+Cl6jdHOHtTQyYv/tu71fjzQiJTAkEAz7wb47/vcSUpNWQxItFpXz0o6rbJh71xmShn1AKP7XptOVZGlW9QRYEzHabV9m/DHqI00cMGhHrWZAhCiTkUCQJAFsJjaJ7o4weAkTieyO7B+CvGZw1h5/V55Jvcx3s1tH5yb22G0Jr6tm9/r2isSnQkReutzZLwgR3e886UvD7lcQJAAUcD2OOuQkDbPwPNtYwaHMbQgJj9JkOI9kskUE5vuiMdltOr/XFAyhygRtdmy2wmhAK1VnDfkmL6/IR8fEGImQJABOB0KCalb0M8CPnqqHzozrD8gPObnIIr4aVvLIPATN2g7MM2N6F7JbI4RZFiKa92LV6bhQCY8OvHi5K2cgFpbw==";
+   public static final String PUBLIC_KEY =
+           "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpC4QMnbTrQOFriJJCCFFWhlruBJThAEBfRk7pRx1jsAhyNVL3CqJb0tRvpnbCnJhrRAEPdgFHXv5A0RrvFp+5Cw7QoFH6O9rKB8+0H7+aVQeKITMUHf/XMXioymw6Iq4QfWd8RhdtM1KM6eGTy8aU7SO2s69Mc1LXefg/x3yw6wIDAQAB";
     /**
      * SQL注入检测的正则对象。
      */

+ 2 - 0
common/common-core/src/main/java/com/tourism/common/core/constant/ErrorCodeEnum.java

@@ -39,6 +39,8 @@ public enum ErrorCodeEnum {
     INVALID_TENANT_CODE("指定的租户编码并不存在,请刷新后重试!"),
     INVALID_TENANT_STATUS("当前租户为不可用状态,请刷新后重试!"),
     INVALID_USER_TENANT("当前用户并不属于当前租户,请刷新后重试!"),
+    INVALID_MOBILE_CODE("手机号不存在!"),
+    SMSCODE_ERR("验证码错误,请重试!"),
 
     HAS_CHILDREN_DATA("数据验证失败,子数据存在,请刷新后重试!"),
     DATA_VALIDATED_FAILED("数据验证失败,请核对!"),

+ 23 - 16
common/common-core/src/main/java/com/tourism/common/core/util/RsaUtil.java

@@ -3,8 +3,12 @@ package com.tourism.common.core.util;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.crypto.asymmetric.KeyType;
 import cn.hutool.crypto.asymmetric.RSA;
+import com.tourism.common.core.constant.ApplicationConstant;
 import lombok.extern.slf4j.Slf4j;
 
+
+import java.net.URLDecoder;
+import java.net.URLEncoder;
 import java.security.*;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
@@ -79,24 +83,27 @@ public class RsaUtil {
     }
 
     public static void main(String[] args) throws Exception {
-        long temp = System.currentTimeMillis();
-        // 生成公钥和私钥
-        genKeyPair();
-        // 加密字符串
-        log.info("公钥:" + KEY_MAP.get(0));
-        log.info("私钥:" + KEY_MAP.get(1));
-        log.info("生成密钥消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
-        log.info("生成后的公钥前端使用!");
-        log.info("生成后的私钥后台使用!");
-        String message = "RSA测试ABCD~!@#$";
+//        long temp = System.currentTimeMillis();
+//        // 生成公钥和私钥
+//        genKeyPair();
+//        // 加密字符串
+//        log.info("公钥:" + KEY_MAP.get(0));
+//        log.info("私钥:" + KEY_MAP.get(1));
+//        log.info("生成密钥消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+//        log.info("生成后的公钥前端使用!");
+//        log.info("生成后的私钥后台使用!");
+        String message = "123456";
         log.info("原文:" + message);
-        temp = System.currentTimeMillis();
-        String messageEn = encrypt(message, KEY_MAP.get(0));
+//        temp = System.currentTimeMillis();
+        String messageEn = encrypt(message, ApplicationConstant.PUBLIC_KEY);
         log.info("密文:" + messageEn);
-        log.info("加密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
-        temp = System.currentTimeMillis();
-        String messageDe = decrypt(messageEn, KEY_MAP.get(1));
+        String encode = URLEncoder.encode(messageEn);
+        log.info("编码:"+encode);
+//        log.info("加密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+//        temp = System.currentTimeMillis();
+        String decode = URLDecoder.decode("qDs%2BJ0TC1sJrbppQzDyh0ivhmSV0JPGnqaB2aTWxlwVpbXk8DppD%2B0iO9x%2BdI9YDBb9MYqEGBTm8wpezmsEylLPcpUs4PO5UZgJ2sRDDq%2F%2Blz23Vv05nRlO41PWsof8nSoZ32rKh4krdXVIHt3Gpej528%2BywPkGtTRxSKC32kSE%3D");
+        String messageDe = decrypt(decode, ApplicationConstant.PRIVATE_KEY);
         log.info("解密:" + messageDe);
-        log.info("解密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+//        log.info("解密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
     }
 }

+ 8 - 0
common/common-log/src/main/java/com/tourism/common/log/model/constant/SysOperationLogType.java

@@ -21,6 +21,14 @@ public final class SysOperationLogType {
      */
     public static final int LOGIN_MOBILE = 1;
     /**
+     * 注册。
+     */
+    public static final int Register = 2;
+    /**
+     * 登录web。
+     */
+    public static final int LOGIN_WEB = 3;
+    /**
      * 登出。
      */
     public static final int LOGOUT = 5;

+ 11 - 1
common/common-satoken/src/main/java/com/tourism/common/satoken/util/SaTokenUtil.java

@@ -166,7 +166,17 @@ public class SaTokenUtil {
      */
     public static String makeLoginId(LoginUserInfo userInfo) {
         StringBuilder sb = new StringBuilder(128);
-        sb.append("SATOKEN_LOGIN:");
+        sb.append("LOGIN:");
+        if (userInfo.getTenantId() != null) {
+            sb.append(userInfo.getTenantId()).append(":");
+        }
+        sb.append(userInfo.getLoginName()).append(":").append(userInfo.getUserId());
+        return sb.toString();
+    }
+
+    public static String makeLoginIdByWeb(LoginUserInfo userInfo) {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("WEB_LOGIN:");
         if (userInfo.getTenantId() != null) {
             sb.append(userInfo.getTenantId()).append(":");
         }