ソースを参照

1.account-service 引入消息队列, 用于将验证码等消息发送到 message-service, 不再使用 message-service 的 rpc 接口
2.account-service 使用一个 face.jpg 文件作为帐号创建时的默认头像, 不再调用 file-service 动态生成头像
3.帐号更新头像时通过 file-service 调用 account-service 的 rpc 接口
4.account-service 不再使用 message 和 file 提供的 rpc 接口, 仅作为一个 rpc provider
5.message-service 发送验证码时引入 mq 的消息确认机制

reghao 1 年間 前
コミット
1e1290d999
25 ファイル変更304 行追加229 行削除
  1. 0 18
      account/account-api/src/main/java/cn/reghao/tnb/account/api/dto/UserAvatarUpdate.java
  2. 1 0
      account/account-api/src/main/java/cn/reghao/tnb/account/api/iface/AccountQuery.java
  3. 9 5
      account/account-service/pom.xml
  4. 1 13
      account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountProfileController.java
  5. 32 0
      account/account-service/src/main/java/cn/reghao/tnb/account/app/middleware/RabbitProducer.java
  6. 22 24
      account/account-service/src/main/java/cn/reghao/tnb/account/app/model/po/UserAccount.java
  7. 7 2
      account/account-service/src/main/java/cn/reghao/tnb/account/app/rpc/AccountQueryImpl.java
  8. 6 9
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/handler/AuthSuccessHandlerImpl.java
  9. 1 1
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AccountRegistryService.java
  10. 0 50
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AvatarService.java
  11. 21 17
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/CodeService.java
  12. 18 21
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountLoginServiceImpl.java
  13. 14 21
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountRegistryServiceImpl.java
  14. 5 0
      account/account-service/src/main/resources/application-cluster.yml
  15. 6 0
      account/account-service/src/main/resources/application-dev.yml
  16. 6 0
      account/account-service/src/main/resources/application-test.yml
  17. BIN
      account/account-service/src/main/resources/static/dist/images/face.jpg
  18. 37 0
      file/file-service/src/main/java/cn/reghao/tnb/file/app/controller/AvatarController.java
  19. 25 0
      file/file-service/src/main/java/cn/reghao/tnb/file/app/model/dto/AvatarUpdate.java
  20. 0 15
      file/file-service/src/main/java/cn/reghao/tnb/file/app/model/dto/ContentRange.java
  21. 38 0
      file/file-service/src/main/java/cn/reghao/tnb/file/app/service/AvatarService.java
  22. 6 0
      gateway/src/main/resources/application.yml
  23. 0 13
      message/message-api/src/main/java/cn/reghao/tnb/message/api/iface/MessageSender.java
  24. 36 7
      message/message-service/src/main/java/cn/reghao/tnb/message/app/rabbit/RabbitListeners.java
  25. 13 13
      message/message-service/src/main/java/cn/reghao/tnb/message/app/service/MessageConsumer.java

+ 0 - 18
account/account-api/src/main/java/cn/reghao/tnb/account/api/dto/UserAvatarUpdate.java

@@ -1,18 +0,0 @@
-package cn.reghao.tnb.account.api.dto;
-
-import lombok.Getter;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-
-/**
- * @author reghao
- * @date 2023-06-22 14:09:06
- */
-@Getter
-public class UserAvatarUpdate {
-    @NotNull
-    private Integer channelId;
-    @NotBlank
-    private String uploadId;
-}

+ 1 - 0
account/account-api/src/main/java/cn/reghao/tnb/account/api/iface/AccountQuery.java

@@ -21,4 +21,5 @@ public interface AccountQuery {
     AccountInfo getByUsername(String username);
     @Deprecated
     Long createCrawledAccount(CrawledUser crawledUser);
+    void updateAvatar(long userId, String avatarUrl);
 }

+ 9 - 5
account/account-service/pom.xml

@@ -32,11 +32,6 @@
             <artifactId>message-api</artifactId>
             <version>1.0.0-SNAPSHOT</version>
         </dependency>
-        <dependency>
-            <groupId>cn.reghao.tnb.file</groupId>
-            <artifactId>file-api</artifactId>
-            <version>1.0.0-SNAPSHOT</version>
-        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -156,6 +151,15 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-freemarker</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.amqp</groupId>
+            <artifactId>spring-rabbit</artifactId>
+        </dependency>
     </dependencies>
 
     <profiles>

+ 1 - 13
account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountProfileController.java

@@ -2,11 +2,9 @@ package cn.reghao.tnb.account.app.controller;
 
 import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.jutil.web.WebResult;
-import cn.reghao.tnb.account.api.dto.UserAvatarUpdate;
 import cn.reghao.tnb.account.app.model.dto.PasswordUpdateDto;
 import cn.reghao.tnb.account.app.model.dto.UserEmailUpdate;
 import cn.reghao.tnb.account.app.service.AccountProfileService;
-import cn.reghao.tnb.account.app.service.AvatarService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.http.MediaType;
@@ -14,7 +12,6 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.constraints.NotBlank;
-import java.util.Map;
 
 /**
  * @author reghao
@@ -24,21 +21,12 @@ import java.util.Map;
 @RestController
 @RequestMapping("/api/account")
 public class AccountProfileController {
-    private final AvatarService avatarService;
     private final AccountProfileService accountProfileService;
 
-    public AccountProfileController(AvatarService avatarService, AccountProfileService accountProfileService) {
-        this.avatarService = avatarService;
+    public AccountProfileController(AccountProfileService accountProfileService) {
         this.accountProfileService = accountProfileService;
     }
 
-    @ApiOperation(value = "修改账号头像", notes = "N")
-    @PostMapping(value = "/profile/avatar", produces = MediaType.APPLICATION_JSON_VALUE)
-    public String updateUserAvatar(@RequestBody @Validated UserAvatarUpdate userAvatarUpdate) throws Exception {
-        String avatarUrl = avatarService.updateAvatar(userAvatarUpdate);
-        return WebResult.success(Map.of("url", avatarUrl));
-    }
-
     @ApiOperation(value = "修改用户名", notes = "N")
     @PostMapping(value = "/screenname", produces = MediaType.APPLICATION_JSON_VALUE)
     public String updateUserScreenName(@NotBlank(message = "用户名不能为空") String screenName) {

+ 32 - 0
account/account-service/src/main/java/cn/reghao/tnb/account/app/middleware/RabbitProducer.java

@@ -0,0 +1,32 @@
+package cn.reghao.tnb.account.app.middleware;
+
+import cn.reghao.jutil.jdk.serializer.JsonConverter;
+import cn.reghao.tnb.message.api.dto.msg.LoginMessage;
+import cn.reghao.tnb.message.api.dto.msg.VerifyMessage;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author reghao
+ * @date 2024-04-19 14:38:30
+ */
+@Component
+public class RabbitProducer {
+    private final RabbitTemplate rabbitTemplate;
+
+    public RabbitProducer(RabbitTemplate rabbitTemplate) {
+        this.rabbitTemplate = rabbitTemplate;
+    }
+
+    public void sendVerifyMessage(VerifyMessage verifyMessage) {
+        String routingKey = "tnb.message.account";
+        String jsonPayload = JsonConverter.objectToJson(verifyMessage);
+        rabbitTemplate.convertAndSend(routingKey, verifyMessage);
+    }
+
+    public void sendLoginMessage(LoginMessage loginMessage) {
+        String routingKey = "tnb.message.account";
+        String jsonPayload = JsonConverter.objectToJson(loginMessage);
+        rabbitTemplate.convertAndSend(routingKey, jsonPayload);
+    }
+}

+ 22 - 24
account/account-service/src/main/java/cn/reghao/tnb/account/app/model/po/UserAccount.java

@@ -52,55 +52,53 @@ public class UserAccount extends BaseObject<Integer> {
     private String signature;
     private Integer gender;
 
-    @Deprecated
-    public UserAccount(long userId, String mobile, String encodedPassword, String salt) {
+    public UserAccount(long userId, String email, String mobile) {
         this.userId = userId;
         this.accountType = AccountType.tnb.getValue();
         this.username = String.format("tnb_%s", userId);
-        this.email = String.format("tnb_%s@reghao.cn", userId);
+        this.email = email;
         this.mobile = mobile;
-        this.encodedPassword = encodedPassword;
-        this.salt = salt;
+        this.encodedPassword = "";
+        this.salt = "";
         this.createAt = LocalDateTime.now();
         this.notify = false;
         this.role = AccountRole.user.getValue();
         this.enabled = true;
         this.locked = false;
         this.screenName = this.username;
-        this.avatarUrl = "";
+        this.avatarUrl = "/dist/images/face.jpg";
         this.gender = 2;
     }
 
-    @Deprecated
-    public UserAccount(long userId, String username, String screenName, String email, String avatarUrl) {
+    public UserAccount(long userId, String email, String mobile, String encodedPassword, String salt) {
         this.userId = userId;
-        this.accountType = AccountType.bilibili.getValue();
-        this.username = username;
+        this.accountType = AccountType.tnb.getValue();
+        this.username = String.format("tnb_%s", userId);
+        this.mobile = mobile;
         this.email = email;
+        this.encodedPassword = encodedPassword;
+        this.salt = salt;
         this.createAt = LocalDateTime.now();
+        this.notify = false;
         this.role = AccountRole.user.getValue();
         this.enabled = true;
         this.locked = false;
-        this.screenName = screenName;
-        this.avatarUrl = avatarUrl;
+        this.screenName = this.username;
+        this.avatarUrl = "/dist/images/face.jpg";
+        this.gender = 2;
     }
 
-    public UserAccount(long userId, String email, String mobile, String encodedPassword, String salt,
-                       int accountType, String role, String avatarUrl) {
+    @Deprecated
+    public UserAccount(long userId, String username, String screenName, String avatarUrl) {
         this.userId = userId;
-        this.accountType = accountType;
-        this.username = String.format("tnb_%s", userId);
-        this.mobile = mobile;
-        this.email = email;
-        this.encodedPassword = encodedPassword;
-        this.salt = salt;
+        this.accountType = AccountType.bilibili.getValue();
+        this.username = username;
+        this.email = String.format("%s@tnb.cn", userId);;
         this.createAt = LocalDateTime.now();
-        this.notify = false;
-        this.role = role;
+        this.role = AccountRole.user.getValue();
         this.enabled = true;
         this.locked = false;
-        this.screenName = this.username;
+        this.screenName = screenName;
         this.avatarUrl = avatarUrl;
-        this.gender = 2;
     }
 }

+ 7 - 2
account/account-service/src/main/java/cn/reghao/tnb/account/app/rpc/AccountQueryImpl.java

@@ -7,6 +7,7 @@ import cn.reghao.tnb.account.api.dto.CrawledUser;
 import cn.reghao.tnb.account.api.iface.AccountQuery;
 import cn.reghao.tnb.account.app.db.repository.AccountRepository;
 import cn.reghao.tnb.account.app.model.po.UserAccount;
+import cn.reghao.tnb.account.app.security.form.AccountAuthToken;
 import cn.reghao.tnb.account.app.service.AccountTokenService;
 import cn.reghao.tnb.account.app.service.impl.AccountRegistryServiceImpl;
 import org.apache.dubbo.config.annotation.DubboService;
@@ -96,8 +97,7 @@ public class AccountQueryImpl implements AccountQuery {
             userId = accountRegistryService.getNextUserId();
         }
 
-        String email = String.format("%s@tnb.cn", userId);
-        UserAccount userAccount = new UserAccount(userId, username, screenName, email, avatarUrl);
+        UserAccount userAccount = new UserAccount(userId, username, screenName, avatarUrl);
         accountRepository.saveAccount(userAccount);
         return userId;
     }
@@ -106,4 +106,9 @@ public class AccountQueryImpl implements AccountQuery {
     public List<AccountAvatar> getAccountAvatars(List<Long> userIds) {
         return accountRepository.getAccountAvatars(userIds);
     }
+
+    @Override
+    public void updateAvatar(long userId, String avatarUrl) {
+        accountRepository.updateUserAvatar(userId, avatarUrl);
+    }
 }

+ 6 - 9
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/handler/AuthSuccessHandlerImpl.java

@@ -3,6 +3,7 @@ package cn.reghao.tnb.account.app.security.handler;
 import cn.reghao.jutil.jdk.converter.DateTimeConverter;
 import cn.reghao.jutil.web.WebResult;
 import cn.reghao.jutil.web.ServletUtil;
+import cn.reghao.tnb.account.app.middleware.RabbitProducer;
 import cn.reghao.tnb.account.app.model.constant.LoginPlat;
 import cn.reghao.tnb.account.api.dto.AccountInfo;
 import cn.reghao.tnb.account.app.model.dto.AccountLoginRet;
@@ -15,8 +16,6 @@ import cn.reghao.tnb.account.app.service.AccountTokenService;
 import cn.reghao.tnb.account.app.service.LoginAttemptService;
 import cn.reghao.tnb.message.api.constant.NotifyType;
 import cn.reghao.tnb.message.api.dto.msg.LoginMessage;
-import cn.reghao.tnb.message.api.iface.MessageSender;
-import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
 import org.springframework.security.web.savedrequest.SavedRequest;
@@ -27,7 +26,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.net.URI;
 import java.time.LocalDateTime;
 
 /**
@@ -38,15 +36,14 @@ import java.time.LocalDateTime;
  */
 @Component
 public class AuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
-    @DubboReference(check = false)
-    private MessageSender messageSender;
-
+    private final RabbitProducer rabbitProducer;
     private final LoginAttemptService loginAttemptService;
     private final UserAccountMapper userAccountMapper;
     private final AccountTokenService accountTokenService;
 
-    public AuthSuccessHandlerImpl(LoginAttemptService loginAttemptService, UserAccountMapper userAccountMapper,
-                                  AccountTokenService accountTokenService) {
+    public AuthSuccessHandlerImpl(RabbitProducer rabbitProducer, LoginAttemptService loginAttemptService,
+                                  UserAccountMapper userAccountMapper, AccountTokenService accountTokenService) {
+        this.rabbitProducer = rabbitProducer;
         this.loginAttemptService = loginAttemptService;
         this.userAccountMapper = userAccountMapper;
         this.accountTokenService = accountTokenService;
@@ -83,7 +80,7 @@ public class AuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
             LocalDateTime localDateTime = loginAttempts.getLoginAt();
             String loginAt = DateTimeConverter.format(localDateTime);
             LoginMessage loginMessage = new LoginMessage(notifyType, receiver, publicIp, userAgent, loginAt);
-            messageSender.sendMessage(loginMessage);
+            rabbitProducer.sendLoginMessage(loginMessage);
         }
 
         long timeout = rememberMe ? 3600*24*30 : 0;

+ 1 - 1
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AccountRegistryService.java

@@ -10,6 +10,6 @@ import cn.reghao.tnb.account.app.model.dto.UserRegisterDto;
  */
 public interface AccountRegistryService {
     Result createAccount(UserRegisterDto userRegisterDto);
-    AccountDetail createAccount(String mobile, String password);
+    AccountDetail createAccount(String principal, String password);
     Result getRegistryStatus();
 }

+ 0 - 50
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AvatarService.java

@@ -1,50 +0,0 @@
-package cn.reghao.tnb.account.app.service;
-
-import cn.reghao.file.api.iface.FileService;
-import cn.reghao.file.api.iface.OssService;
-import cn.reghao.tnb.account.api.dto.UserAvatarUpdate;
-import cn.reghao.tnb.account.app.db.repository.AccountRepository;
-import cn.reghao.oss.sdk.model.dto.media.ImageInfo;
-import cn.reghao.tnb.account.app.security.form.AccountAuthToken;
-import org.apache.dubbo.config.annotation.DubboReference;
-import org.springframework.stereotype.Service;
-
-/**
- * @author reghao
- * @date 2024-02-15 12:32:09
- */
-@Service
-public class AvatarService {
-    @DubboReference(check = false)
-    private OssService ossService;
-    @DubboReference(check = false)
-    private FileService fileService;
-
-    private final AccountTokenService accountTokenService;
-    private final AccountRepository accountRepository;
-
-    public AvatarService(AccountTokenService accountTokenService, AccountRepository accountRepository) {
-        this.accountTokenService = accountTokenService;
-        this.accountRepository = accountRepository;
-    }
-
-    public String getAccountAvatar(long userId) {
-        return fileService.getAccountAvatar(userId);
-    }
-
-    public String updateAvatar(UserAvatarUpdate userAvatarUpdate) throws Exception {
-        int channelId = userAvatarUpdate.getChannelId();
-        String uploadId = userAvatarUpdate.getUploadId();
-        ImageInfo imageInfo = ossService.getImageInfo(channelId, uploadId);
-        if (imageInfo != null) {
-            String avatarUrl = imageInfo.getUrl();
-
-            AccountAuthToken authToken = accountTokenService.getAuthedToken();
-            long userId = authToken.getUserId();
-            accountRepository.updateUserAvatar(userId, avatarUrl);
-            return avatarUrl;
-        }
-
-        return null;
-    }
-}

+ 21 - 17
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/CodeService.java

@@ -6,6 +6,7 @@ import cn.reghao.jutil.jdk.string.StringRegexp;
 import cn.reghao.tnb.account.api.constant.VerifyChannel;
 import cn.reghao.tnb.account.api.dto.VerifyCode;
 import cn.reghao.tnb.account.app.db.repository.AccountRepository;
+import cn.reghao.tnb.account.app.middleware.RabbitProducer;
 import cn.reghao.tnb.account.app.model.po.UserAccount;
 import cn.reghao.tnb.account.app.model.po.UserRegistry;
 import cn.reghao.tnb.account.app.util.CaptchaUtil;
@@ -13,9 +14,7 @@ import cn.reghao.tnb.account.app.redis.RedisKeys;
 import cn.reghao.tnb.account.app.redis.ds.RedisString;
 import cn.reghao.tnb.message.api.constant.NotifyType;
 import cn.reghao.tnb.message.api.dto.msg.VerifyMessage;
-import cn.reghao.tnb.message.api.iface.MessageSender;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.boot.autoconfigure.web.ServerProperties;
 import org.springframework.stereotype.Service;
 
@@ -31,14 +30,13 @@ import java.io.InputStream;
 @Slf4j
 @Service
 public class CodeService {
-    @DubboReference(check = false)
-    private MessageSender messageSender;
-
+    private final RabbitProducer rabbitProducer;
     private final RedisString redisString;
     private final long sessionTimeout;
     private final AccountRepository accountRepository;
 
-    public CodeService(RedisString redisString, ServerProperties serverProperties, AccountRepository accountRepository) {
+    public CodeService(RabbitProducer rabbitProducer, RedisString redisString, ServerProperties serverProperties, AccountRepository accountRepository) {
+        this.rabbitProducer = rabbitProducer;
         this.redisString = redisString;
         this.sessionTimeout = serverProperties.getServlet().getSession().getTimeout().getSeconds();
         this.accountRepository = accountRepository;
@@ -66,25 +64,31 @@ public class CodeService {
         Integer channel = verifyCode.getChannel();
         String receiver = verifyCode.getReceiver();
 
-        UserRegistry userRegistry = accountRepository.getUserRegistry();
-        if (channel == VerifyChannel.registryChannel.getValue() && !userRegistry.getEnabled()) {
-            return Result.fail("系统当前未开放注册");
-        }
-
         int type;
-        UserAccount userAccount;
         if (StringRegexp.matchEmail(receiver)) {
             type = NotifyType.email.getValue();
-            userAccount = accountRepository.findByEmail(type, receiver);
         } else if (StringRegexp.matchMobile(receiver)) {
             type = NotifyType.mobile.getValue();
-            userAccount = accountRepository.findByEmail(type, receiver);
         } else {
             return Result.fail("邮箱/手机号格式错误");
         }
 
-        if (userAccount != null) {
-            return Result.fail("邮箱/手机号已存在");
+        UserRegistry userRegistry = accountRepository.getUserRegistry();
+        if (channel == VerifyChannel.registryChannel.getValue()) {
+            if (!userRegistry.getEnabled()) {
+                return Result.fail("系统当前未开放注册");
+            } else {
+                UserAccount userAccount = null;
+                if (type == NotifyType.email.getValue()) {
+                    userAccount = accountRepository.findByEmail(type, receiver);
+                } else if (type == NotifyType.mobile.getValue()) {
+                    userAccount = accountRepository.findByEmail(type, receiver);
+                }
+
+                if (userAccount != null) {
+                    return Result.fail("帐号已存在");
+                }
+            }
         }
 
         String code;
@@ -98,7 +102,7 @@ public class CodeService {
 
         String title = "验证码";
         VerifyMessage verifyMessage = new VerifyMessage(receiver, type, title, code);
-        messageSender.sendMessage(verifyMessage);
+        rabbitProducer.sendVerifyMessage(verifyMessage);
         return Result.success("验证码已发送, 请及时查收");
     }
 

+ 18 - 21
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountLoginServiceImpl.java

@@ -83,8 +83,6 @@ public class AccountLoginServiceImpl implements AccountLoginService {
             accountDetail = userAuthQuery.findByMobile(username);
         } else {
             accountDetail = userAuthQuery.findByUsername(username);
-            /*String errMsg = String.format("帐号 %s 格式不正确", username);
-            throw new UsernameNotFoundException(errMsg);*/
         }
 
         if (accountDetail == null) {
@@ -126,28 +124,27 @@ public class AccountLoginServiceImpl implements AccountLoginService {
         String principal = (String) authToken.getPrincipal();
         String credential = (String) authToken.getCredentials();
 
-        UserDetail userDetail = (UserDetail) loadUserByUsername(principal);
-        String savedCredential = codeService.getVerifyCode(VerifyChannel.loginChannel.getValue(), principal);
-        if (savedCredential == null) {
-            String errMsg = "当前会话已过期, 请刷新页面后重新登录. 或者检查浏览器是否禁用了 cookie";
-            throw new DisabledException(errMsg);
-        } else if (!credential.equals(savedCredential)) {
-            String errMsg = "短信验证码不正确";
-            if (userDetail != null) {
-                long userId = userDetail.getUserId();
-                loginAttemptService.check(userId, plat);
-                throw new AccountLoginException(errMsg, userId, plat);
+        UserDetail userDetail;
+        try {
+            userDetail = (UserDetail) loadUserByUsername(principal);
+            String savedCredential = codeService.getVerifyCode(VerifyChannel.loginChannel.getValue(), principal);
+            if (savedCredential == null) {
+                String errMsg = "当前会话已过期, 请刷新页面后重新登录. 或者检查浏览器是否禁用了 cookie";
+                throw new DisabledException(errMsg);
+            } else if (!credential.equals(savedCredential)) {
+                String errMsg = "短信验证码不正确";
+                if (userDetail != null) {
+                    long userId = userDetail.getUserId();
+                    loginAttemptService.check(userId, plat);
+                    throw new AccountLoginException(errMsg, userId, plat);
+                }
+
+                throw new DisabledException(errMsg);
             }
-
-            throw new DisabledException(errMsg);
-        }
-
-        // 帐号不存在则自动注册
-        if (userDetail == null) {
+        } catch (UsernameNotFoundException e) {
+            // 验证码方式登入时帐号不存在则自动注册
             AccountDetail accountDetail = accountRegistryService.createAccount(principal, "");
             userDetail = new UserDetail(accountDetail);
-            /*String errMsg = String.format("帐号 %s 未注册", principal);
-            throw new UsernameNotFoundException(errMsg);*/
         }
 
         userDetail.setLoginType(LoginType.mobileCode.getValue());

+ 14 - 21
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountRegistryServiceImpl.java

@@ -5,13 +5,10 @@ import cn.reghao.jutil.jdk.result.ResultStatus;
 import cn.reghao.jutil.jdk.security.RandomString;
 import cn.reghao.jutil.jdk.string.StringRegexp;
 import cn.reghao.tnb.account.api.constant.VerifyChannel;
-import cn.reghao.tnb.account.app.model.constant.AccountRole;
-import cn.reghao.tnb.account.app.model.constant.AccountType;
 import cn.reghao.tnb.account.app.model.dto.AccountDetail;
 import cn.reghao.tnb.account.app.model.dto.UserRegisterDto;
 import cn.reghao.tnb.account.app.db.repository.AccountRepository;
 import cn.reghao.tnb.account.app.model.po.UserRegistry;
-import cn.reghao.tnb.account.app.service.AvatarService;
 import cn.reghao.tnb.account.app.service.CodeService;
 import cn.reghao.tnb.account.app.model.po.UserAccount;
 import cn.reghao.tnb.account.app.service.AccountRegistryService;
@@ -33,16 +30,13 @@ public class AccountRegistryServiceImpl implements AccountRegistryService {
     private final PasswordEncoder passwordEncoder;
     private final AccountRepository accountRepository;
     private final PubkeyService pubkeyService;
-    private final AvatarService avatarService;
 
     public AccountRegistryServiceImpl(CodeService codeService, PasswordEncoder passwordEncoder,
-                                      AccountRepository accountRepository, PubkeyService pubkeyService,
-                                      AvatarService avatarService) {
+                                      AccountRepository accountRepository, PubkeyService pubkeyService) {
         this.codeService = codeService;
         this.passwordEncoder = passwordEncoder;
         this.accountRepository = accountRepository;
         this.pubkeyService = pubkeyService;
-        this.avatarService = avatarService;
     }
 
     @Override
@@ -64,8 +58,8 @@ public class AccountRegistryServiceImpl implements AccountRegistryService {
             return Result.fail("短信验证码不正确, 请重新获取");
         }
 
-        String mobile = null;
-        String email = null;
+        String email = "";
+        String mobile = "";
         if (StringRegexp.matchEmail(principal)) {
             email = principal;
         } else if (StringRegexp.matchMobile(principal)) {
@@ -83,14 +77,7 @@ public class AccountRegistryServiceImpl implements AccountRegistryService {
         long userId = getNextUserId();
         String salt = RandomString.getSalt(64);
         String encodedPassword = passwordEncoder.encode(decryptCredential + salt);
-        int accountType = AccountType.tnb.getValue();
-        String role = AccountRole.user.getValue();
-        String avatarUrl = avatarService.getAccountAvatar(userId);
-        if (avatarUrl == null) {
-            return Result.result(ResultStatus.FAIL, "头像创建失败");
-        }
-
-        UserAccount userAccount = new UserAccount(userId, email, mobile, encodedPassword, salt, accountType, role, avatarUrl);
+        UserAccount userAccount = new UserAccount(userId, email, mobile, encodedPassword, salt);
         accountRepository.saveAccount(userAccount);
         return Result.result(ResultStatus.SUCCESS, "帐号已创建");
     }
@@ -108,11 +95,17 @@ public class AccountRegistryServiceImpl implements AccountRegistryService {
     }
 
     @Override
-    public AccountDetail createAccount(String mobile, String password) {
+    public AccountDetail createAccount(String principal, String password) {
+        String email = "";
+        String mobile = "";
+        if (StringRegexp.matchEmail(principal)) {
+            email = principal;
+        } else if (StringRegexp.matchMobile(principal)) {
+            mobile = principal;
+        }
+
         long userId = getNextUserId();
-        String salt = RandomString.getSalt(64);
-        String encodedPassword = passwordEncoder.encode(password + salt);
-        UserAccount userAccount = new UserAccount(userId, mobile, encodedPassword, salt);
+        UserAccount userAccount = new UserAccount(userId, email, mobile);
         accountRepository.saveAccount(userAccount);
         return accountRepository.getUserDetailByMobile(mobile);
     }

+ 5 - 0
account/account-service/src/main/resources/application-cluster.yml

@@ -13,6 +13,11 @@ spring:
     host: 192.168.0.177
     port: 6379
     password: Test@123456
+  rabbitmq:
+    addresses: 192.168.0.177:5672,192.168.0.178:5672,192.168.0.179:5672
+    virtual-host: /
+    username: test
+    password: Test@123456
   datasource:
     url: jdbc:mysql://192.168.0.177:3306/tnb_account_tdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
     username: test

+ 6 - 0
account/account-service/src/main/resources/application-dev.yml

@@ -12,6 +12,12 @@ spring:
     host: localhost
     port: 6379
     password: Dev@123456
+  rabbitmq:
+    host: localhost
+    port: 5672
+    virtual-host: /
+    username: dev
+    password: Dev@123456
   datasource:
     url: jdbc:mysql://localhost:3306/tnb_account_rdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
     username: dev

+ 6 - 0
account/account-service/src/main/resources/application-test.yml

@@ -12,6 +12,12 @@ spring:
     host: 192.168.0.210
     port: 6379
     password: Test@123456
+  rabbitmq:
+    host: 192.168.0.210
+    port: 5672
+    virtual-host: /
+    username: test
+    password: Test@123456
   datasource:
     url: jdbc:mysql://192.168.0.210:3306/tnb_account_tdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
     username: test

BIN
account/account-service/src/main/resources/static/dist/images/face.jpg


+ 37 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/controller/AvatarController.java

@@ -0,0 +1,37 @@
+package cn.reghao.tnb.file.app.controller;
+
+import cn.reghao.jutil.web.WebResult;
+import cn.reghao.tnb.file.app.model.dto.AvatarUpdate;
+import cn.reghao.tnb.file.app.service.AvatarService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.http.MediaType;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2024-12-06 10:54:39
+ */
+@Api(tags = "头像接口")
+@RestController
+@RequestMapping("/api/file/avatar")
+public class AvatarController {
+    private final AvatarService avatarService;
+
+    public AvatarController(AvatarService avatarService) {
+        this.avatarService = avatarService;
+    }
+
+    @ApiOperation(value = "更新账号头像", notes = "N")
+    @PostMapping(value = "/update", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String updateUserAvatar(@RequestBody @Validated AvatarUpdate avatarUpdate) throws Exception {
+        String avatarUrl = avatarService.updateAvatar(avatarUpdate);
+        return WebResult.success(Map.of("url", avatarUrl));
+    }
+}

+ 25 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/model/dto/AvatarUpdate.java

@@ -0,0 +1,25 @@
+package cn.reghao.tnb.file.app.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.checkerframework.checker.units.qual.A;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author reghao
+ * @date 2022-11-28 11:27:27
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Setter
+@Getter
+public class AvatarUpdate {
+    @NotNull
+    private Integer channelId;
+    @NotBlank
+    private String uploadId;
+}

+ 0 - 15
file/file-service/src/main/java/cn/reghao/tnb/file/app/model/dto/ContentRange.java

@@ -1,15 +0,0 @@
-package cn.reghao.tnb.file.app.model.dto;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * @author reghao
- * @date 2022-11-28 11:27:27
- */
-@AllArgsConstructor
-@Getter
-public class ContentRange {
-    private long start;
-    private long end;
-}

+ 38 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/service/AvatarService.java

@@ -0,0 +1,38 @@
+package cn.reghao.tnb.file.app.service;
+
+import cn.reghao.file.api.iface.OssService;
+import cn.reghao.oss.sdk.model.dto.media.ImageInfo;
+import cn.reghao.tnb.account.api.iface.AccountQuery;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.file.app.model.dto.AvatarUpdate;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author reghao
+ * @date 2024-12-06 10:55:13
+ */
+@Service
+public class AvatarService {
+    @DubboReference(check = false, timeout = 60_000)
+    private AccountQuery accountQuery;
+    private final OssService ossService;
+
+    public AvatarService(OssService ossService) {
+        this.ossService = ossService;
+    }
+
+    public String updateAvatar(AvatarUpdate avatarUpdate) throws Exception {
+        int channelId = avatarUpdate.getChannelId();
+        String uploadId = avatarUpdate.getUploadId();
+        ImageInfo imageInfo = ossService.getImageInfo(channelId, uploadId);
+        if (imageInfo != null) {
+            String avatarUrl = imageInfo.getUrl();
+            long loginUser = UserContext.getUser();
+            accountQuery.updateAvatar(loginUser, avatarUrl);
+            return avatarUrl;
+        }
+
+        return null;
+    }
+}

+ 6 - 0
gateway/src/main/resources/application.yml

@@ -18,6 +18,12 @@ spring:
             - Path=/api/account/**
           filters:
             - StripPrefix=0
+        - id: static-router
+          uri: lb://account-service
+          predicates:
+            - Path=/dist/images/**
+          filters:
+            - StripPrefix=0
         - id: auth-router
           uri: lb://account-service
           predicates:

+ 0 - 13
message/message-api/src/main/java/cn/reghao/tnb/message/api/iface/MessageSender.java

@@ -1,13 +0,0 @@
-package cn.reghao.tnb.message.api.iface;
-
-import cn.reghao.tnb.message.api.dto.msg.BaseMessage;
-import cn.reghao.tnb.message.api.dto.msg.LoginMessage;
-import cn.reghao.tnb.message.api.dto.msg.VerifyMessage;
-
-/**
- * @author reghao
- * @date 2023-08-28 13:55:03
- */
-public interface MessageSender {
-    void sendMessage(BaseMessage baseMessage);
-}

+ 36 - 7
message/message-service/src/main/java/cn/reghao/tnb/message/app/rabbit/RabbitListeners.java

@@ -1,14 +1,15 @@
 package cn.reghao.tnb.message.app.rabbit;
 
 import cn.reghao.jutil.jdk.serializer.JsonConverter;
+import cn.reghao.tnb.message.api.dto.msg.BaseMessage;
+import cn.reghao.tnb.message.app.service.MessageConsumer;
 import cn.reghao.tnb.message.app.ws.WsConnection;
 import cn.reghao.tnb.message.app.ws.msg.ChatPayload;
+import com.rabbitmq.client.Channel;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.amqp.core.ExchangeTypes;
-import org.springframework.amqp.rabbit.annotation.Exchange;
-import org.springframework.amqp.rabbit.annotation.Queue;
-import org.springframework.amqp.rabbit.annotation.QueueBinding;
-import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.annotation.*;
 import org.springframework.messaging.handler.annotation.Payload;
 import org.springframework.stereotype.Component;
 
@@ -22,17 +23,19 @@ import java.io.IOException;
 @Component
 public class RabbitListeners {
     private final WsConnection wsConnection;
+    private final MessageConsumer messageConsumer;
 
-    public RabbitListeners(WsConnection wsConnection) {
+    public RabbitListeners(WsConnection wsConnection, MessageConsumer messageConsumer) {
         this.wsConnection = wsConnection;
+        this.messageConsumer = messageConsumer;
     }
 
     @RabbitListener(bindings =@QueueBinding(
             // 队列名字可以随便定义
             value = @Queue(autoDelete = "false"),
             // 交换机的名字必须和生产者保持一致, fanout 是以广播模式(发布订阅模式)
-            exchange = @Exchange(value = "amq.fanout", type = ExchangeTypes.FANOUT)
-    ))
+            exchange = @Exchange(value = "amq.fanout", type = ExchangeTypes.FANOUT))
+    )
     public void chatMessageConsumer(@Payload String msg) {
         ChatPayload chatPayload = JsonConverter.jsonToObject(msg, ChatPayload.class);
         long receiverId = chatPayload.getReceiverId();
@@ -48,4 +51,30 @@ public class RabbitListeners {
             log.info("{} 的 session 不在本节点上", receiverId);
         }
     }
+
+    /*@RabbitListener(bindings =@QueueBinding(
+            value = @Queue(value = "tnb.message.verify", durable = "true"),
+            key = "tnb.account.message",
+            exchange = @Exchange(value = "amq.direct"))
+    )
+    public void verifyMessageConsumer(@Payload String msg) {
+    }*/
+
+    @RabbitListener(bindings =@QueueBinding(
+            value = @Queue(value = "tnb.message.account", durable = "true"),
+            key = "tnb.message.account",
+            exchange = @Exchange(value = "amq.direct")),
+            ackMode = "MANUAL"
+    )
+    @RabbitHandler
+    public void consumeMessage(@Payload BaseMessage baseMessage, Channel channel, Message message) {
+        messageConsumer.sendMessage(baseMessage);
+        long deliveryTag = message.getMessageProperties().getDeliveryTag();
+        try {
+            log.info("手动确认消息 {}", deliveryTag);
+            channel.basicAck(deliveryTag,true);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
 }

+ 13 - 13
message/message-service/src/main/java/cn/reghao/tnb/message/app/rpc/MessageSenderImpl.java → message/message-service/src/main/java/cn/reghao/tnb/message/app/service/MessageConsumer.java

@@ -1,15 +1,11 @@
-package cn.reghao.tnb.message.app.rpc;
+package cn.reghao.tnb.message.app.service;
 
-import cn.reghao.jutil.jdk.serializer.JsonConverter;
 import cn.reghao.tnb.message.api.constant.NotifyType;
 import cn.reghao.tnb.message.api.dto.msg.BaseMessage;
 import cn.reghao.tnb.message.api.dto.msg.LoginMessage;
-import cn.reghao.tnb.message.app.service.NotifyService;
-import cn.reghao.tnb.message.app.service.notifier.email.EmailMsg;
 import cn.reghao.tnb.message.api.dto.msg.VerifyMessage;
-import cn.reghao.tnb.message.api.iface.MessageSender;
+import cn.reghao.tnb.message.app.service.notifier.email.EmailMsg;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.stereotype.Service;
 
 /**
@@ -17,16 +13,14 @@ import org.springframework.stereotype.Service;
  * @date 2023-08-28 13:55:03
  */
 @Slf4j
-@DubboService
 @Service
-public class MessageSenderImpl implements MessageSender {
+public class MessageConsumer {
     private final NotifyService notifyService;
 
-    public MessageSenderImpl(NotifyService notifyService) {
+    public MessageConsumer(NotifyService notifyService) {
         this.notifyService = notifyService;
     }
 
-    @Override
     public void sendMessage(BaseMessage baseMessage) {
         if (baseMessage instanceof LoginMessage) {
             LoginMessage loginMessage = (LoginMessage) baseMessage;
@@ -37,13 +31,19 @@ public class MessageSenderImpl implements MessageSender {
                 String content = String.format("您的帐号在 %s 通过客户端 %s 登录, 登录 IP 为 %s", loginMessage.getLoginAt(), loginMessage.getUserAgent(), loginMessage.getPublicIp());
                 EmailMsg emailMsg = new EmailMsg(subject, content);
                 notifyService.notify(receiver, emailMsg);
+            } else if (notifyType == NotifyType.mobile.getValue()) {
+                log.info("短信消息尚未实现");
             }
         } else if (baseMessage instanceof VerifyMessage) {
             VerifyMessage verifyMessage = (VerifyMessage) baseMessage;
             int notifyType = verifyMessage.getNotifyType();
-            String receiver = verifyMessage.getReceiver();
-            EmailMsg emailMsg = new EmailMsg(verifyMessage.getTitle(), verifyMessage.getContent());
-            notifyService.notify(receiver, emailMsg);
+            if (notifyType == NotifyType.email.getValue()) {
+                String receiver = verifyMessage.getReceiver();
+                EmailMsg emailMsg = new EmailMsg(verifyMessage.getTitle(), verifyMessage.getContent());
+                notifyService.notify(receiver, emailMsg);
+            } else if (notifyType == NotifyType.mobile.getValue()) {
+                log.info("短信验证码尚未实现");
+            }
         }
     }
 }