实体关系图(ER Diagram)描述

mermaid

复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
erDiagram
USER ||--o{ FRIEND : "1:n"
USER ||--o{ FRIEND_APPLY : "1:n"
USER_ID ||--|| USER : "1:1"

USER {
INT uid PK
VARCHAR(255) name
VARCHAR(255) email
VARCHAR(255) pwd
VARCHAR(255) nick
VARCHAR(255) desc
INT sex
VARCHAR(255) icon
}

FRIEND {
INT id PK
INT self_id
INT friend_id
VARCHAR(255) back
}

FRIEND_APPLY {
BIGINT id PK
INT from_uid
INT to_uid
SMALLINT status
}

USER_ID {
INT id PK
}

表结构详细说明

1. 用户表(user)

用户表是存储用户基本信息的表,作为系统用户身份认证的核心数据源,支持双登录体系(用户名/邮箱+密码)。包含用户ID、用户名、邮箱、密码、昵称、描述、性别和头像等字段。需要注意的是,uid字段是业务用户ID,由user_id表生成,而id是自增主键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| 字段名      | 类型          | 约束               | 描述                     |
|------------|--------------|--------------------|--------------------------|
| id | INT | PK, AUTO_INCREMENT | 自增主键 |
| uid | INT | UNIQUE | 业务用户ID(来自user_id表)|
| name | VARCHAR(255) | NOT NULL, UNIQUE | 用户名 |
| email | VARCHAR(255) | NOT NULL, UNIQUE | 邮箱地址 |
| pwd | VARCHAR(255) | NOT NULL | 密码(建议加密存储) |
| nick | VARCHAR(255) | NOT NULL | 昵称 |
| desc | VARCHAR(255) | NOT NULL | 个人描述 |
| sex | INT | NOT NULL | 性别(0-未知,1-男,2-女)|
| icon | VARCHAR(255) | NOT NULL | 头像路径 |

索引:
- 唯一索引uid:确保用户ID唯一性
- 唯一索引email:防止重复注册
- 普通索引name:支持用户名搜索

2. 好友关系表(friend)

好友表主要用于存储用户之间的好友关系,维护用户好友关系的双向映射,实现好友备注功能。包含自增主键id,self_id(用户自身ID),friend_id(好友ID),以及back(备注)。好友关系是双向存储的,比如用户A和用户B成为好友,会有两条记录,分别对应A的好友列表和B的好友列表。联合唯一索引self_friend确保不会重复添加相同的好友关系。支持快速查询某用户的所有好友(WHERE self_id=xxx)。

1
2
3
4
5
6
7
8
9
| 字段名     | 类型         | 约束                    | 描述                      |
|------------|--------------|-------------------------|---------------------------|
| id | INT UNSIGNED | PK, AUTO_INCREMENT | 自增主键 |
| self_id | INT | NOT NULL | 用户ID(关联user.uid) |
| friend_id | INT | NOT NULL | 好友ID(关联user.uid) |
| back | VARCHAR(255) | DEFAULT '' | 好友备注 |

唯一约束:
- 联合唯一索引self_friend (self_id, friend_id):防止重复好友关系

好友关系建立时序图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
flowchart TD
A[用户A发起申请] --> B{系统验证}
B -->|验证通过| C[写入friend_apply表]
C --> D[通知用户B]
D --> E{用户B操作}
E -->|同意| F[更新申请状态为1]
F --> G{建立双向好友}
G --> H[插入用户A的好友记录]
H --> I[插入用户B的好友记录]
I --> J[返回成功提示]
E -->|拒绝| K[更新状态为2]
K --> L[返回拒绝通知]
E -->|超时未处理| M[更新状态为3]
M --> N[清理过期申请]

style A fill:#b3e2cd,stroke:#333
style B fill:#fdcdac,stroke:#333
style C fill:#cbd5e8,stroke:#333
style D fill:#cbd5e8,stroke:#333
style E fill:#fdcdac,stroke:#333
style F fill:#cbd5e8,stroke:#333
style G fill:#fdcdac,stroke:#333
style H fill:#cbd5e8,stroke:#333
style I fill:#cbd5e8,stroke:#333
style J fill:#b3e2cd,stroke:#333
style K fill:#f4cae4,stroke:#333
style L fill:#f4cae4,stroke:#333
style M fill:#e6f5c9,stroke:#333
style N fill:#e6f5c9,stroke:#333

3. 好友申请表(friend_apply)

好友申请表管理好友申请,记录申请者(from_uid)、被申请者(to_uid)以及申请状态(status)。status的默认值是0,可能表示待处理,1表示已同意。需要说明申请流程和状态变化,以及唯一索引from_to_uid的作用,防止重复申请。

1
2
3
4
5
6
7
8
9
graph LR
A[用户A申请] --> B{用户B处理}
B -->|同意| C[status=1]
B -->|拒绝| D[status=2]
B -->|超时| E[status=3]
C --> F[插入双向好友记录]

F --> G["INSERT INTO friend VALUES(81,1002,1019,'zack')"]
F --> H["INSERT INTO friend VALUES(82,1019,1002,'')"]
1
2
3
4
5
6

| 字段 | 作用说明 |
|-----------|---------------------------------------------------------------------|
| from_uid | 申请发起方用户ID |
| to_uid | 申请接收方用户ID |
| status | 申请状态(0=待处理,1=已同意,可扩展2=已拒绝/3=已过期) |

好友申请状态流程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
stateDiagram-v2
[*] --> Pending
Pending --> Accepted: 接受申请
Pending --> Rejected: 拒绝申请
Pending --> Expired: 超时(3天)
Accepted --> Relationship: 建立好友关系
Rejected --> [*]: 流程结束
Expired --> [*]: 流程结束

state Relationship {
[*] --> InsertFriendA
InsertFriendA --> InsertFriendB
InsertFriendB --> [*]
}
1
2
3
4
5
6
7
8
9
| 字段名     | 类型         | 约束                    | 描述                      |
|------------|--------------|-------------------------|---------------------------|
| id | BIGINT | PK, AUTO_INCREMENT | 自增主键 |
| from_uid | INT | NOT NULL | 申请人ID(关联user.uid) |
| to_uid | INT | NOT NULL | 被申请人ID(关联user.uid) |
| status | SMALLINT | DEFAULT 0 | 状态(0-待处理,1-已同意) |

唯一约束:
- 联合唯一索引from_to_uid:防止重复申请

4. 用户ID生成表(user_id)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sequenceDiagram
participant Client
participant DB
Client->>DB: 调用reg_user存储过程
Note right of DB: 开始事务
DB->>DB: 检查用户名/邮箱唯一性
alt 存在重复
DB-->>Client: 返回0
else 可注册
DB->>user_id: UPDATE id=id+1
DB->>user_id: SELECT id
DB->>user: 插入新用户记录
DB-->>Client: 返回新uid
end
Note right of DB: 提交事务
1
2
3
4
5
6
7
8
9
| 字段名     | 类型         | 约束       | 描述                      |
|------------|--------------|------------|---------------------------|
| id | INT | PK | 当前可分配的最大用户ID |

工作机制:
通过存储过程reg_user实现ID自增:
```sql
UPDATE user_id SET id = id + 1; -- 原子性递增
SELECT id INTO @new_id FROM user_id;

5. 存储过程(reg_user)

markdown

复制

1
2
3
4
5
6
7
8
功能说明:
- 事务处理用户注册流程
- 检查用户名和邮箱唯一性
- 生成递增的用户ID
- 返回结果代码:
* -1:系统错误
* 0:用户名或邮箱已存在
* 正整数:注册成功的用户ID

关键设计特点

  1. 双ID体系

mermaid

复制

1
2
3
graph TD
A[user_id表生成uid] --> B[user表业务ID]
B --> C[好友关系关联]
  1. 好友关系设计

sql

复制

1
2
3
-- 双向好友关系存储示例
INSERT INTO friend VALUES (55, 1055, 1054, 'sqy'); -- 用户1055的好友列表
INSERT INTO friend VALUES (56, 1054, 1055, ''); -- 用户1054的好友列表
  1. 状态管理

mermaid

复制

1
2
3
4
5
stateDiagram
[*] --> Pending: 发起申请
Pending --> Accepted: 同意申请
Pending --> Rejected: 拒绝申请
Accepted --> [*]: 建立好友关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
sequenceDiagram
participant User as 客户端
participant Gate as GateServer
participant Verify as VerifyServer
participant Redis

User->>+Gate: POST /register (邮箱+密码)
Gate->>Verify: gRPC GenerateCaptcha(email)
Verify->>Redis: SETEX captcha:{email} 300 628491
Verify->>+SMTP: 异步发送验证码邮件
SMTP-->>-Verify: 250 OK
Verify-->>Gate: 200(验证码已发送)
Gate-->>User: 显示输入验证码界面

User->>+Gate: POST /confirm (email+验证码)
Gate->>Redis: GET captcha:{email}
Redis-->>Gate: 628491
alt 验证码匹配
Gate->>MySQL: INSERT users (email,password_hash)
MySQL-->>Gate: user_id
Gate-->>User: 302跳转至主界面
else 验证码错误
Gate-->>User: 401 Unauthorized
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sequenceDiagram
participant A as 用户A
participant B as 用户B
participant Gate
participant Relation as RelationServer
participant Push as PushServer

A->>+Gate: POST /friends/request (B_uid)
Gate->>Relation: gRPC CreateFriendRequest(A_uid,B_uid)
Relation->>MySQL: INSERT requests (from,to,status=PENDING)
Relation-->>Gate: 201 Created
Gate->>Push: gRPC NotifyUser(B_uid)
alt B在线
Push->>B: WebSocket推送请求通知
B->>+Gate: PUT /friends/response (req_id,ACCEPT)
Gate->>Relation: gRPC UpdateRequestStatus(ACCEPT)
Relation->>MySQL: 事务更新状态+双向插入好友关系
Relation->>Push: 通知双方更新列表
Push->>A: 好友添加成功通知
Push->>B: 刷新好友列表
else B离线
Relation->>Redis: LPUSH offline_msg:B {type:FRIEND_REQ}
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
sequenceDiagram
participant ClientA as 客户端A
participant ClientB as 客户端B
participant Gateway as 网关服务
participant MySQL as 数据库
participant PushService as 推送服务

Note over ClientA: 发起好友申请(HTTP)
ClientA->>Gateway: POST /friends/apply {to_uid:10002}
Gateway->>MySQL: 查询users表 WHERE uid=10002
alt 目标用户存在
MySQL-->>Gateway: 返回用户记录
Gateway->>MySQL: INSERT INTO friend_applies (from_uid,to_uid,status)
MySQL-->>Gateway: 插入成功
Gateway->>PushService: 触发推送标记
Gateway-->>ClientA: 200 OK
else 用户不存在
MySQL-->>Gateway: 无记录
Gateway-->>ClientA: 404错误
end

Note over ClientB: 定期轮询申请(HTTP)
loop 每5秒轮询
ClientB->>Gateway: GET /friends/pending
Gateway->>MySQL: SELECT * FROM friend_applies WHERE to_uid=10002
MySQL-->>Gateway: 返回申请列表
alt 存在新申请
Gateway-->>ClientB: 返回申请数据
else 无新申请
Gateway-->>ClientB: 204 No Content
end
end

Note over ClientB: 响应申请(HTTP)
ClientB->>Gateway: PUT /friends/response {apply_id:123,status:1}
Gateway->>MySQL: UPDATE friend_applies SET status=1
Gateway->>MySQL: BEGIN TRANSACTION
Gateway->>MySQL: INSERT INTO friends (self_id,friend_id) VALUES (10001,10002)
Gateway->>MySQL: INSERT INTO friends (self_id,friend_id) VALUES (10002,10001)
Gateway->>MySQL: COMMIT
Gateway-->>ClientB: 200 OK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
sequenceDiagram
participant Sender as 发送方
participant Receiver as 接收方
participant Gateway as 网关服务
participant MsgServer as 消息服务
participant MySQL as 数据库
participant Redis as 缓存服务

Note over Sender: 发送消息(HTTP)
Sender->>Gateway: POST /messages {to_uid:10002, content}
Gateway->>MySQL: 验证to_uid存在性
alt 接收方存在
Gateway->>MsgServer: 转发消息
MsgServer->>MySQL: INSERT INTO messages (...)
MsgServer->>Redis: 存储离线消息
MsgServer-->>Gateway: 200 OK
Gateway-->>Sender: 发送成功
else 接收方不存在
Gateway-->>Sender: 404错误
end

Note over Receiver: 消息拉取(HTTP)
loop 每3秒轮询
Receiver->>Gateway: GET /messages/pending
Gateway->>Redis: 检查离线消息
alt 存在新消息
Redis-->>Gateway: 返回消息列表
Gateway-->>Receiver: 200 OK + 消息数据
else 无新消息
Gateway-->>Receiver: 204 No Content
end
end

Note over Sender,Receiver: 文件传输(TCP直连)
Sender->>Receiver: TCP连接建立(IP:PORT)
Sender->>Receiver: 发送文件流数据
Receiver-->>Sender: ACK确认包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
%% 消息传递流程
flowchart TD
A[用户输入消息] --> B{消息类型}
B --> |文本| C[生成唯一UUID]
C --> D{文本长度 > 1024?}
D --> |是| E[立即发送分片数据]
D --> |否| F[缓存消息]
E --> G[清空缓存]
F --> H[点击发送按钮]
H --> I[TCP发送请求]
I --> J{服务器处理}

subgraph 服务器路由
J --> K[解析 fromuid/touid]
K --> L{用户在线?}
L --> |本服务器在线| M[直接推送通知]
L --> |跨服务器/离线| N[gRPC转发]
N --> O[目标服务器接收]
O --> P{用户在线?}
P --> |是| Q[推送至客户端]
P --> |否| R[存入离线消息]
end

Q --> S[接收方UI更新]
M --> S
S --> T[显示新消息]
J --> U[返回响应ID_TEXT_CHAT_MSG_RSP]
U --> V[客户端更新状态]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
%% 好友查询与申请流程
flowchart TD
subgraph 客户端交互
A[用户输入UID/昵称] --> B[显示加载动画]
B --> C{发送搜索请求\nID_SEARCH_USER_REQ}
C --> |成功| D[展示FindSuccessDlg]
C --> |失败| E[展示FindFailDlg]
D --> F[点击添加按钮]
F --> G[弹出ApplyFriend对话框]
G --> H{发送申请请求\nID_ADD_FRIEND_REQ}
end

subgraph 服务器处理
I[接收ID_SEARCH_USER_REQ] --> J{查询Redis/DB}
J --> |存在| K[返回用户信息]
J --> |不存在| L[返回错误码]

M[接收ID_ADD_FRIEND_REQ] --> N[更新MySQL申请记录]
N --> O[查询touid所在服务器]
O --> P{是否本服务器?}
P --> |是| Q[直接推送通知]
P --> |否| R[gRPC转发请求]
end

subgraph 跨服务器通信
R --> S[目标服务器接收请求]
S --> T{用户在线?}
T --> |在线| U[通过Session推送]
T --> |离线| V[记录离线消息]
end

subgraph 数据库操作
N -.-> W[INSERT friend_apply]
J -.-> X[SELECT user表]
W -.-> Y[ON DUPLICATE更新]
end

H --> M
Q --> U
U --> Z[接收方显示红点通知]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
%% 注册功能流程图
sequenceDiagram
participant Client as 客户端
participant Server as 服务端
participant Redis as Redis缓存
participant MySQL as MySQL数据库

Client->>Client: 用户填写注册表单
Client->>Client: 点击sure_btn按钮
Client->>Client: 校验输入完整性
alt 输入校验失败
Client->>Client: 显示错误提示
else 输入校验通过
Client->>Server: POST /user_register (JSON请求)
Server->>Redis: 查询验证码
alt 验证码不存在/过期
Server->>Client: 返回错误码VarifyExpired
else 验证码不匹配
Server->>Client: 返回错误码VarifyCodeErr
else 验证码匹配
Server->>MySQL: 调用存储过程reg_user
MySQL->>MySQL: 检查用户/邮箱是否存在
alt 用户已存在
Server->>Client: 返回错误码UserExist
else 插入新用户
MySQL->>MySQL: 更新user_id表
MySQL->>MySQL: 插入user表
Server->>Client: 返回uid和成功状态
Client->>Client: 显示注册成功提示
Client->>Client: 跳转至登录界面
end
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
%% 登录功能流程图
sequenceDiagram
participant Client as 客户端
participant GateServer as 网关服务
participant MySQL as 用户数据库
participant StatusServer as 状态服务
participant ChatServer as 聊天服务

Client->>Client: 用户输入用户名/密码
Client->>Client: 点击login_btn按钮
alt 输入校验失败
Client->>Client: 显示错误提示
else 校验通过
Client->>GateServer: POST /user_login (加密密码)
GateServer->>MySQL: 查询用户密码
alt 用户不存在/密码错误
GateServer->>Client: 返回错误码PasswdInvalid
else 验证通过
GateServer->>StatusServer: gRPC GetChatServer(uid)
StatusServer->>StatusServer: 轮询选择可用聊天服务器
StatusServer->>GateServer: 返回host+token
GateServer->>Client: 返回登录成功及服务器信息
Client->>ChatServer: 使用token建立长连接
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%% 验证码发送流程图
sequenceDiagram
participant Client as 客户端
participant GateServer as 网关服务
participant VarifyServer as 验证服务
participant Redis as Redis缓存
participant Email as 邮箱服务

Client->>GateServer: POST /get_verify_code (邮箱)
GateServer->>VarifyServer: gRPC GetVarifyCode(邮箱)
alt 首次请求
VarifyServer->>VarifyServer: 生成4位随机验证码
VarifyServer->>Redis: SET code_邮箱=验证码(1分钟过期)
VarifyServer->>Email: 调用nodemailer发送邮件
Email-->>VarifyServer: 发送结果
else 重复请求(1分钟内)
VarifyServer->>Redis: GET code_邮箱
Redis-->>VarifyServer: 返回已有验证码
end
VarifyServer-->>GateServer: 返回操作结果
GateServer-->>Client: 返回成功/错误信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
%% 文件传输流程图
sequenceDiagram
participant Client as 客户端
participant TcpClient as TCP连接模块
participant LogicSystem as 逻辑处理系统
participant FileSystem as 文件存储系统
participant FileWorker as 文件写入线程

Client->>TcpClient: 分块发送文件(ID_UPLOAD_FILE_REQ)
TcpClient->>TcpClient: 封装报文(ID+长度+Base64数据)
TcpClient->>LogicSystem: 消息投递到逻辑队列
alt 首次分块
LogicSystem->>LogicSystem: 按文件名哈希选择工作线程
LogicSystem->>FileSystem: 投递到对应FileWorker队列
FileSystem->>FileWorker: 取出任务并写入文件
FileWorker->>FileSystem: 追加分块数据到临时文件
else 后续分块
FileWorker->>FileWorker: 按序列号追加数据
end
FileSystem-->>Client: 返回分块确认(ID_UPLOAD_FILE_RSP)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
classDiagram
%% 消息结构体
class AuthFriendRsp {
<<message>>
int32 error=1
int32 fromuid=2
int32 touid=3
}

class TextChatMsgReq {
<<message>>
int32 fromuid=1
int32 tuid=2
TextChatData[] textmsgs
}

class TextChatData {
<<message>>
string msgid=1
string msgcontent=2
}

class TextChatMsgRsp {
<<message>>
int32 error=1
int32 fromuid=2
int32 tuid=3
TextChatData[] textmsgs
}

%% 服务接口
class ChatService {
<<service>>
+ NotifyAddFriend(AddFriendReq): AddFriendRsp
+ RplyAddFriend(RplyFriendReq): RplyFriendRsp
+ SendChatMsg(SendChatMsgReq): SendChatMsgRsp
+ NotifyAuthFriend(AuthFriendReq): AuthFriendRsp
+ NotifyTextChatMsg(TextChatMsgReq): TextChatMsgRsp
}

%% 关系定义
TextChatMsgReq --> TextChatData : contains *
TextChatMsgRsp --> TextChatData : contains *
ChatService ..> AuthFriendRsp : returns
ChatService ..> TextChatMsgReq : accepts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sequenceDiagram
participant Client
participant RedisConPool
participant RedisServer

Client->>RedisConPool: 创建连接池(指定poolSize,host,port,pwd)
loop 创建连接
RedisConPool->>RedisServer: redisConnect(host,port)
RedisServer-->>RedisConPool: 返回context
RedisConPool->>RedisServer: AUTH pwd
RedisServer-->>RedisConPool: 返回认证结果
alt 认证成功
RedisConPool->>connections_: 存入队列
else 认证失败
RedisConPool->>RedisServer: 关闭连接
end
end
Note right of RedisConPool: 启动守护线程(定时检查连接活性)

A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
sequenceDiagram
participant Client
participant gRPCServer
participant Redis
participant EmailService

Client->>gRPCServer: GetVarifyCode(email)
activate gRPCServer

gRPCServer->>Redis: Check connection
Redis-->>gRPCServer: Connection status

alt Redis not ready
gRPCServer-->>Client: Return RedisErr
else Redis ready
gRPCServer->>Redis: GET code_<email>
Redis-->>gRPCServer: Existing code/null

alt No existing code
gRPCServer->>gRPCServer: Generate 4-digit UUID
gRPCServer->>Redis: SETEX code_<email> 100s
Redis-->>gRPCServer: Success/Failure
end

gRPCServer->>EmailService: Send verification email
EmailService-->>gRPCServer: Send result

alt Email sent successfully
gRPCServer-->>Client: Return Success
else Email failed
gRPCServer-->>Client: Return Exception
end
end

deactivate gRPCServer