Эх сурвалжийг харах

Merge branch 'dev-yhq' into develop

tsurumure 1 сар өмнө
parent
commit
747dc1cd35
27 өөрчлөгдсөн 480 нэмэгдсэн , 419 устгасан
  1. 34 0
      .drone.version.sh
  2. 0 42
      src/main/java/com/backendsys/modules/app/controller/AppAuthController.java
  3. 25 0
      src/main/java/com/backendsys/modules/app/crt/controller/AppCrtDramaProjectController.java
  4. 0 20
      src/main/java/com/backendsys/modules/app/entity/AppAuth.java
  5. 0 11
      src/main/java/com/backendsys/modules/app/service/AppAuthService.java
  6. 0 4
      src/main/java/com/backendsys/modules/app/service/AppUserService.java
  7. 0 158
      src/main/java/com/backendsys/modules/app/service/impl/AppAuthServiceImpl.java
  8. 48 0
      src/main/java/com/backendsys/modules/app/user/controller/AppAuthController.java
  9. 2 2
      src/main/java/com/backendsys/modules/app/user/controller/AppUserController.java
  10. 2 2
      src/main/java/com/backendsys/modules/app/user/dao/AppUserDao.java
  11. 22 0
      src/main/java/com/backendsys/modules/app/user/entity/AppAuth.java
  12. 1 2
      src/main/java/com/backendsys/modules/app/user/entity/AppUser.java
  13. 26 0
      src/main/java/com/backendsys/modules/app/user/service/AppAuthService.java
  14. 4 0
      src/main/java/com/backendsys/modules/app/user/service/AppUserService.java
  15. 297 0
      src/main/java/com/backendsys/modules/app/user/service/impl/AppAuthServiceImpl.java
  16. 2 2
      src/main/java/com/backendsys/modules/app/user/service/impl/AppUserServiceImpl.java
  17. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Dec.java
  18. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Enc.java
  19. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Sign.java
  20. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Verify.java
  21. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/WechatUtil.java
  22. 5 0
      src/main/java/com/backendsys/modules/common/config/security/utils/HttpRequestUtil.java
  23. 0 14
      src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/entity/WechatAuth.java
  24. 0 11
      src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/service/WechatAuthService.java
  25. 0 143
      src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/service/impl/WechatAuthServiceImpl.java
  26. 2 0
      src/main/java/com/backendsys/modules/system/controller/SysAuthController.java
  27. 5 3
      src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

+ 34 - 0
.drone.version.sh

@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+# .drone.version.sh
+
+VERSION_FILE="version.txt"
+
+# 若文件不存在则初始化
+if [[ ! -f "$VERSION_FILE" ]]; then
+    echo "0.0.1" > "$VERSION_FILE"
+    echo "Initialized version: 0.0.1"
+    exit 0
+fi
+
+# 读取当前版本并拆分为数组
+IFS='.' read -r -a parts < "$VERSION_FILE"
+
+major=${parts[0]}
+minor=${parts[1]}
+patch=${parts[2]}
+
+# 递增 patch,处理进位 (100进位)
+patch=$((patch + 1))
+if [[ $patch -ge 100 ]]; then
+    patch=0
+    minor=$((minor + 1))
+fi
+if [[ $minor -ge 100 ]]; then
+    minor=0
+    major=$((major + 1))
+fi
+
+new_version="${major}.${minor}.${patch}"
+echo "$new_version" > "$VERSION_FILE"
+echo "Updated version: $new_version"
+

+ 0 - 42
src/main/java/com/backendsys/modules/app/controller/AppAuthController.java

@@ -1,42 +0,0 @@
-package com.backendsys.modules.app.controller;
-
-import com.backendsys.modules.app.entity.AppAuth;
-import com.backendsys.modules.app.service.AppAuthService;
-import com.backendsys.modules.common.config.security.annotations.Anonymous;
-import com.backendsys.modules.common.utils.Result;
-import com.backendsys.modules.sdk.wechat.miniprogram.entity.WechatAuth;
-import com.backendsys.modules.sdk.wechat.miniprogram.service.WechatAuthService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
-
-@Validated
-@RestController
-@Tag(name = "APP用户登录与注册")
-public class AppAuthController {
-
-    @Autowired
-    private AppAuthService appAuthService;
-
-    @Autowired
-    private WechatAuthService wechatAuthService;
-
-    @Anonymous
-    @Operation(summary = "APP登录 (用户名登录)")
-    @PostMapping("/api/app/auth/login")
-    public Result login(@Validated(AppAuth.Login.class) @RequestBody AppAuth appAuth) {
-        return Result.success().put("data", appAuthService.login(appAuth));
-    }
-
-    @Anonymous
-    @Operation(summary = "微信小程序用户登录")
-    @PostMapping("/api/app/auth/loginWithWechatMiniprogram")
-    public Result loginWithWechatMiniprogram(@Validated(WechatAuth.Login.class) @RequestBody WechatAuth wechatAuth) {
-        return Result.success().put("data", wechatAuthService.loginWithWechatMiniprogram(wechatAuth));
-    }
-
-}

+ 25 - 0
src/main/java/com/backendsys/modules/app/crt/controller/AppCrtDramaProjectController.java

@@ -0,0 +1,25 @@
+package com.backendsys.modules.app.crt.controller;
+
+import com.backendsys.modules.common.aspect.AppUserLogin;
+import com.backendsys.modules.common.utils.Result;
+import com.backendsys.modules.crt.entity.CrtDramaProject;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Validated
+@RestController
+@Tag(name = "APP-智能应用中心-短剧创作")
+public class AppCrtDramaProjectController {
+
+    @AppUserLogin
+    @Operation(summary = "项目列表")
+    @GetMapping("/api/app/crt/drama/getDramaProjectAllList")
+    public Result getDramaProjectAllList() {
+        return Result.success();
+    }
+
+}

+ 0 - 20
src/main/java/com/backendsys/modules/app/entity/AppAuth.java

@@ -1,20 +0,0 @@
-package com.backendsys.modules.app.entity;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-
-@Data
-public class AppAuth {
-
-    public static interface Login{}
-
-    @NotEmpty(message = "手机号码不能为空", groups = { Login.class })
-    private String phone;
-    @NotEmpty(message = "密码不能为空", groups = { Login.class })
-    private String password;
-    @TableField(exist = false)
-    @NotEmpty(message = "验证码不能为空", groups = { Login.class })
-    private String captcha;
-
-}

+ 0 - 11
src/main/java/com/backendsys/modules/app/service/AppAuthService.java

@@ -1,11 +0,0 @@
-package com.backendsys.modules.app.service;
-
-import com.backendsys.modules.app.entity.AppAuth;
-import com.backendsys.modules.app.entity.AppUser;
-
-public interface AppAuthService {
-
-    // APP登录 (用户名登录)
-    AppUser login(AppAuth appAuth);
-
-}

+ 0 - 4
src/main/java/com/backendsys/modules/app/service/AppUserService.java

@@ -1,4 +0,0 @@
-package com.backendsys.modules.app.service;
-
-public interface AppUserService {
-}

+ 0 - 158
src/main/java/com/backendsys/modules/app/service/impl/AppAuthServiceImpl.java

@@ -1,158 +0,0 @@
-package com.backendsys.modules.app.service.impl;
-
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.date.DateUnit;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.json.JSONUtil;
-import com.backendsys.exception.CustException;
-import com.backendsys.modules.app.dao.AppUserDao;
-import com.backendsys.modules.app.entity.AppAuth;
-import com.backendsys.modules.app.entity.AppUser;
-import com.backendsys.modules.app.service.AppAuthService;
-import com.backendsys.modules.common.config.redis.utils.RedisUtil;
-import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
-import com.backendsys.modules.common.config.security.utils.*;
-import com.backendsys.modules.system.entity.TokenCatch;
-import com.backendsys.modules.system.service.SysCommonService;
-import com.backendsys.utils.response.ResultEnum;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.stereotype.Service;
-
-import java.util.Date;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-@Service
-public class AppAuthServiceImpl implements AppAuthService {
-
-    @Autowired
-    private JwtUtil jwtUtil;
-    @Autowired
-    private TokenUtil tokenUtil;
-    @Autowired
-    private RedisUtil redisUtil;
-    @Autowired
-    private CaptchaUtil captchaUtil;
-    @Autowired
-    private LockStatusUtil lockStatusUtil;
-    @Autowired
-    private HttpRequestUtil httpRequestUtil;
-
-    @Autowired
-    private AppUserDao appUserDao;
-    @Autowired
-    private SysCommonService sysCommonService;
-
-    @Value("${tencent.sms.debug}")
-    private String SMS_DEBUG;
-    @Value("${CAPTCHA_DURATION}")
-    private Integer CAPTCHA_DURATION;
-    @Value("${REDIS_LOGIN_TOKEN_PREFIX}")
-    private String REDIS_LOGIN_TOKEN_PREFIX;
-    @Value("${spring.application.name}")
-    private String APPLICATION_NAME;
-    private String redisKeyOfLogin = APPLICATION_NAME + "-app-sms-login";
-    private String redisKeyOfRegister = APPLICATION_NAME + "-app-sms-register";
-    private String redisKeyOfLoginFail = APPLICATION_NAME + "-app-login-error";
-    private String redisKeyOfRegisterFail = APPLICATION_NAME + "-app-register-error";
-
-
-    // [方法] 登录失败 (errMsg: 错误提示文本, phone: 手机号码, intercept: 是否拦截)
-    private void loginFail(String errMsg, String phone, Boolean isIntercept) {
-        // 删除图形验证码
-        redisUtil.delete(httpRequestUtil.getKaptchaKey());
-        // 添加登录错误的冻结标记
-        if (isIntercept) lockStatusUtil.setLockStatus(redisKeyOfLoginFail, phone);
-        throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode());
-    }
-
-    // [方法] 登录成功
-    private AppUser loginSuccess(AppUser appUser) {
-
-        // 删除图形验证码缓存
-        redisUtil.delete(httpRequestUtil.getKaptchaKey());
-
-        // 删除旧的登录缓存
-        tokenUtil.deleteRedisLoginToken(appUser.getLast_login_uuid());
-
-        // 判断用户是否启用
-        Integer status = appUser.getStatus();
-        if (status != null && status.equals(-1)) throw new CustException("该用户已被禁用,请与客服联系");
-
-        // 判断用户是否已删除
-        Integer del_flag = appUser.getDel_flag();
-        if (del_flag != null && del_flag.equals(1)) throw new CustException("当前用户不可用,请与客服联系");
-
-        // 设置 最后一次的登录信息 (uuid, ip, 登录时间)
-        String uuid = Convert.toStr(UUID.randomUUID());
-        appUser.setUser_id(appUser.getId());
-        appUser.setLast_login_uuid(uuid);
-        appUser.setLast_login_ip(httpRequestUtil.getIpAddr());
-        appUser.setLast_login_time(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
-        appUserDao.updateById(appUser);
-
-
-        // [系统配置] 微信用户默认登录过期时间(小时)
-        Integer APP_USER_LOGIN_DURATION_DEFAULT = Convert.toInt(sysCommonService.getCommonByTag("APP_USER_LOGIN_DURATION_DEFAULT"));
-        // 将小时转换为毫秒
-        Long DEFAULT_MILLISECONDS = APP_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
-        Integer token_duration_hours = Convert.toInt(DEFAULT_MILLISECONDS / 3600000L);
-
-        Date token_expiration = new Date((new Date()).getTime() + DEFAULT_MILLISECONDS);
-        appUser.setToken_expiration(DateUtil.format(token_expiration, "yyyy-MM-dd HH:mm:ss"));
-        appUser.setRole("APP_USER");
-
-        // 生成 Token
-        SecurityAppUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(appUser), SecurityAppUserInfo.class);
-        String token = jwtUtil.createAppJwtToken(securityUserInfo);
-        String token_redis_key = REDIS_LOGIN_TOKEN_PREFIX + uuid;
-        appUser.setToken(token);
-
-        // [Redis] 将 Token 存入缓存
-        TokenCatch tokenCatch = new TokenCatch(token, null);
-        redisUtil.setCacheObject(token_redis_key, JSONUtil.toJsonStr(tokenCatch), token_duration_hours, TimeUnit.HOURS);
-
-        return appUser;
-    }
-
-    /**
-     * APP登录 (用户名登录)
-     */
-    @Override
-    public AppUser login(AppAuth appAuth) {
-
-        String phone = appAuth.getPhone();
-        String password = appAuth.getPassword();
-        String captcha = appAuth.getCaptcha();
-
-        // 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示)
-        lockStatusUtil.checkLockStatus(redisKeyOfLoginFail, phone);
-
-        // 判断图形验证码是否正确
-        if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey())) {
-            loginFail("验证码错误", phone, false);
-            return null;
-        }
-
-        // [Method] 判断 用户 是否存在 && 密码是否正确
-        AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, phone));
-        if (appUser == null) {
-            // [登录失败] 用户不存在
-            loginFail("手机号码或密码错误", phone, true);
-            return null;
-        } else {
-            // [登录失败] 密码不正确
-            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
-            if (!encoder.matches(password, appUser.getPassword())) {
-                loginFail("手机号码或密码错误", phone, true);
-            }
-            // [登录成功]
-            return loginSuccess(appUser);
-        }
-
-    }
-
-}

+ 48 - 0
src/main/java/com/backendsys/modules/app/user/controller/AppAuthController.java

@@ -0,0 +1,48 @@
+package com.backendsys.modules.app.user.controller;
+
+import com.backendsys.modules.app.user.entity.AppAuth;
+import com.backendsys.modules.app.user.service.AppAuthService;
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
+import com.backendsys.modules.common.utils.Result;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+
+@Validated
+@RestController
+@Tag(name = "APP用户登录与注册")
+public class AppAuthController {
+
+    @Autowired
+    private AppAuthService appAuthService;
+
+    @Anonymous
+    @Operation(summary = "获取图形验证码")
+    @GetMapping("/api/app/auth/getCaptcha")
+    public void getCaptcha(HttpServletResponse response) throws IOException {
+        appAuthService.renderCaptcha(response);
+    }
+
+    @Anonymous
+    @Operation(summary = "前台用户登录 (手机账号)")
+    @PostMapping("/api/app/auth/loginWithPhone")
+    public Result login(@Validated(AppAuth.LoginWithPhone.class) @RequestBody AppAuth appAuth) {
+        return Result.success().put("data", appAuthService.loginWithPhone(appAuth));
+    }
+
+    @Anonymous
+    @Operation(summary = "前台用户登录 (微信小程序用户)")
+    @PostMapping("/api/app/auth/loginWithWechatMiniprogram")
+    public Result loginWithWechatMiniprogram(@Validated(AppAuth.LoginWithWechat.class) @RequestBody AppAuth appAuth) {
+        return Result.success().put("data", appAuthService.loginWithWechatMiniprogram(appAuth));
+    }
+
+}

+ 2 - 2
src/main/java/com/backendsys/modules/app/controller/AppUserController.java → src/main/java/com/backendsys/modules/app/user/controller/AppUserController.java

@@ -1,6 +1,6 @@
-package com.backendsys.modules.app.controller;
+package com.backendsys.modules.app.user.controller;
 
-import com.backendsys.modules.app.service.AppUserService;
+import com.backendsys.modules.app.user.service.AppUserService;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;

+ 2 - 2
src/main/java/com/backendsys/modules/app/dao/AppUserDao.java → src/main/java/com/backendsys/modules/app/user/dao/AppUserDao.java

@@ -1,6 +1,6 @@
-package com.backendsys.modules.app.dao;
+package com.backendsys.modules.app.user.dao;
 
-import com.backendsys.modules.app.entity.AppUser;
+import com.backendsys.modules.app.user.entity.AppUser;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 

+ 22 - 0
src/main/java/com/backendsys/modules/app/user/entity/AppAuth.java

@@ -0,0 +1,22 @@
+package com.backendsys.modules.app.user.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+@Data
+public class AppAuth {
+
+    public static interface LoginWithPhone{}
+    public static interface LoginWithWechat{}
+
+    @NotEmpty(message = "手机号码不能为空", groups = { LoginWithPhone.class })
+    private String phone;
+    @NotEmpty(message = "密码不能为空", groups = { LoginWithPhone.class })
+    private String password;
+    private String captcha;
+
+    @NotEmpty(message = "code 不能为空", groups = { LoginWithWechat.class })
+    private String code;
+
+}

+ 1 - 2
src/main/java/com/backendsys/modules/app/entity/AppUser.java → src/main/java/com/backendsys/modules/app/user/entity/AppUser.java

@@ -1,10 +1,9 @@
-package com.backendsys.modules.app.entity;
+package com.backendsys.modules.app.user.entity;
 
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
 
 @Data

+ 26 - 0
src/main/java/com/backendsys/modules/app/user/service/AppAuthService.java

@@ -0,0 +1,26 @@
+package com.backendsys.modules.app.user.service;
+
+import com.backendsys.modules.app.user.entity.AppAuth;
+import com.backendsys.modules.app.user.entity.AppUser;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+public interface AppAuthService {
+
+    // 获取图形验证码
+    void renderCaptcha(HttpServletResponse response) throws IOException;
+
+
+    // 前台用户登录 (手机账号)
+    AppUser loginWithPhone(AppAuth appAuth);
+    // 前台用户登录 (微信小程序用户)
+    AppUser loginWithWechatMiniprogram(AppAuth appAuth);
+
+
+    // [事件] 登录成功
+    AppUser loginSuccess(AppUser appUser);
+    // [事件] 登录失败
+    void loginFail(String errMsg, String phone, Boolean isIntercept);
+
+}

+ 4 - 0
src/main/java/com/backendsys/modules/app/user/service/AppUserService.java

@@ -0,0 +1,4 @@
+package com.backendsys.modules.app.user.service;
+
+public interface AppUserService {
+}

+ 297 - 0
src/main/java/com/backendsys/modules/app/user/service/impl/AppAuthServiceImpl.java

@@ -0,0 +1,297 @@
+package com.backendsys.modules.app.user.service.impl;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.app.user.dao.AppUserDao;
+import com.backendsys.modules.app.user.entity.AppAuth;
+import com.backendsys.modules.app.user.entity.AppUser;
+import com.backendsys.modules.app.user.service.AppAuthService;
+import com.backendsys.modules.app.user.utils.AuthWechat.WechatUtil;
+import com.backendsys.modules.common.config.redis.utils.RedisUtil;
+import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
+import com.backendsys.modules.common.config.security.utils.*;
+import com.backendsys.modules.system.entity.TokenCatch;
+import com.backendsys.modules.system.service.SysCommonService;
+import com.backendsys.utils.response.ResultEnum;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.code.kaptcha.Producer;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class AppAuthServiceImpl implements AppAuthService {
+
+    @Autowired
+    private JwtUtil jwtUtil;
+    @Autowired
+    private TokenUtil tokenUtil;
+    @Autowired
+    private RedisUtil redisUtil;
+    @Autowired
+    private WechatUtil wechatUtil;
+    @Autowired
+    private CaptchaUtil captchaUtil;
+    @Autowired
+    private LockStatusUtil lockStatusUtil;
+    @Autowired
+    private HttpRequestUtil httpRequestUtil;
+
+    @Autowired
+    private AppUserDao appUserDao;
+    @Autowired
+    private SysCommonService sysCommonService;
+
+    @Value("${spring.application.name}")
+    private String APPLICATION_NAME;
+    @Value("${CAPTCHA_DURATION}")
+    private Integer CAPTCHA_DURATION;
+    @Value("${REDIS_LOGIN_TOKEN_PREFIX}")
+    private String REDIS_LOGIN_TOKEN_PREFIX;
+
+    @Autowired
+    private Producer captchaProducer;
+
+    @Override
+    public void renderCaptcha(HttpServletResponse response) throws IOException {
+        try {
+
+            byte[] captchaChallengeAsJpeg;
+            ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
+
+            String createText = captchaProducer.createText();
+            // 获得当前 (UA + IP) 生成的 Key
+            String captchaRedisKey = httpRequestUtil.getKaptchaKey("APP");
+            // 保存 验证码字符串 到 redis 中
+            redisUtil.setCacheObject(captchaRedisKey, createText, this.CAPTCHA_DURATION, TimeUnit.MILLISECONDS);
+            // 返回 BufferedImage 对象并转为 byte 写入到 byte 数组中
+            BufferedImage challenge = captchaProducer.createImage(createText);
+            ImageIO.write(challenge, "jpg", jpegOutputStream);
+
+            // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
+            captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
+            response.setHeader("Cache-Control", "no-store");
+            response.setHeader("Pragma", "no-cache");
+            response.setDateHeader("Expires", 0);
+            response.setContentType("image/jpeg");
+
+            ServletOutputStream responseOutputStream = response.getOutputStream();
+            responseOutputStream.write(captchaChallengeAsJpeg);
+            responseOutputStream.flush();
+            responseOutputStream.close();
+
+        } catch (IOException e) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+        }
+    }
+
+
+    // [方法] 登录失败 (errMsg: 错误提示文本, phone: 手机号码, intercept: 是否拦截)
+    public void loginFail(String errMsg, String phone, Boolean isIntercept) {
+        // 验证码是否必填
+        Boolean currentCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-app-login-required-captcha-" + phone, 3);
+        System.out.println("(loginFailByUsername) currentCaptchaRequired = " + currentCaptchaRequired);
+
+        // 删除图形验证码
+        redisUtil.delete(httpRequestUtil.getKaptchaKey("APP"));
+        // 添加登录错误的冻结标记
+        if (isIntercept) lockStatusUtil.setLockStatus(APPLICATION_NAME + "-app-login-error", phone);
+
+        if (currentCaptchaRequired) {
+            throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode(), Map.of("is_captcha_required", true));
+        } else {
+            throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode());
+        }
+    }
+
+    // [方法] 登录成功
+    public AppUser loginSuccess(AppUser appUser) {
+
+        // 删除图形验证码缓存 (如果有的话)
+        redisUtil.delete(httpRequestUtil.getKaptchaKey("APP"));
+
+        // 删除旧的登录缓存
+        tokenUtil.deleteRedisLoginToken(appUser.getLast_login_uuid());
+
+        // 判断用户是否启用
+        Integer status = appUser.getStatus();
+        if (status != null && status.equals(-1)) throw new CustException("该用户已被禁用,请与客服联系");
+
+        // 判断用户是否已删除
+        Integer del_flag = appUser.getDel_flag();
+        if (del_flag != null && del_flag.equals(1)) throw new CustException("当前用户不可用,请与客服联系");
+
+        // 设置 最后一次的登录信息 (uuid, ip, 登录时间)
+        String uuid = Convert.toStr(UUID.randomUUID());
+        appUser.setUser_id(appUser.getId());
+        appUser.setLast_login_uuid(uuid);
+        appUser.setLast_login_ip(httpRequestUtil.getIpAddr());
+
+        // 最后登录时间 (本地时间)
+        String localTimeStr = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        appUser.setLast_login_time(localTimeStr);
+
+//        // UTC 时间
+//        String utcTimeStr = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC"))
+//                .toString("yyyy-MM-dd HH:mm:ss");
+//        appUser.setLast_login_time(utcTimeStr);
+
+        // [db] 更新前台用户信息
+        appUserDao.updateById(appUser);
+
+
+        // [系统配置] 微信用户默认登录过期时间(小时)
+        Integer APP_USER_LOGIN_DURATION_DEFAULT = Convert.toInt(sysCommonService.getCommonByTag("APP_USER_LOGIN_DURATION_DEFAULT"));
+        // 将小时转换为毫秒
+        Long DEFAULT_MILLISECONDS = APP_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
+        Integer token_duration_hours = Convert.toInt(DEFAULT_MILLISECONDS / 3600000L);
+
+        // Token 记录的登录时间,是本地时间
+        Date token_expiration = new Date((new Date()).getTime() + DEFAULT_MILLISECONDS);
+        appUser.setToken_expiration(DateUtil.format(token_expiration, "yyyy-MM-dd HH:mm:ss"));
+        appUser.setRole("APP_USER");
+
+        // 生成 Token
+        SecurityAppUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(appUser), SecurityAppUserInfo.class);
+        String token = jwtUtil.createAppJwtToken(securityUserInfo);
+        String token_redis_key = REDIS_LOGIN_TOKEN_PREFIX + uuid;
+        appUser.setToken(token);
+
+        // [Redis] 将 Token 存入缓存
+        TokenCatch tokenCatch = new TokenCatch(token, null);
+        redisUtil.setCacheObject(token_redis_key, JSONUtil.toJsonStr(tokenCatch), token_duration_hours, TimeUnit.HOURS);
+
+        return appUser;
+    }
+
+    private void setLoginRequired(String key) {
+        Object captchaValue = redisUtil.getCacheObject(APPLICATION_NAME + "-app-login-required-captcha-" + key);
+        Integer currentErrCount = (captchaValue == null) ? 1 : (Convert.toInt(captchaValue) + 1);
+        redisUtil.setCacheObject(APPLICATION_NAME + "-app-login-required-captcha-" + key, currentErrCount, 1, TimeUnit.MINUTES);
+        System.out.println("currentErrCount: " + currentErrCount);
+    }
+    private void cleanLoginRequired(String key) {
+        redisUtil.delete(APPLICATION_NAME + "-app-login-required-captcha-" + key);
+    }
+
+
+
+    /**
+     * 前台用户登录 (手机账号)
+     */
+    @Override
+    public AppUser loginWithPhone(AppAuth appAuth) {
+
+        String phone = appAuth.getPhone();
+        String password = appAuth.getPassword();
+        String captcha = appAuth.getCaptcha();
+
+        // 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示)
+        lockStatusUtil.checkLockStatus(APPLICATION_NAME + "-app-login-error", phone);
+
+        // -- 判断是否需要输入验证码 ----------------------------------------------------
+        // - 当输错 3 次密码时,需要输入验证码
+        // - 当输错后 1 分钟后重置
+        Boolean isCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-app-login-required-captcha-" + phone, 3);
+        if (isCaptchaRequired) {
+            Boolean isCaptchaEmpty = StrUtil.isEmpty(captcha);
+            Boolean isCpatchaValid = (captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey("APP")));
+            if (isCaptchaEmpty) { loginFail("验证码不能为空", phone, false); return null; }
+            if (!isCpatchaValid) { loginFail("验证码错误", phone, false); return null; }
+        }
+        // --------------------------------------------------------------------------
+
+        // [Method] 判断 用户 是否存在 && 密码是否正确
+        AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, phone));
+        if (appUser == null) {
+            // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+            setLoginRequired(phone);
+            // [登录失败] 用户不存在
+            loginFail("手机号码或密码错误", phone, true);
+            return null;
+        } else {
+            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+            if (!encoder.matches(password, appUser.getPassword())) {
+                // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+                setLoginRequired(phone);
+                // [登录失败] 密码不正确
+                loginFail("手机号码或密码错误", phone, true);
+            }
+            // [登录成功]
+            cleanLoginRequired(phone);
+            return loginSuccess(appUser);
+        }
+
+    }
+
+    /**
+     * 前台用户登录 (微信小程序用户)
+     */
+    @Override
+    public AppUser loginWithWechatMiniprogram(AppAuth appAuth) {
+
+        String code = appAuth.getCode();
+        JSONObject response = wechatUtil.getCode2Session(code);
+        // [失败] 返回值:{"errcode":40164,"errmsg":"invalid ip 116.31.165.86 ipv6 ::ffff:116.31.165.86, not in whitelist, rid: 67d15951-5b3f2778-79d6f047"}
+        // [成功] 返回值:{"session_key":"uQAcry2PC1Lx/Krp+6rr0g==","openid":"oSB9r7T7kN1bC7PabJ7RmTUiaJmo"}
+
+        // - errcode: 错误码,请求失败时返回
+        if (!response.containsKey("errcode")) {
+
+            String openid = response.getStr("openid");
+            String session_key = response.getStr("session_key");
+
+            String uuid = Convert.toStr(UUID.randomUUID());
+            String last_login_ip = httpRequestUtil.getIpAddr();
+            String last_login_time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+            // [Get] 判断用户是否存在 (openid)
+            AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getWechat_open_id, openid));
+            if (appUser == null) {
+                // [Insert] 不存在,则创建
+                appUser = new AppUser();
+                appUser.setWechat_open_id(openid);
+                appUser.setNickname("微信用户" + RandomUtil.randomStringUpper(4));
+                //
+                appUser.setLast_login_uuid(uuid);
+                appUser.setLast_login_ip(last_login_ip);
+                appUser.setLast_login_time(last_login_time);
+                appUserDao.insert(appUser);
+            } else {
+                // [Update] 更新时间
+                appUser.setLast_login_uuid(uuid);
+                appUser.setLast_login_ip(last_login_ip);
+                appUser.setLast_login_time(last_login_time);
+                appUserDao.updateById(appUser);
+            }
+            appUser.setUser_id(appUser.getId());
+            appUser.setSession_key(session_key);
+
+            // 登录成功
+            return loginSuccess(appUser);
+        } else {
+            throw new CustException(response.getStr("errmsg"));
+        }
+
+    }
+
+}

+ 2 - 2
src/main/java/com/backendsys/modules/app/service/impl/AppUserServiceImpl.java → src/main/java/com/backendsys/modules/app/user/service/impl/AppUserServiceImpl.java

@@ -1,6 +1,6 @@
-package com.backendsys.modules.app.service.impl;
+package com.backendsys.modules.app.user.service.impl;
 
-import com.backendsys.modules.app.service.AppUserService;
+import com.backendsys.modules.app.user.service.AppUserService;
 import org.springframework.stereotype.Service;
 
 @Service

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/AES_Dec.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Dec.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/AES_Enc.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Enc.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/RSA_Sign.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Sign.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import com.google.gson.JsonObject;
 import java.nio.charset.StandardCharsets;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/RSA_Verify.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Verify.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import com.google.gson.JsonObject;
 import java.io.ByteArrayInputStream;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/WechatUtil.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/WechatUtil.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;

+ 5 - 0
src/main/java/com/backendsys/modules/common/config/security/utils/HttpRequestUtil.java

@@ -141,6 +141,11 @@ public class HttpRequestUtil {
         String userAgent = getUa();
         return "captcha:ua:ip:" + CommonUtil.MD5(ip + userAgent);
     }
+    public String getKaptchaKey(String key) {
+        String ip = getIpAddr();
+        String userAgent = getUa();
+        return "captcha:ua:ip:" + CommonUtil.MD5(ip + userAgent + key);
+    }
 
     public String getUa() {
         HttpServletRequest request = getRequest();

+ 0 - 14
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/entity/WechatAuth.java

@@ -1,14 +0,0 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.entity;
-
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-
-@Data
-public class WechatAuth {
-
-    public static interface Login{}
-
-    @NotEmpty(message = "code 不能为空", groups = { Login.class })
-    private String code;
-
-}

+ 0 - 11
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/service/WechatAuthService.java

@@ -1,11 +0,0 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.service;
-
-import com.backendsys.modules.app.entity.AppUser;
-import com.backendsys.modules.sdk.wechat.miniprogram.entity.WechatAuth;
-
-public interface WechatAuthService {
-
-    // 微信小程序用户登录
-    AppUser loginWithWechatMiniprogram(WechatAuth wechatAuth);
-
-}

+ 0 - 143
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/service/impl/WechatAuthServiceImpl.java

@@ -1,143 +0,0 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.service.impl;
-
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.date.DateUnit;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.RandomUtil;
-import cn.hutool.json.JSONObject;
-import cn.hutool.json.JSONUtil;
-import com.backendsys.exception.CustException;
-import com.backendsys.modules.app.dao.AppUserDao;
-import com.backendsys.modules.app.entity.AppUser;
-import com.backendsys.modules.common.config.redis.utils.RedisUtil;
-import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
-import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
-import com.backendsys.modules.common.config.security.utils.JwtUtil;
-import com.backendsys.modules.common.config.security.utils.TokenUtil;
-import com.backendsys.modules.sdk.wechat.miniprogram.entity.WechatAuth;
-import com.backendsys.modules.sdk.wechat.miniprogram.service.WechatAuthService;
-import com.backendsys.modules.sdk.wechat.miniprogram.utils.WechatUtil;
-import com.backendsys.modules.system.entity.TokenCatch;
-import com.backendsys.modules.system.service.SysCommonService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.util.Date;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-@Service
-public class WechatAuthServiceImpl implements WechatAuthService {
-
-    @Autowired
-    private JwtUtil jwtUtil;
-    @Autowired
-    private TokenUtil tokenUtil;
-    @Autowired
-    private RedisUtil redisUtil;
-    @Autowired
-    private WechatUtil wechatUtil;
-    @Autowired
-    private HttpRequestUtil httpRequestUtil;
-
-    @Value("${REDIS_LOGIN_TOKEN_PREFIX}")
-    private String REDIS_LOGIN_TOKEN_PREFIX;
-
-    @Autowired
-    private SysCommonService sysCommonService;
-    @Autowired
-    private AppUserDao appUserDao;
-
-
-    // [方法] 登录成功
-    private AppUser loginSuccess(AppUser appUser) {
-
-        // 删除旧的登录缓存
-        tokenUtil.deleteRedisLoginToken(appUser.getLast_login_uuid());
-
-        // 判断用户是否启用
-        Integer status = appUser.getStatus();
-        if (status != null && status.equals(-1)) throw new CustException("该用户已被禁用,请与客服联系");
-
-        // 判断用户是否已删除
-        Integer del_flag = appUser.getDel_flag();
-        if (del_flag != null && del_flag.equals(1)) throw new CustException("当前用户不可用,请与客服联系");
-
-        // [系统配置] 微信用户默认登录过期时间(小时)
-        Integer APP_USER_LOGIN_DURATION_DEFAULT = Convert.toInt(sysCommonService.getCommonByTag("APP_USER_LOGIN_DURATION_DEFAULT"));
-        // 将小时转换为毫秒
-        Long DEFAULT_MILLISECONDS = APP_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
-        Integer token_duration_hours = Convert.toInt(DEFAULT_MILLISECONDS / 3600000L);
-
-        Date token_expiration = new Date((new Date()).getTime() + DEFAULT_MILLISECONDS);
-        appUser.setToken_expiration(DateUtil.format(token_expiration, "yyyy-MM-dd HH:mm:ss"));
-        appUser.setRole("APP_USER");
-
-        // 生成 Token
-        SecurityAppUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(appUser), SecurityAppUserInfo.class);
-
-        String token = jwtUtil.createAppJwtToken(securityUserInfo);
-        String token_redis_key = REDIS_LOGIN_TOKEN_PREFIX + appUser.getLast_login_uuid();
-        appUser.setToken(token);
-
-        // [Redis] 将 Token 存入缓存
-        TokenCatch tokenCatch = new TokenCatch(token, null);
-        redisUtil.setCacheObject(token_redis_key, JSONUtil.toJsonStr(tokenCatch), token_duration_hours, TimeUnit.HOURS);
-
-        return appUser;
-    }
-
-    /**
-     * 微信小程序用户登录
-     */
-    @Override
-    public AppUser loginWithWechatMiniprogram(WechatAuth wechatAuth) {
-
-        String code = wechatAuth.getCode();
-        JSONObject response = wechatUtil.getCode2Session(code);
-        // [失败] 返回值:{"errcode":40164,"errmsg":"invalid ip 116.31.165.86 ipv6 ::ffff:116.31.165.86, not in whitelist, rid: 67d15951-5b3f2778-79d6f047"}
-        // [成功] 返回值:{"session_key":"uQAcry2PC1Lx/Krp+6rr0g==","openid":"oSB9r7T7kN1bC7PabJ7RmTUiaJmo"}
-
-        // - errcode: 错误码,请求失败时返回
-        if (!response.containsKey("errcode")) {
-
-            String openid = response.getStr("openid");
-            String session_key = response.getStr("session_key");
-
-            String uuid = Convert.toStr(UUID.randomUUID());
-            String last_login_ip = httpRequestUtil.getIpAddr();
-            String last_login_time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
-
-            // [Get] 判断用户是否存在 (openid)
-            AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getWechat_open_id, openid));
-            if (appUser == null) {
-                // [Insert] 不存在,则创建
-                appUser = new AppUser();
-                appUser.setWechat_open_id(openid);
-                appUser.setNickname("微信用户" + RandomUtil.randomStringUpper(4));
-                //
-                appUser.setLast_login_uuid(uuid);
-                appUser.setLast_login_ip(last_login_ip);
-                appUser.setLast_login_time(last_login_time);
-                appUserDao.insert(appUser);
-            } else {
-                // [Update] 更新时间
-                appUser.setLast_login_uuid(uuid);
-                appUser.setLast_login_ip(last_login_ip);
-                appUser.setLast_login_time(last_login_time);
-                appUserDao.updateById(appUser);
-            }
-            appUser.setUser_id(appUser.getId());
-            appUser.setSession_key(session_key);
-
-            // 登录成功
-            return loginSuccess(appUser);
-        } else {
-            throw new CustException(response.getStr("errmsg"));
-        }
-
-    }
-
-}

+ 2 - 0
src/main/java/com/backendsys/modules/system/controller/SysAuthController.java

@@ -36,11 +36,13 @@ public class SysAuthController {
     public void getCaptcha(HttpServletResponse response) throws IOException {
         sysAuthService.renderCaptcha(response);
     }
+
     @Operation(summary = "判断是否需验证码登录状态")
     @GetMapping("/api/system/auth/checkCaptchaRequired")
     public Result checkCaptchaRequired(String username) {
         return Result.success().put("data", sysAuthService.checkCaptchaRequired(username));
     }
+
     @Operation(summary = "系统用户登录 (用户名)")
     @PostMapping(value = "/api/system/auth/login")
     public Result systemLogin(@Validated(SysAuth.Login.class) @RequestBody SysAuth sysAuth) {

+ 5 - 3
src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

@@ -237,15 +237,17 @@ public class SysAuthServiceImpl implements SysAuthService {
         // [Method] 判断 用户 是否存在 && 密码是否正确
         SysUser sysUser = sysUserDao.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
         if (sysUser == null) {
+            // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+            setLoginRequired(username);
             // [登录失败] 用户不存在
-            setLoginRequired(username); // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
             loginFail("用户名或密码错误", username, true);
             return null;
         } else {
-            // [登录失败] 密码不正确
             BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
             if (!encoder.matches(password, sysUser.getPassword())) {
-                setLoginRequired(username); // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+                // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+                setLoginRequired(username);
+                // [登录失败] 密码不正确
                 loginFail("用户名或密码错误", username, true);
             }
             // [登录成功]