From d15f1b4b3c109cb7f43063d5c34b6adb0c75840b Mon Sep 17 00:00:00 2001
From: BanTanger <88583317+BanTanger@users.noreply.github.com>
Date: Sat, 4 Nov 2023 23:01:17 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=A5=E6=A1=A5=E6=8E=A5=E6=A8=A1?=
=?UTF-8?q?=E5=BC=8F=E5=92=8C=E9=80=82=E9=85=8D=E5=99=A8=E6=A8=A1=E5=BC=8F?=
=?UTF-8?q?=E4=B8=A4=E7=A7=8D=E6=96=B9=E5=BC=8F=E5=90=84=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?=E7=AC=AC=E4=B8=89=E6=96=B9=20Oauth(github)=20=E7=99=BB?=
=?UTF-8?q?=E5=BD=95=20[demo=E5=BB=BA=E8=AE=BE]=20(#57)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: 以桥接和适配器两种方式实现第三方 Oauth(github) 登录 [demo]
---
README.md | 36 +++----
assert/design/design_pattern/adapter.md | 63 ++++++++++++
assert/design/design_pattern/login3rd.md | 61 ++++++++++++
assert/design/{ => photo}/IM-WhaleShark.png | Bin
...7\350\241\214\346\265\201\347\250\213.png" | Bin
...7\350\203\275\346\265\213\350\257\225.png" | Bin
...7\350\203\275\350\256\262\350\247\243.png" | Bin
...44\270\212\344\270\213\350\241\214ACK.png" | Bin
...6\346\210\221\350\201\224\347\263\273.png" | Bin
.../\345\206\231\346\211\251\346\225\243.png" | Bin
...7\350\267\257\347\224\261\345\261\202.png" | Bin
...0\346\201\257\347\241\256\350\256\244.png" | Bin
...4\346\255\245\346\224\271\350\277\233.png" | Bin
...5\347\232\204\345\274\212\347\253\257.png" | Bin
...3\346\236\204\346\233\264\345\217\230.png" | Bin
...0\346\200\247\344\274\240\350\276\276.png" | Bin
...1\346\200\247\344\277\235\350\257\201.png" | Bin
...4\350\260\203\350\256\276\350\256\241.png" | Bin
...6\346\236\204\350\256\276\350\256\241.png" | Bin
...0\346\201\257\346\265\201\350\275\254.png" | Bin
...0\345\210\266\351\207\215\345\217\221.png" | Bin
...0\346\201\257\347\241\256\350\256\244.png" | Bin
...4\346\255\245\346\265\201\347\250\213.png" | Bin
.../\350\257\273\346\211\251\346\225\243.png" | Bin
assert/sql/register_login_demo.sql | 13 +++
docker-compose.yml | 2 +-
docker/mysql/conf/my.cnf | 6 +-
docker/mysql/db/register_login_demo.sql | 13 +++
.../assert/adapter.md | 63 ++++++++++++
.../login-design-adapter-demo/assert/img.png | Bin 0 -> 62881 bytes
.../assert/login3rd.md | 52 ++++++++++
.../assert/success.png | Bin 0 -> 27030 bytes
.../assert/timeout.png | Bin 0 -> 91470 bytes
...234\211github\350\264\246\345\217\267.png" | Bin 0 -> 15014 bytes
.../login-design-adapter-demo/pom.xml | 19 ++++
.../bantanger/design/adapter/Application.java | 17 ++++
.../adapter/controller/UserController.java | 48 +++++++++
.../adapter/respository/UserRepository.java | 48 +++++++++
.../adapter/respository/dao/UserEntity.java | 35 +++++++
.../mapper/UserRegisterLoginMapper.java | 14 +++
.../adapter/service/Login3rdAdapter.java | 93 ++++++++++++++++++
.../adapter/service/Login3rdTarget.java | 28 ++++++
.../design/adapter/service/UserService.java | 57 +++++++++++
.../design/adapter/utils/HttpClientUtils.java | 57 +++++++++++
.../src/main/resources/application.yml | 79 +++++++++++++++
.../src/main/resources/logback-spring.xml | 54 ++++++++++
.../main/resources/register_login_demo.sql | 13 +++
.../login-design-bridge-demo/assert/bridge.md | 57 +++++++++++
.../login-design-bridge-demo/assert/img.png | Bin 0 -> 87385 bytes
.../login-design-bridge-demo/pom.xml | 19 ++++
.../bantanger/design/bridge/Application.java | 19 ++++
.../abst/AbstractRegisterLoginComponent.java | 25 +++++
.../bridge/abst/RegisterLoginComponent.java | 38 +++++++
.../RegisterLoginComponentFactory.java | 42 ++++++++
.../function/AbstractRegisterLoginFunc.java | 82 +++++++++++++++
.../function/RegisterLoginByDefault.java | 43 ++++++++
.../function/RegisterLoginByGithub.java | 92 +++++++++++++++++
.../bridge/function/RegisterLoginFunc.java | 42 ++++++++
.../bridge/controller/UserController.java | 46 +++++++++
.../bridge/respository/UserRepository.java | 48 +++++++++
.../bridge/respository/dao/UserEntity.java | 35 +++++++
.../mapper/UserRegisterLoginMapper.java | 14 +++
.../bridge/service/UserBridgeService.java | 34 +++++++
.../design/bridge/utils/HttpClientUtils.java | 57 +++++++++++
.../src/main/resources/application.yml | 79 +++++++++++++++
.../src/main/resources/logback-spring.xml | 54 ++++++++++
.../main/resources/register_login_demo.sql | 13 +++
.../src/main/resources/static/img.png | Bin 0 -> 62881 bytes
.../src/main/resources/static/success.png | Bin 0 -> 27030 bytes
.../src/main/resources/static/timeout.png | Bin 0 -> 91470 bytes
...234\211github\350\264\246\345\217\267.png" | Bin 0 -> 15014 bytes
.../login-ordinary-demo/logs/access_log.log | 1 +
.../error_register_login_demo.2023-11-04.log | 0
.../info_register_login_demo.2023-11-04.log | 61 ++++++++++++
.../login-ordinary-demo/pom.xml | 19 ++++
.../design/ordinary/Application.java | 17 ++++
.../ordinary/controller/UserController.java | 41 ++++++++
.../ordinary/respository/UserRepository.java | 48 +++++++++
.../ordinary/respository/dao/UserEntity.java | 35 +++++++
.../mapper/UserRegisterLoginMapper.java | 14 +++
.../service/UserLogin3rdServiceImpl.java | 55 +++++++++++
.../design/ordinary/service/UserService.java | 45 +++++++++
.../ordinary/service/UserServiceImpl.java | 78 +++++++++++++++
.../src/main/resources/application.yml | 51 ++++++++++
.../src/main/resources/logback-spring.xml | 54 ++++++++++
im-design-demo/im-register-login-demo/pom.xml | 87 ++++++++++++++++
im-design-demo/pom.xml | 25 +++++
pom.xml | 9 +-
88 files changed, 2327 insertions(+), 23 deletions(-)
create mode 100644 assert/design/design_pattern/adapter.md
create mode 100644 assert/design/design_pattern/login3rd.md
rename assert/design/{ => photo}/IM-WhaleShark.png (100%)
rename "assert/design/SDK\346\211\247\350\241\214\346\265\201\347\250\213.png" => "assert/design/photo/SDK\346\211\247\350\241\214\346\265\201\347\250\213.png" (100%)
rename "assert/design/websocket\345\212\237\350\203\275\346\265\213\350\257\225.png" => "assert/design/photo/websocket\345\212\237\350\203\275\346\265\213\350\257\225.png" (100%)
rename "assert/design/websocket\347\252\227\345\217\243\345\212\237\350\203\275\350\256\262\350\247\243.png" => "assert/design/photo/websocket\347\252\227\345\217\243\345\212\237\350\203\275\350\256\262\350\247\243.png" (100%)
rename "assert/design/\344\270\212\344\270\213\350\241\214ACK.png" => "assert/design/photo/\344\270\212\344\270\213\350\241\214ACK.png" (100%)
rename "assert/design/\344\270\216\346\210\221\350\201\224\347\263\273.png" => "assert/design/photo/\344\270\216\346\210\221\350\201\224\347\263\273.png" (100%)
rename "assert/design/\345\206\231\346\211\251\346\225\243.png" => "assert/design/photo/\345\206\231\346\211\251\346\225\243.png" (100%)
rename "assert/design/\345\210\206\345\270\203\345\274\217\350\267\257\347\224\261\345\261\202.png" => "assert/design/photo/\345\210\206\345\270\203\345\274\217\350\267\257\347\224\261\345\261\202.png" (100%)
rename "assert/design/\345\234\250\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png" => "assert/design/photo/\345\234\250\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png" (100%)
rename "assert/design/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\346\224\271\350\277\233.png" => "assert/design/photo/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\346\224\271\350\277\233.png" (100%)
rename "assert/design/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\347\232\204\345\274\212\347\253\257.png" => "assert/design/photo/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\347\232\204\345\274\212\347\253\257.png" (100%)
rename "assert/design/\346\225\260\346\215\256\345\272\223\347\273\223\346\236\204\346\233\264\345\217\230.png" => "assert/design/photo/\346\225\260\346\215\256\345\272\223\347\273\223\346\236\204\346\233\264\345\217\230.png" (100%)
rename "assert/design/\346\234\211\344\272\206TCP\344\270\272\344\273\200\344\271\210\350\277\230\350\246\201\344\277\235\350\257\201\345\217\257\351\235\240\346\200\247\344\274\240\350\276\276.png" => "assert/design/photo/\346\234\211\344\272\206TCP\344\270\272\344\273\200\344\271\210\350\277\230\350\246\201\344\277\235\350\257\201\345\217\257\351\235\240\346\200\247\344\274\240\350\276\276.png" (100%)
rename "assert/design/\346\266\210\346\201\257\345\271\202\347\255\211\346\200\247\344\277\235\350\257\201.png" => "assert/design/photo/\346\266\210\346\201\257\345\271\202\347\255\211\346\200\247\344\277\235\350\257\201.png" (100%)
rename "assert/design/\346\266\210\346\201\257\346\213\211\345\217\226\345\211\215\345\220\216\347\253\257\350\201\224\350\260\203\350\256\276\350\256\241.png" => "assert/design/photo/\346\266\210\346\201\257\346\213\211\345\217\226\345\211\215\345\220\216\347\253\257\350\201\224\350\260\203\350\256\276\350\256\241.png" (100%)
rename "assert/design/\346\266\210\346\201\257\346\213\211\345\217\226\346\236\266\346\236\204\350\256\276\350\256\241.png" => "assert/design/photo/\346\266\210\346\201\257\346\213\211\345\217\226\346\236\266\346\236\204\350\256\276\350\256\241.png" (100%)
rename "assert/design/\346\266\210\346\201\257\346\265\201\350\275\254.png" => "assert/design/photo/\346\266\210\346\201\257\346\265\201\350\275\254.png" (100%)
rename "assert/design/\347\246\201\346\255\242\345\256\242\346\210\267\347\253\257\346\227\240\351\231\220\345\210\266\351\207\215\345\217\221.png" => "assert/design/photo/\347\246\201\346\255\242\345\256\242\346\210\267\347\253\257\346\227\240\351\231\220\345\210\266\351\207\215\345\217\221.png" (100%)
rename "assert/design/\347\246\273\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png" => "assert/design/photo/\347\246\273\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png" (100%)
rename "assert/design/\347\276\244\350\201\212\346\266\210\346\201\257\345\220\214\346\255\245\346\265\201\347\250\213.png" => "assert/design/photo/\347\276\244\350\201\212\346\266\210\346\201\257\345\220\214\346\255\245\346\265\201\347\250\213.png" (100%)
rename "assert/design/\350\257\273\346\211\251\346\225\243.png" => "assert/design/photo/\350\257\273\346\211\251\346\225\243.png" (100%)
create mode 100644 assert/sql/register_login_demo.sql
create mode 100644 docker/mysql/db/register_login_demo.sql
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/adapter.md
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/img.png
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/login3rd.md
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/success.png
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/timeout.png
create mode 100644 "im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/\346\225\260\346\215\256\345\272\223\345\255\230\346\234\211github\350\264\246\345\217\267.png"
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/pom.xml
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/Application.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/controller/UserController.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/respository/UserRepository.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/respository/dao/UserEntity.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/respository/mapper/UserRegisterLoginMapper.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/service/Login3rdAdapter.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/service/Login3rdTarget.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/service/UserService.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/java/com/bantanger/design/adapter/utils/HttpClientUtils.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/resources/application.yml
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/resources/logback-spring.xml
create mode 100644 im-design-demo/im-register-login-demo/login-design-adapter-demo/src/main/resources/register_login_demo.sql
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/assert/bridge.md
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/assert/img.png
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/pom.xml
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/Application.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/abst/AbstractRegisterLoginComponent.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/abst/RegisterLoginComponent.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/abst/factory/RegisterLoginComponentFactory.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/function/AbstractRegisterLoginFunc.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/function/RegisterLoginByDefault.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/function/RegisterLoginByGithub.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/bridge/function/RegisterLoginFunc.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/controller/UserController.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/respository/UserRepository.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/respository/dao/UserEntity.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/respository/mapper/UserRegisterLoginMapper.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/service/UserBridgeService.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/java/com/bantanger/design/bridge/utils/HttpClientUtils.java
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/application.yml
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/logback-spring.xml
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/register_login_demo.sql
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/static/img.png
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/static/success.png
create mode 100644 im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/static/timeout.png
create mode 100644 "im-design-demo/im-register-login-demo/login-design-bridge-demo/src/main/resources/static/\346\225\260\346\215\256\345\272\223\345\255\230\346\234\211github\350\264\246\345\217\267.png"
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/logs/access_log.log
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/logs/error_register_login_demo.2023-11-04.log
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/logs/info_register_login_demo.2023-11-04.log
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/pom.xml
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/Application.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/controller/UserController.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/respository/UserRepository.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/respository/dao/UserEntity.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/respository/mapper/UserRegisterLoginMapper.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/service/UserLogin3rdServiceImpl.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/service/UserService.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/java/com/bantanger/design/ordinary/service/UserServiceImpl.java
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/resources/application.yml
create mode 100644 im-design-demo/im-register-login-demo/login-ordinary-demo/src/main/resources/logback-spring.xml
create mode 100644 im-design-demo/im-register-login-demo/pom.xml
create mode 100644 im-design-demo/pom.xml
diff --git a/README.md b/README.md
index bca8a04..5826ee2 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# IM-WhaleShark
-![IM-WhaleShark](assert/design/IM-WhaleShark.png)
+![IM-WhaleShark](assert/design/photo/IM-WhaleShark.png)
IM-WhaleShark(鲸鲨)是基于 Netty 实现的高性能分布式 IM 即时通讯系统
@@ -169,11 +169,11 @@ Docker 部署测试请访问 `localhost:19000`
浏览方式通过 F12 查看服务端发送的 `json` 格式是否正确
-![](assert/design/websocket窗口功能讲解.png)
+![](assert/design/photo/websocket窗口功能讲解.png)
如图所示: 平台 [appId = 10001] 的用户 [userId = 10001, clientType = 3, imei = 200] 在登录 Login 之后向群组 [groupId = 27a35ff2f9be4cc9a8d3db1ad3322804] 通过操作指令`群发模式`[command = 2104] 发送一条群组消息
-![websocket功能测试](assert/design/websocket功能测试.png)
+![websocket功能测试](assert/design/photo/websocket功能测试.png)
## 架构设计
### 私有协议
@@ -211,7 +211,7 @@ IM 的私有协议确立信息如下:
### 消息投递过程
-![一条消息的流转](assert/design/消息流转.png)
+![一条消息的流转](assert/design/photo/消息流转.png)
流程如下:
1. 客户端 userA 发送一条消息到服务器, 消息通过私有协议转化为二进制序列化, 通过 TCP 三次握手保证消息在传输层的稳定性(上下行 ACK 保证消息在应用层的稳定性 )
@@ -228,7 +228,7 @@ IM 的私有协议确立信息如下:
当然, 也可采用 TCP、UDP 连接甚至是 HTTP 短连接也行,只不过这样会需要更多的设计,需要考虑用户弱网行为,后续设计中我会添加
### 路由层
-![分布式路由层](assert/design/分布式路由层.png)
+![分布式路由层](assert/design/photo/分布式路由层.png)
由于使用了分布式, 用户的信息会因为负载均衡分布在不同的服务器上,怎么保证多 Channel 的跨节点通讯就显得额外的重要。
@@ -254,7 +254,7 @@ IM 的私有协议确立信息如下:
### 读写扩散模型
#### 写扩散
-![写扩散](assert/design/写扩散.png)
+![写扩散](assert/design/photo/写扩散.png)
+ 在架构中, 单聊会话消息采用写扩散
写扩散优缺:
@@ -272,7 +272,7 @@ IM 的私有协议确立信息如下:
+ 先写扩散后读,实时性差。
#### 读扩散
-![读扩散](assert/design/读扩散.png)
+![读扩散](assert/design/photo/读扩散.png)
+ 在架构中, 群聊会话消息采用读扩散
读扩散优缺:
@@ -292,21 +292,21 @@ IM 的私有协议确立信息如下:
#### 多端消息同步的弊端:
由于 WhaleShark 实现了用户多端同步,因此需要保证一条消息既同步给发送方的其他端,又得保证消息能发送给目标对象的所有端。一条消息的处理流程如下:
-![多端消息同步的弊端](assert/design/多端消息同步的弊端.png)
+![多端消息同步的弊端](assert/design/photo/多端消息同步的弊端.png)
如架构图所演示,一条消息如图所示就裂变成三条消息了,如果说端的类型更多 (设计上是有六种端: Windows、Mac、Web、Android、IOS、WebApi) 但实际上我们基本是通过 WebApi 来接收消息, 再同步给其他端,也就需要裂变出 `5 * 5 - 1 = 24` 条消息
一口气发送如此多条消息对于服务器来说,压力是巨大的,因此我们需要重新设计一些策略来实现消息同步
#### 多端消息同步改进:
-![多端消息同步改进](assert/design/多端消息同步改进.png)
+![多端消息同步改进](assert/design/photo/多端消息同步改进.png)
1. 发送方 userA 发送消息给服务端
2. 服务端接收发送方的消息之后向发送方回应消息接收确认 ACK 数据包表示服务端已经成功接收消息
3. 先将消息同步给发送方其他端(在线端使用 TCP 通知投递,离线端存储最新的 1000 条数据到离线消息队列里)
4. 发送消息给接收方所有端
#### 群聊消息同步流程:
-![群聊消息同步流程](assert/design/群聊消息同步流程.png)
+![群聊消息同步流程](assert/design/photo/群聊消息同步流程.png)
1. 发送方 userA 发送消息给服务端
2. 服务端接收发送方的消息之后向发送方回应消息接收确认 ACK 数据包表示服务端已经成功接收消息
3. 先将消息同步给发送方其他端(在线端使用 TCP 通知投递,离线端存储最新的 1000 条数据到离线消息队列里)
@@ -317,7 +317,7 @@ IM 的私有协议确立信息如下:
### 消息可靠传达模型
我们难以保证消息全都可靠传达,不会产生丢失现象,在 IM 系统中也不允许丢失一条消息。如下图:
-![有了TCP为什么还要保证可靠性传达](assert/design/有了TCP为什么还要保证可靠性传达.png)
+![有了TCP为什么还要保证可靠性传达](assert/design/photo/有了TCP为什么还要保证可靠性传达.png)
+ 在传输层,TCP的三次握手保证了双方通讯的可靠性,稳定性。简而言之,用户发送的消息,
在忽视应用层的情况下,无论如何都会从自身主机的 “发送缓冲区” 抵达对方主机的 “接收缓冲区”
@@ -327,7 +327,7 @@ IM 的私有协议确立信息如下:
> 如果只是单台机器进行双向通信,则不会经历传输层拆包装包的过程,而是直接将数据包通过内核拷贝到另一个进程进行通讯
在设计上,我们采用应用层两次握手(上下行 ACK)来保证消息在应用层的可靠传达
-![上下行ACK](assert/design/上下行ACK.png)
+![上下行ACK](assert/design/photo/上下行ACK.png)
+ 上行 ACK:服务端发送给消息发送方的接收确认 ACK
+ 下行 ACK:目标用户发送给消息发送方的接收确认 ACK
@@ -345,10 +345,10 @@ ps: 上行 ACK 也同理, 服务端的消息发送实际抵达 MQ 时有一个
RecvID, ServerID, ClientID, SendTime 做冗余避免查库提升性能
#### 在线用户消息接收
-![在线用户消息确认](assert/design/在线用户消息确认.png)
+![在线用户消息确认](assert/design/photo/在线用户消息确认.png)
#### 离线用户消息接收
-![离线用户消息确认](assert/design/离线用户消息确认.png)
+![离线用户消息确认](assert/design/photo/离线用户消息确认.png)
#### ACK 丢失现象
下面分别探讨上下行 ACK 丢失现象的处理流程
@@ -369,13 +369,13 @@ ACK 丢失现象解决策略: 由于我们 ack 中含有 msgId, 可以在客户
或者是消息到达时做一个缓存,缓存时间尽量短,缓存时间内的消息重试直接让接收方接收消息,不进行二次持久化。
-![消息幂等性保证](assert/design/消息幂等性保证.png)
+![消息幂等性保证](assert/design/photo/消息幂等性保证.png)
为了避免客户端无限制重发的现象,我们可以对缓存做一个过期时间,只有在过期时间之前的缓存才能做幂等;
当超过缓存时间时, 服务端忽略重投的消息, 直到客户端计时器超时并且已经超过了最大重试次数,
才让客户端重新生成消息唯一id:messageId, 也就是重新做一个消息体
-![禁止客户端无限制重发](assert/design/禁止客户端无限制重发.png)
+![禁止客户端无限制重发](assert/design/photo/禁止客户端无限制重发.png)
### 消息有序性保证
为了提高消息在服务端的处理流程(MQ消费,落库存储,ACK确认),我们在程序实现中采用了线程池技术来提高消息处理时长
@@ -412,7 +412,7 @@ ACK 丢失现象解决策略: 由于我们 ack 中含有 msgId, 可以在客户
## 前后端对接
IM 服务采用 SDK 方式集成到前端代码。一个大致的流程演示如下:
-![SDK执行流程](assert/design/SDK执行流程.png)
+![SDK执行流程](assert/design/photo/SDK执行流程.png)
对此我已经大致实现了后端的一个 im-app-server 与前端的 SDK 进行对接,欢迎前端同学与我一起来完善 WhaleShark
## 联系
@@ -420,4 +420,4 @@ IM 服务采用 SDK 方式集成到前端代码。一个大致的流程演示如
欢迎与我联系交流,我拉你进交流群,微信二维码为(注明来意喔~):
-![与我联系.png](assert/design/与我联系.png)
+![与我联系.png](assert/design/photo/与我联系.png)
diff --git a/assert/design/design_pattern/adapter.md b/assert/design/design_pattern/adapter.md
new file mode 100644
index 0000000..5b5a150
--- /dev/null
+++ b/assert/design/design_pattern/adapter.md
@@ -0,0 +1,63 @@
+# 适配器模式(Adapter)说明
+
+适配器:意在将一个类的接口适配成用户所需的接口,它能帮助不兼容的接口变得兼容,宏观做法是将用户自定义的接口包裹在想要适配的接口里,就好比苹果的数据线..
+
+适配器模式有三个角色
+
++ Target: 目标角色, 在 im-register-login-demo#adapter#Login3rdTarget, 是暴露给用户的接口, 根据设计模式六大原则之迪米特法则,一个类最好只暴露实现方法,而不暴露具体细节
++ Adaptee: 被适配角色, 在 im-register-login-demo#adapter#UserService,适配器将继承 UserService 类以达到扩展新功能而不改动原有类的需求,这是设计模式六大原则的开闭原则,即对修改关闭,对扩展开放
++ Adapter: 适配器角色, 在 im-register-login-demo#adapter#Login3rdAdapter,他将扩展出第三方登陆的核心逻辑方法,并且还具有 UserService 已实现的查询数据库是否有账号和注册逻辑
+
+适配器根据适配的对象不同,可分为对象适配器和类适配器
++ 前者适配器关联一个包裹它的类实例
++ 后者适配器继承被适配的类对象(一般采用这种方式)
+
+对象适配器的一种实现方式:
+
+```java
+@Component
+public class Login3rdAdapter {
+ @Resource
+ private UserService userService;
+ // ...
+}
+```
+
+类适配器的一种实现方式:
+
+```java
+@Component
+public class Login3rdAdapter extends UserService {
+
+}
+```
+
+Target 是接口,自然需要子类真正实现,在这里子类自然是 Adapter
+不难写出这样的代码
+
+```java
+public class Login3rdAdapter extends UserService implements Login3rdTarget {
+
+ public Login3rdAdapter(UserRepository userRepository) {
+ super(userRepository);
+ }
+
+ @Override
+ public String loginByGithub(String code, String state) {
+ return null;
+ }
+
+ @Override
+ public String loginByWechat() {
+ return null;
+ }
+
+ @Override
+ public String loginByQQ() {
+ return null;
+ }
+}
+```
++ 继承 UserService,以实现不侵入原有方法前提下进行第三方登录的扩展
+
+
diff --git a/assert/design/design_pattern/login3rd.md b/assert/design/design_pattern/login3rd.md
new file mode 100644
index 0000000..15c8bc1
--- /dev/null
+++ b/assert/design/design_pattern/login3rd.md
@@ -0,0 +1,61 @@
+# 第三方登录的实现以及使用说明
+
+## 使用说明
+
++ im-design-demo/im-register-login-demo/login-design-adapter-demo
++ im-design-demo/im-register-login-demo/login-design-bridge-demo
+
+这两个子模块都对接了第三方登录的功能
+
+体验步骤:
+1. 运行项目
+2. 点击命令行里出现的蓝链 进行权限校验
+
+## Github Oauth 第三方登录实现
+参考这篇文章:
+
+[官方文档](https://docs.github.com/zh/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)
+
+[github第三方登录超详细流程及分析(小白笔记)](https://blog.csdn.net/qq_43516238/article/details/105884926)
+
+[GitHub OAuth 第三方登录示例教程 - 阮一峰的网络日志](https://ruanyifeng.com/blog/2019/04/github-oauth.html)
+
+```yml
+client_id: cf00a9382ce8110c2a70
+client_secret: fd348b2050f64c7a99c07294b390a5adfaa21e8c
+redirect_uri: http://localhost:21001/github
+```
+
+yml 配置
+```yml
+github:
+ state: GITHUB
+ user_prefix: ${github.state}@
+
+ # ========= 自己申请 client_id\secret 用完之后记得删除 =========
+ client_id: cf00a9382ce8110c2a70
+ client_secret: fd348b2050f64c7a99c07294b390a5adfaa21e8c
+ # ==========================================================
+
+ callback: http://localhost:21001/github # github 回调 callback 会携带 code 参数
+ token_url: https://github.com/login/oauth/access_token?client_id=${github.client_id}&client_secret=${github.client_secret}&redirect_uri=${github.callback}&code= # 拼接 code
+ user_url: https://api.github.com/user # 使用访问令牌访问 API
+```
+
+![img.png](../../../im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/img.png)
+
+权限访问:
+
+https://github.com/login/oauth/authorize?client_id=cf00a9382ce8110c2a70&redirect_uri=http://localhost:21001/github&state=GITHUB
+
+可能会出现超时的情况
+
+![timeout.png](../../../im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/timeout.png)
+
+成功
+
+![success.png](../../../im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/success.png)
+
+数据库存在该数据
+
+![数据库存有github账号.png](../../../im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/数据库存有github账号.png)
\ No newline at end of file
diff --git a/assert/design/IM-WhaleShark.png b/assert/design/photo/IM-WhaleShark.png
similarity index 100%
rename from assert/design/IM-WhaleShark.png
rename to assert/design/photo/IM-WhaleShark.png
diff --git "a/assert/design/SDK\346\211\247\350\241\214\346\265\201\347\250\213.png" "b/assert/design/photo/SDK\346\211\247\350\241\214\346\265\201\347\250\213.png"
similarity index 100%
rename from "assert/design/SDK\346\211\247\350\241\214\346\265\201\347\250\213.png"
rename to "assert/design/photo/SDK\346\211\247\350\241\214\346\265\201\347\250\213.png"
diff --git "a/assert/design/websocket\345\212\237\350\203\275\346\265\213\350\257\225.png" "b/assert/design/photo/websocket\345\212\237\350\203\275\346\265\213\350\257\225.png"
similarity index 100%
rename from "assert/design/websocket\345\212\237\350\203\275\346\265\213\350\257\225.png"
rename to "assert/design/photo/websocket\345\212\237\350\203\275\346\265\213\350\257\225.png"
diff --git "a/assert/design/websocket\347\252\227\345\217\243\345\212\237\350\203\275\350\256\262\350\247\243.png" "b/assert/design/photo/websocket\347\252\227\345\217\243\345\212\237\350\203\275\350\256\262\350\247\243.png"
similarity index 100%
rename from "assert/design/websocket\347\252\227\345\217\243\345\212\237\350\203\275\350\256\262\350\247\243.png"
rename to "assert/design/photo/websocket\347\252\227\345\217\243\345\212\237\350\203\275\350\256\262\350\247\243.png"
diff --git "a/assert/design/\344\270\212\344\270\213\350\241\214ACK.png" "b/assert/design/photo/\344\270\212\344\270\213\350\241\214ACK.png"
similarity index 100%
rename from "assert/design/\344\270\212\344\270\213\350\241\214ACK.png"
rename to "assert/design/photo/\344\270\212\344\270\213\350\241\214ACK.png"
diff --git "a/assert/design/\344\270\216\346\210\221\350\201\224\347\263\273.png" "b/assert/design/photo/\344\270\216\346\210\221\350\201\224\347\263\273.png"
similarity index 100%
rename from "assert/design/\344\270\216\346\210\221\350\201\224\347\263\273.png"
rename to "assert/design/photo/\344\270\216\346\210\221\350\201\224\347\263\273.png"
diff --git "a/assert/design/\345\206\231\346\211\251\346\225\243.png" "b/assert/design/photo/\345\206\231\346\211\251\346\225\243.png"
similarity index 100%
rename from "assert/design/\345\206\231\346\211\251\346\225\243.png"
rename to "assert/design/photo/\345\206\231\346\211\251\346\225\243.png"
diff --git "a/assert/design/\345\210\206\345\270\203\345\274\217\350\267\257\347\224\261\345\261\202.png" "b/assert/design/photo/\345\210\206\345\270\203\345\274\217\350\267\257\347\224\261\345\261\202.png"
similarity index 100%
rename from "assert/design/\345\210\206\345\270\203\345\274\217\350\267\257\347\224\261\345\261\202.png"
rename to "assert/design/photo/\345\210\206\345\270\203\345\274\217\350\267\257\347\224\261\345\261\202.png"
diff --git "a/assert/design/\345\234\250\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png" "b/assert/design/photo/\345\234\250\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png"
similarity index 100%
rename from "assert/design/\345\234\250\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png"
rename to "assert/design/photo/\345\234\250\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png"
diff --git "a/assert/design/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\346\224\271\350\277\233.png" "b/assert/design/photo/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\346\224\271\350\277\233.png"
similarity index 100%
rename from "assert/design/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\346\224\271\350\277\233.png"
rename to "assert/design/photo/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\346\224\271\350\277\233.png"
diff --git "a/assert/design/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\347\232\204\345\274\212\347\253\257.png" "b/assert/design/photo/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\347\232\204\345\274\212\347\253\257.png"
similarity index 100%
rename from "assert/design/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\347\232\204\345\274\212\347\253\257.png"
rename to "assert/design/photo/\345\244\232\347\253\257\346\266\210\346\201\257\345\220\214\346\255\245\347\232\204\345\274\212\347\253\257.png"
diff --git "a/assert/design/\346\225\260\346\215\256\345\272\223\347\273\223\346\236\204\346\233\264\345\217\230.png" "b/assert/design/photo/\346\225\260\346\215\256\345\272\223\347\273\223\346\236\204\346\233\264\345\217\230.png"
similarity index 100%
rename from "assert/design/\346\225\260\346\215\256\345\272\223\347\273\223\346\236\204\346\233\264\345\217\230.png"
rename to "assert/design/photo/\346\225\260\346\215\256\345\272\223\347\273\223\346\236\204\346\233\264\345\217\230.png"
diff --git "a/assert/design/\346\234\211\344\272\206TCP\344\270\272\344\273\200\344\271\210\350\277\230\350\246\201\344\277\235\350\257\201\345\217\257\351\235\240\346\200\247\344\274\240\350\276\276.png" "b/assert/design/photo/\346\234\211\344\272\206TCP\344\270\272\344\273\200\344\271\210\350\277\230\350\246\201\344\277\235\350\257\201\345\217\257\351\235\240\346\200\247\344\274\240\350\276\276.png"
similarity index 100%
rename from "assert/design/\346\234\211\344\272\206TCP\344\270\272\344\273\200\344\271\210\350\277\230\350\246\201\344\277\235\350\257\201\345\217\257\351\235\240\346\200\247\344\274\240\350\276\276.png"
rename to "assert/design/photo/\346\234\211\344\272\206TCP\344\270\272\344\273\200\344\271\210\350\277\230\350\246\201\344\277\235\350\257\201\345\217\257\351\235\240\346\200\247\344\274\240\350\276\276.png"
diff --git "a/assert/design/\346\266\210\346\201\257\345\271\202\347\255\211\346\200\247\344\277\235\350\257\201.png" "b/assert/design/photo/\346\266\210\346\201\257\345\271\202\347\255\211\346\200\247\344\277\235\350\257\201.png"
similarity index 100%
rename from "assert/design/\346\266\210\346\201\257\345\271\202\347\255\211\346\200\247\344\277\235\350\257\201.png"
rename to "assert/design/photo/\346\266\210\346\201\257\345\271\202\347\255\211\346\200\247\344\277\235\350\257\201.png"
diff --git "a/assert/design/\346\266\210\346\201\257\346\213\211\345\217\226\345\211\215\345\220\216\347\253\257\350\201\224\350\260\203\350\256\276\350\256\241.png" "b/assert/design/photo/\346\266\210\346\201\257\346\213\211\345\217\226\345\211\215\345\220\216\347\253\257\350\201\224\350\260\203\350\256\276\350\256\241.png"
similarity index 100%
rename from "assert/design/\346\266\210\346\201\257\346\213\211\345\217\226\345\211\215\345\220\216\347\253\257\350\201\224\350\260\203\350\256\276\350\256\241.png"
rename to "assert/design/photo/\346\266\210\346\201\257\346\213\211\345\217\226\345\211\215\345\220\216\347\253\257\350\201\224\350\260\203\350\256\276\350\256\241.png"
diff --git "a/assert/design/\346\266\210\346\201\257\346\213\211\345\217\226\346\236\266\346\236\204\350\256\276\350\256\241.png" "b/assert/design/photo/\346\266\210\346\201\257\346\213\211\345\217\226\346\236\266\346\236\204\350\256\276\350\256\241.png"
similarity index 100%
rename from "assert/design/\346\266\210\346\201\257\346\213\211\345\217\226\346\236\266\346\236\204\350\256\276\350\256\241.png"
rename to "assert/design/photo/\346\266\210\346\201\257\346\213\211\345\217\226\346\236\266\346\236\204\350\256\276\350\256\241.png"
diff --git "a/assert/design/\346\266\210\346\201\257\346\265\201\350\275\254.png" "b/assert/design/photo/\346\266\210\346\201\257\346\265\201\350\275\254.png"
similarity index 100%
rename from "assert/design/\346\266\210\346\201\257\346\265\201\350\275\254.png"
rename to "assert/design/photo/\346\266\210\346\201\257\346\265\201\350\275\254.png"
diff --git "a/assert/design/\347\246\201\346\255\242\345\256\242\346\210\267\347\253\257\346\227\240\351\231\220\345\210\266\351\207\215\345\217\221.png" "b/assert/design/photo/\347\246\201\346\255\242\345\256\242\346\210\267\347\253\257\346\227\240\351\231\220\345\210\266\351\207\215\345\217\221.png"
similarity index 100%
rename from "assert/design/\347\246\201\346\255\242\345\256\242\346\210\267\347\253\257\346\227\240\351\231\220\345\210\266\351\207\215\345\217\221.png"
rename to "assert/design/photo/\347\246\201\346\255\242\345\256\242\346\210\267\347\253\257\346\227\240\351\231\220\345\210\266\351\207\215\345\217\221.png"
diff --git "a/assert/design/\347\246\273\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png" "b/assert/design/photo/\347\246\273\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png"
similarity index 100%
rename from "assert/design/\347\246\273\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png"
rename to "assert/design/photo/\347\246\273\347\272\277\347\224\250\346\210\267\346\266\210\346\201\257\347\241\256\350\256\244.png"
diff --git "a/assert/design/\347\276\244\350\201\212\346\266\210\346\201\257\345\220\214\346\255\245\346\265\201\347\250\213.png" "b/assert/design/photo/\347\276\244\350\201\212\346\266\210\346\201\257\345\220\214\346\255\245\346\265\201\347\250\213.png"
similarity index 100%
rename from "assert/design/\347\276\244\350\201\212\346\266\210\346\201\257\345\220\214\346\255\245\346\265\201\347\250\213.png"
rename to "assert/design/photo/\347\276\244\350\201\212\346\266\210\346\201\257\345\220\214\346\255\245\346\265\201\347\250\213.png"
diff --git "a/assert/design/\350\257\273\346\211\251\346\225\243.png" "b/assert/design/photo/\350\257\273\346\211\251\346\225\243.png"
similarity index 100%
rename from "assert/design/\350\257\273\346\211\251\346\225\243.png"
rename to "assert/design/photo/\350\257\273\346\211\251\346\225\243.png"
diff --git a/assert/sql/register_login_demo.sql b/assert/sql/register_login_demo.sql
new file mode 100644
index 0000000..1052a08
--- /dev/null
+++ b/assert/sql/register_login_demo.sql
@@ -0,0 +1,13 @@
+CREATE DATABASE IF NOT EXISTS im_register_login_demo;
+USE im_register_login_demo;
+
+CREATE TABLE user (
+ id BIGINT AUTO_INCREMENT
+ PRIMARY KEY,
+ username VARCHAR(64),
+ password VARCHAR(64),
+ create_time DATE,
+ user_email VARCHAR(64)
+);
+
+INSERT INTO user (id, username, password, create_time, user_email) VALUES ('10001', 'admin', 'admin', now(), 'admin@edu.com')
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index b8c3b4b..d6625ea 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -24,7 +24,7 @@ services:
timeout: 5s
retries: 3
ports:
- - "3306:3306"
+ - "13306:3306"
networks:
- im-network
diff --git a/docker/mysql/conf/my.cnf b/docker/mysql/conf/my.cnf
index 7b363b6..67e7a42 100644
--- a/docker/mysql/conf/my.cnf
+++ b/docker/mysql/conf/my.cnf
@@ -1,6 +1,6 @@
[mysqld]
-# 设置3306端口
-port=3306
+# 设置13306端口
+port=13306
# 设置mysql的安装目录
basedir=/usr/local/mysql
@@ -47,5 +47,5 @@ default-character-set=utf8mb4
[client]
# 设置mysql客户端连接服务端时默认使用的端口
-port=3306
+port=13306
default-character-set=utf8mb4
\ No newline at end of file
diff --git a/docker/mysql/db/register_login_demo.sql b/docker/mysql/db/register_login_demo.sql
new file mode 100644
index 0000000..1052a08
--- /dev/null
+++ b/docker/mysql/db/register_login_demo.sql
@@ -0,0 +1,13 @@
+CREATE DATABASE IF NOT EXISTS im_register_login_demo;
+USE im_register_login_demo;
+
+CREATE TABLE user (
+ id BIGINT AUTO_INCREMENT
+ PRIMARY KEY,
+ username VARCHAR(64),
+ password VARCHAR(64),
+ create_time DATE,
+ user_email VARCHAR(64)
+);
+
+INSERT INTO user (id, username, password, create_time, user_email) VALUES ('10001', 'admin', 'admin', now(), 'admin@edu.com')
\ No newline at end of file
diff --git a/im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/adapter.md b/im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/adapter.md
new file mode 100644
index 0000000..0faf495
--- /dev/null
+++ b/im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/adapter.md
@@ -0,0 +1,63 @@
+# 适配器模式(Adapter)说明
+
+适配器:意在将一个类的接口适配成用户所需的接口,它能帮助不兼容的接口变得兼容,宏观做法是将用户自定义的接口包裹在想要适配的接口里,就好比苹果的数据线..
+
+适配器模式有三个角色
+
++ Target: 目标角色, 在 im-register-login-demo#adapter#Login3rdTarget, 是暴露给用户的接口, 根据设计模式六大原则之迪米特法则,一个类最好只暴露实现方法,而不暴露具体细节
++ Adaptee: 被适配角色, 在 im-register-login-demo#adapter#UserService,适配器将继承 UserService 类以达到扩展新功能而不改动原有类的需求,这是设计模式六大原则的开闭原则,即对修改关闭,对扩展开放
++ Adapter: 适配器角色, 在 im-register-login-demo#adapter#Login3rdAdapter,他将扩展出第三方登陆的核心逻辑方法,并且还具有 UserService 已实现的查询数据库是否有账号和注册逻辑
+
+适配器根据适配的对象不同,可分为对象适配器和类适配器
++ 前者适配器关联一个包裹它的类实例
++ 后者适配器继承被适配的类对象(一般采用这种方式)
+
+对象适配器的一种实现方式:
+
+```java
+@Component
+public class Login3rdAdapter {
+ @Resource
+ private UserService userService;
+ // ...
+}
+```
+
+类适配器的一种实现方式:
+
+```java
+@Component
+public class Login3rdAdapter extends UserService {
+
+}
+```
+
+Target 是接口,自然需要子类真正实现,在这里子类自然是 Adapter
+不难写出这样的代码
+
+```java
+public class Login3rdAdapter extends UserService implements Login3rdTarget {
+
+ public Login3rdAdapter(UserRepository userRepository) {
+ super(userRepository);
+ }
+
+ @Override
+ public String loginByGithub(String code, String state) {
+ return null;
+ }
+
+ @Override
+ public String loginByWechat() {
+ return null;
+ }
+
+ @Override
+ public String loginByQQ() {
+ return null;
+ }
+}
+```
++ 继承 UserService,以实现不侵入原有方法前提下进行第三方登录的扩展
+
+适配器的好处在于不修改原有逻辑就能实现扩展与替换,但如果需要扩展的子类过多,例如 demo 里所展示的第三方账号越来越多,手机短信验证码、CSDN账号、Gitee、twitter 等等,可能会导致适配器适配的种类越来越多
diff --git a/im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/img.png b/im-design-demo/im-register-login-demo/login-design-adapter-demo/assert/img.png
new file mode 100644
index 0000000000000000000000000000000000000000..e5071879509c98af988a13fcdab43157689598ed
GIT binary patch
literal 62881
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