可用于构建类似微信的即时通讯应用,支持单聊、群聊、朋友圈、文件传输等功能,采用分层架构,接口与实现分离,事务自动管理,JWT+Redis 认证,MD5 秒传,gnet 高性能 WebSocket。【此简介由AI生成】
Go-WeChat
一个基于 Go 语言开发的高性能即时通讯后端服务,类似微信,支持单聊、群聊、朋友圈、文件传输等功能。
项目特点:采用经典分层架构,接口与实现分离,事务自动管理,JWT+Redis 认证,MD5 秒传,gnet 高性能 WebSocket
✨ 功能特性
-
👤 用户系统
- 用户注册/登录
- JWT Token 认证
- 用户信息管理
- 用户搜索
-
👥 好友系统
- 添加/删除好友
- 好友申请处理
- 好友列表管理
- 好友备注与分组
- 黑名单管理
-
💬 消息系统
- 文本消息
- 图片消息
- 视频消息
- 文件消息
- 已读回执
- 消息历史记录
- 会话列表管理
-
👨👩👧👦 群组系统
- 创建/解散群组
- 群成员管理
- 群角色设置(群主/管理员/成员)
- 入群申请/邀请(需被邀请人同意)
- 群公告
- 邀请码加群
- 群昵称设置
- 成员禁言管理
- 群主转让
-
📇 通讯录
- 好友列表展示
- 群聊列表展示
- 快速发起会话
-
🖼️ 文件上传
- 支持 MinIO 对象存储
- 图片压缩处理
- 视频封面生成
- MD5 秒传机制
- 分类型存储管理
-
📱 朋友圈
- 发布动态(文字/图片/视频)
- 支持 Markdown 编辑器
- 代码语法高亮(PrismJS)
- 点赞/取消点赞
- 评论功能
- 动态可见性控制
-
实时通信
-
WebSocket 长连接
-
基于 gnet 的高性能网络框架
-
支持多端登录
-
心跳检测
🛠️ 技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| Go | 1.25+ | 编程语言 |
| Gin | v1.12 | Web 框架 |
| GORM | v1.31 | ORM 框架 |
| MySQL | 8.0+ | 关系型数据库 |
| Redis | 6.0+ | 缓存、Token 存储 |
| MinIO | 最新版 | 对象存储 |
| Zap | v1.27 | 结构化日志 |
| gnet | v2.9 | 高性能网络框架 |
| JWT | v5 | 身份认证 |
| bcrypt | 最新版 | 密码加密 |
🏗️ 架构设计
分层架构
┌─────────────────────────────────────────────────────────────┐
│ Controller 层 │
│ (HTTP 处理器 - 接收请求、参数校验、调用 Service) │
├─────────────────────────────────────────────────────────────┤
│ Service 层 │
│ (业务逻辑层 - 实现接口,处理核心业务) │
│ ├── 接口定义: service/*.go │
│ └── 实现: service/impl/*.go │
├─────────────────────────────────────────────────────────────┤
│ DAO 层 │
│ (数据访问层 - 数据库 CRUD 操作) │
├─────────────────────────────────────────────────────────────┤
│ Models 层 │
│ (数据模型 - 定义结构体、DTO、枚举) │
├─────────────────────────────────────────────────────────────┤
│ 基础设施层 │
│ MySQL / Redis / MinIO / WebSocket / Logs │
└─────────────────────────────────────────────────────────────┘
核心设计亮点
1. 事务自动管理(Middleware 模式)
通过 Gin 中间件实现事务的自动开启、提交和回滚:
// middleware/transaction.go
func TransactionMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tx := mysql.DB.Begin()
c.Set(TransactionKey, tx) // 将事务存入上下文
c.Next()
// 根据响应状态自动提交或回滚
if c.Writer.Status() >= 400 {
tx.Rollback()
} else {
tx.Commit()
}
}
}
// DAO 层自动获取事务
func DB(c *gin.Context) *gorm.DB {
if tx, exists := c.Get(TransactionKey); exists {
return tx.(*gorm.DB)
}
return mysql.DB
}
优势:
- 业务代码无需关心事务管理
- 自动根据 HTTP 响应状态决定提交/回滚
- 支持事务嵌套和手动控制
2. JWT + Redis 双 Token 验证
// utils/jwt.go
func GenerateToken(userID uint64, username string, source Source) (string, error) {
// 生成 JWT Token
token, err := jwt.NewWithClaims(...).SignedString(JwtKey)
// 存入 Redis,支持多端登录管理和主动失效
key := "user:token:" + uuid
redis.Rdb.Set(ctx, key, token, TokenExpire)
return token, nil
}
func ParseToken(tokenStr string) (*Claims, error) {
// 解析 JWT
claims, _ := jwt.ParseWithClaims(...)
// 校验 Redis 是否存在(支持登出失效)
redisToken, _ := redis.Rdb.Get(ctx, key).Result()
if redisToken != tokenStr {
return nil, errors.New("token 已过期或已退出")
}
return claims, nil
}
优势:
- Token 可主动失效(登出功能)
- 支持多端登录控制
- 可扩展单点登录限制
3. 文件秒传机制
// service/impl/file_service_impl.go
func (s *FileService) UploadFile(...) (*models.FileUploadResponse, error) {
// 计算文件 MD5
md5Hash, _ := calculateMD5(file)
// 检查是否已存在
if existingFile, _ := dao.GetFileUploadByMD5(md5Hash); existingFile != nil {
return existingFile.ToResponse(), nil // 秒传:直接返回已有文件
}
// 不存在则上传...
}
优势:
- 节省存储空间
- 提升上传速度
- 减少网络带宽
4. 接口与实现分离
// service/user_service.go - 接口定义
type IUserService interface {
GetUserInfo(id uint64) *models.User
Register(req *models.UserRegisterRequest) (*models.LoginResponse, error)
Login(req *models.UserLoginRequest) (*models.LoginResponse, error)
}
// service/impl/user_service_impl.go - 实现
type UserService struct{}
func (u *UserService) Register(req *models.UserRegisterRequest) (*models.LoginResponse, error) {
// 业务逻辑实现
}
// controller 中注入使用
var userService = &impl.UserService{}
优势:
- 便于单元测试(可 Mock 接口)
- 支持实现替换(如换缓存框架)
- 符合依赖倒置原则
5. WebSocket 高性能设计
基于 gnet 实现,采用 Reactor 模式:
// ws/server.go
type Server struct {
handler Handler // 业务处理器
manager *ConnManager // 连接管理器
worker *WorkerPool // 工作协程池
}
// 配置选项
func DefaultOptions() Options {
return Options{
Port: "9501",
ReadBufferCap: 16 * 1024,
IdleTimeout: 60 * time.Second,
Multicore: true,
LockOSThread: true,
}
}
优势:
- 百万级并发连接
- 零拷贝网络 I/O
- 多核 CPU 亲和性绑定
📁 项目结构
go-wechat/
├── main.go # 程序入口
├── application.yml # 配置文件
├── go.mod # 模块定义
│
├── controller/ # 控制器层(HTTP 处理器)
│ ├── user_controller.go # 用户相关接口
│ ├── friend_controller.go # 好友相关接口
│ ├── group_controller.go # 群组相关接口
│ ├── message_controller.go # 消息相关接口
│ ├── file_controller.go # 文件上传接口
│ └── moment_controller.go # 朋友圈接口
│
├── service/ # 业务逻辑接口定义
│ ├── user_service.go
│ ├── friend_service.go
│ ├── group_service.go
│ ├── message_service.go
│ ├── file_service.go
│ └── moment_service.go
│
├── service/impl/ # 业务逻辑实现
│ ├── user_service_impl.go
│ ├── friend_service_impl.go
│ ├── group_service_impl.go
│ ├── message_service_impl.go
│ ├── file_service_impl.go
│ └── moment_service_impl.go
│
├── dao/ # 数据访问层
│ ├── dao.go # 事务管理封装
│ ├── user_dao.go
│ ├── friend_dao.go
│ ├── group_dao.go
│ ├── message_dao.go
│ ├── file_dao.go
│ └── moment_dao.go
│
├── models/ # 数据模型
│ ├── User.go # 用户模型
│ ├── friend.go # 好友模型
│ ├── group.go # 群组模型
│ ├── message.go # 消息模型
│ ├── file.go # 文件模型
│ └── moment.go # 朋友圈模型
│
├── router/ # 路由配置
│ └── router.go
│
├── middleware/ # 中间件
│ └── transaction.go # 事务中间件
│
├── config/ # 配置管理
│ └── config.go # YAML 配置读取
│
├── mysql/ # MySQL 连接
│ └── mysql.go
│
├── redis/ # Redis 连接
│ └── redis.go
│
├── minio/ # MinIO 对象存储
│ └── minio.go
│
├── ws/ # WebSocket 服务
│ ├── server.go # 服务器实现
│ ├── handler.go # Handler 接口定义
│ ├── chat_handler.go # 聊天业务处理器
│ ├── manager.go # 连接管理器
│ ├── pool.go # 协程池
│ └── worker.go # 工作协程
│
├── utils/ # 工具函数
│ └── jwt.go # JWT 生成与解析
│
├── logs/ # 日志配置
│ └── log.go # Zap 日志初始化
│
├── common/ # 通用组件
│ └── common.go # 统一响应、异常处理、JWT 中间件
│
├── templates/ # HTML 模板
│ ├── login.html
│ ├── register.html
│ ├── chat.html
│ └── moment.html
│
└── static/ # 静态资源
├── css/
└── layui/
🚀 快速开始
环境要求
- Go 1.25+
- MySQL 8.0+
- Redis 6.0+
- MinIO (可选,用于文件存储)
1. 克隆项目
git clone https://github.com/yourusername/go-wechat.git
cd go-wechat
2. 安装依赖
go mod download
3. 配置数据库
创建 MySQL 数据库:
CREATE DATABASE `go-wechat` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
4. 修改配置
编辑 application.yml:
server:
port: 8084
mysql:
host: 127.0.0.1
port: 3306
username: root
password: your_password
database: go-wechat
charset: utf8mb4
minio:
endpoint: 127.0.0.1
port: 9000
accessKey: your_access_key
secretKey: your_secret_key
bucketName: go-wechat
useSSL: false
redis:
host: 127.0.0.1
port: 6379
password: ""
database: 0
5. 启动服务
go run main.go
或编译运行:
go build -o go-wechat.exe
./go-wechat.exe
启动成功后将看到:
╔══════════════════════════════════════════════════════════╗
║ 服务器启动成功 ║
╠══════════════════════════════════════════════════════════╣
║ 登录页面: http://localhost:8084/login ║
║ 注册页面: http://localhost:8084/register ║
║ 聊天页面: http://localhost:8084/chat ║
║ 朋友圈: http://localhost:8084/moment ║
╚══════════════════════════════════════════════════════════╝
6. 访问服务
| 页面 | URL | 功能说明 |
|---|---|---|
| 登录页面 | http://localhost:8084/login | 用户登录 |
| 注册页面 | http://localhost:8084/register | 用户注册 |
| 聊天页面 | http://localhost:8084/chat | 聊天、通讯录、群聊 |
| 朋友圈 | http://localhost:8084/moment | 动态发布与浏览 |
📡 API 接口
统一响应格式
{
"code": 200,
"msg": "success",
"data": {}
}
用户模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/user/register | 用户注册 | 否 |
| POST | /api/user/login | 用户登录 | 否 |
| GET | /api/user/current | 获取当前用户 | 是 |
| POST | /api/user/logout | 用户登出 | 是 |
| GET | /api/users/search | 搜索用户 | 是 |
| GET | /api/user/getBy/:id | 获取用户信息 | 否 |
好友模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/friend/request/send | 发送好友申请 | 是 |
| POST | /api/friend/request/handle | 处理好友申请 | 是 |
| GET | /api/friend/request/received | 获取收到的好友申请 | 是 |
| GET | /api/friend/request/sent | 获取发出的好友申请 | 是 |
| GET | /api/friend/list | 获取好友列表 | 是 |
| GET | /api/friend/blacklist | 获取黑名单列表 | 是 |
| POST | /api/friend/remark | 更新好友备注 | 是 |
| POST | /api/friend/group | 移动好友到分组 | 是 |
| POST | /api/friend/block | 拉黑/移出黑名单 | 是 |
| DELETE | /api/friend/:id | 删除好友 | 是 |
| GET | /api/friend/is-friend/:id | 检查是否为好友 | 是 |
好友分组模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/friend/group/create | 创建好友分组 | 是 |
| GET | /api/friend/group/list | 获取好友分组列表 | 是 |
| PUT | /api/friend/group/:id | 更新好友分组 | 是 |
| DELETE | /api/friend/group/:id | 删除好友分组 | 是 |
群组模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/group/create | 创建群组 | 是 |
| PUT | /api/group/update | 更新群组信息 | 是 |
| DELETE | /api/group/dissolve/:id | 解散群组 | 是 |
| GET | /api/group/info/:id | 获取群组详情 | 是 |
| GET | /api/group/list | 获取我的群组列表 | 是 |
| GET | /api/group/search | 搜索群组 | 是 |
| POST | /api/group/join-by-code | 通过邀请码加入群组 | 是 |
群成员模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/group-member/invite | 邀请成员入群 | 是 |
| POST | /api/group-member/leave | 退出群组 | 是 |
| DELETE | /api/group-member/kick | 踢出成员 | 是 |
| GET | /api/group-member/list/:id | 获取群成员列表 | 是 |
| PUT | /api/group-member/nickname | 修改群昵称 | 是 |
| POST | /api/group-member/mute | 设置成员禁言 | 是 |
| POST | /api/group-member/transfer | 转让群主 | 是 |
| POST | /api/group-member/set-admin | 设置/取消管理员 | 是 |
入群申请模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/group-join/handle | 处理入群申请/邀请 | 是 |
| GET | /api/group-join/requests/:id | 获取群申请列表 | 是 |
| GET | /api/group-join/my-requests | 获取我的申请记录 | 是 |
| GET | /api/group-join/pending-invites | 获取待处理邀请 | 是 |
| GET | /api/group-join/pending-invites-count | 获取待处理邀请数量 | 是 |
消息模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| GET | /api/conversations | 获取会话列表 | 是 |
| GET | /api/messages/history | 获取历史消息 | 是 |
文件模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/file/upload | 通用文件上传 | 是 |
| POST | /api/file/upload/image | 图片上传(支持压缩) | 是 |
| POST | /api/file/upload/video | 视频上传(生成封面) | 是 |
| POST | /api/file/check-md5 | MD5 秒传检查 | 是 |
| GET | /api/file/list | 获取文件列表 | 是 |
| GET | /api/file/list/:type | 按类型获取文件列表 | 是 |
| GET | /api/file/:id | 获取文件详情 | 是 |
| DELETE | /api/file/:id | 删除文件 | 是 |
朋友圈模块
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/moments | 发布动态 | 是 |
| GET | /api/moments | 获取好友动态列表 | 是 |
| GET | /api/moments/user/:user_id | 获取指定用户动态 | 是 |
| GET | /api/moments/:id | 获取动态详情 | 是 |
| DELETE | /api/moments/:id | 删除动态 | 是 |
| POST | /api/moments/:id/like | 点赞 | 是 |
| DELETE | /api/moments/:id/like | 取消点赞 | 是 |
| POST | /api/moments/:id/comment | 评论 | 是 |
| DELETE | /api/moments/comment/:id | 删除评论 | 是 |
🔌 WebSocket 接口
WebSocket 服务运行在 ws://localhost:9501
连接方式
const ws = new WebSocket('ws://localhost:9501?token=your_jwt_token');
ws.onopen = () => {
console.log('连接成功');
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log('收到消息:', msg);
};
消息格式
{
"type": 1,
"from": 10001,
"from_name": "张三",
"to": 10002,
"content": "你好",
"room_id": "private_10001_10002",
"timestamp": 1714023456,
"image_url": "",
"width": 0,
"height": 0,
"video_url": "",
"cover_url": "",
"duration": 0,
"file_url": "",
"file_name": "",
"file_size": 0
}
消息类型
| 类型 | 值 | 说明 |
|---|---|---|
| MessageTypeText | 1 | 文本消息 |
| MessageTypeImage | 2 | 图片消息 |
| MessageTypeVideo | 3 | 视频消息 |
| MessageTypeFile | 4 | 文件消息 |
| MessageTypeSystem | 5 | 系统消息 |
房间 ID 格式
| 类型 | 格式 | 示例 |
|---|---|---|
| 单聊 | private_小ID_大ID |
private_10001_10002 |
| 群聊 | group_群ID |
group_5 |
群聊消息广播
群聊消息通过 WebSocket 自动广播给所有在线群成员:
// 服务端处理流程
1. 接收消息 -> 解析 room_id (格式: group_群ID)
2. 查询该群所有成员
3. 遍历在线成员,通过 userConns 映射找到连接
4. 向每个在线成员转发消息
📝 代码规范
1. 包命名规范
- 使用小写字母,不带下划线
- 简洁明了,如
controller,service,dao - 模块路径:
com.zhangmeng/go-wechat
2. 接口与实现分离
// service/user_service.go - 接口定义
type IUserService interface {
GetUserInfo(id uint64) *models.User
Register(req *models.UserRegisterRequest) (*models.LoginResponse, error)
}
// service/impl/user_service_impl.go - 实现
type UserService struct{}
func (u *UserService) GetUserInfo(id uint64) *models.User {
if id == 0 {
panic("用户ID不合法")
}
return dao.GetUserById(id)
}
3. 模型定义规范
type User struct {
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
Username string `gorm:"size:50;not null;uniqueIndex" json:"username"`
Password string `gorm:"size:255;not null" json:"-"` // 敏感字段不返回
CreatedAt time.Time `json:"created_at"`
}
// DTO 分离
type UserLoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// 转换方法
func (u *User) ToResponse() UserResponse {
return UserResponse{
ID: u.ID,
Username: u.Username,
}
}
4. 枚举定义
type MessageType int
const (
MessageTypeText MessageType = iota + 1 // 1 - 文本
MessageTypeImage // 2 - 图片
MessageTypeVideo // 3 - 视频
)
// 实现 String() 方法
func (mt MessageType) String() string {
switch mt {
case MessageTypeText:
return "text"
case MessageTypeImage:
return "image"
default:
return "unknown"
}
}
5. DAO 层写法
// 直接使用全局 DB 实例
func GetUserById(id uint64) *models.User {
var user models.User
if err := mysql.DB.First(&user, id).Error; err != nil {
return nil
}
return &user
}
// 支持事务的版本
func GetUserByIdWithTx(tx *gorm.DB, id uint64) *models.User {
var user models.User
if err := tx.First(&user, id).Error; err != nil {
return nil
}
return &user
}
6. 错误处理
// Service 层返回 error
func (u *UserService) Register(req *models.UserRegisterRequest) (*models.LoginResponse, error) {
if dao.CheckUsernameExists(req.Username) {
return nil, errors.New("用户名已存在")
}
// ...
}
// Controller 层统一处理
func (uc *UserController) Register(c *gin.Context) {
resp, err := userService.Register(&req)
if err != nil {
c.JSON(200, common.Fail(err.Error()))
return
}
c.JSON(200, common.Success(resp))
}
7. 日志规范
// 使用 Zap 结构化日志
logs.LogInfo("用户登录成功", zap.Uint64("user_id", user.ID))
logs.LogError("数据库连接失败", zap.Error(err))
// 自动记录 HTTP 请求
r.Use(logs.ZapLoggerMiddleware(logs.Logger), gin.Recovery())
🔄 项目启动流程
func main() {
r := router.InitRoute() // 1. 初始化路由
mysql.InitMySQL() // 2. 连接 MySQL
mysql.CreateUpdateMysqlData() // 3. 自动建表/迁移
minio.InitMinIO() // 4. 连接 MinIO
redis.InitRedis() // 5. 连接 Redis
go startWebSocketServer() // 6. 启动 WebSocket(协程)
runHttpServer(r) // 7. 启动 HTTP 服务(主线程)
}
优雅关闭
// 监听系统退出信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 关闭资源
mysql.CloseMySQL()
redis.CloseRedis()
srv.Shutdown(ctx) // 优雅关闭 HTTP 服务
📄 许可证
MIT License
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📧 联系方式
如有问题,请提交 Issue 或联系项目维护者。