企业圈系统设计

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
46
47
根据需求,出一份符合规范的设计文档。

# 需求
仿造微信朋友圈,设计并实现以下能力:
- **需求1**: 企业用户可以发布或删除企业圈内容
- **需求2** 同企业成员互相可见
- **需求3**支持点赞企业圈内容,24小时内发布且点赞数超过10的企业圈内容中,点赞数前三的自动置顶,置顶的内容按点赞数高到低排序
- **需求4** (可选)支持企业圈内容评论

参考数据
- **企业数量**:1w
- **每日新增内容**:50w条
- **日活用户数**:500w

# 设计文档规范
## 文档结构
```
一、 引言
文档变更日志
二、 项目概述
2.1 需求背景
三、详细设计
3.1 系统架构设计
系统架构设计图,当涉及到多模块架构时,需按总系统架构图->各子模块系统架构图的层级关系表示
3.1.1系统架构图
3.1.2子模块1架构图
3.1.3子模块2架构图
3.2 数据库设计
3.3 系统功能设计
3.4 接口设计
五、附录
5.1 名词解释
4.2 参考资料
参考资料链接列表
```

# 技术选型
- **编程语言**: golang
- **网络框架**:gin
- **数据库**: mysql

# 注意!
- 设计文档请严格按照规范输出
- 流程图格式使用 mermaid 绘制
- 注意,请认真阅读4个需求,接口和数据库设计都必须要实现
- 文档内容要足够清晰详细,前端需要看接口文档对接联通,测试需要看文档写测试用例
- 你的所有回答、方案、实现都需要谨慎、专业、深入的思考。

企业圈系统设计文档

一、引言

本设计文档描述了企业圈系统的整体架构设计、数据库设计、功能模块设计以及API接口设计。该系统旨在为企业内部员工提供类似微信朋友圈的社交功能,支持企业内容发布、点赞、评论等功能,并通过智能置顶算法提升优质内容的曝光度。

文档变更日志

*版本* *变更日期* *变更内容* *变更人*
v1.0 2024-12-19 初始版本,包含系统架构、数据库设计、API设计 系统架构师
v1.1 2024-12-19 优化架构图为Mermaid格式,完善技术实现细节 系统架构师

二、项目概述

2.1 需求背景

随着企业数字化转型的推进,企业内部员工之间的信息交流和协作需求日益增长。传统的企业内部沟通方式往往缺乏互动性和即时性,难以满足现代企业的沟通需求。

本项目旨在构建一个企业级的内部社交平台,具体实现以下核心需求:

****核心功能需求****:

  1. ****内容管理****:企业用户可以发布或删除企业圈内容
  2. ****权限控制****:同企业成员互相可见,跨企业内容隔离
  3. ****智能置顶****:24小时内发布且点赞数超过10的内容中,点赞数前三的自动置顶
  4. ****互动功能****:支持企业圈内容评论,增强用户互动

****系统规模指标****:

  • ****企业数量****:10,000家企业
  • ****每日新增内容****:500,000条
  • ****日活用户数****:5,000,000人
  • ****峰值QPS预估****:~15,000 QPS(考虑高峰时段集中访问)
  • ****存储预估****:每日新增约100GB数据(包含文本、图片、元数据等)

三、详细设计

3.1 系统架构设计

3.1.1 系统整体架构图

系统采用微服务架构,分为客户端层、网关层、应用服务层、数据存储层和基础设施层。通过水平分层和垂直拆分,确保系统的高可用性和可扩展性。

image-20250828142839440

graph TB
    subgraph "客户端层"
        WebClient["Web客户端"]
        MobileApp["移动端APP"]
        ThirdParty["第三方应用"]
    end
    
    subgraph "网关层"
        Gateway["API网关
负载均衡
限流熔断"] end subgraph "应用服务层" AuthService["认证服务
- 用户登录注册
- JWT令牌管理
- 权限验证"] ContentService["内容服务
- 企业圈CRUD
- 动态Feed生成
- 置顶算法"] LikeService["点赞服务
- 点赞/取消
- 计数统计
- 热度计算"] CommentService["评论服务
- 评论CRUD
- 二级回复
- 内容审核"] end subgraph "数据存储层" MySQL["MySQL集群
- 主从分离
- 分库分表
- 读写分离"] Redis["Redis集群
- 缓存热点数据
- 计数器
- 分布式锁"] FileStorage["文件存储
- 图片存储
- CDN加速"] end subgraph "基础设施层" MQ["消息队列
- 异步处理
- 削峰填谷"] Monitor["监控告警
- 性能监控
- 业务监控"] Schedule["定时任务
- 置顶更新
- 数据同步"] end WebClient --> Gateway MobileApp --> Gateway ThirdParty --> Gateway Gateway --> AuthService Gateway --> ContentService Gateway --> LikeService Gateway --> CommentService AuthService --> MySQL AuthService --> Redis ContentService --> MySQL ContentService --> Redis ContentService --> FileStorage ContentService --> MQ LikeService --> MySQL LikeService --> Redis LikeService --> MQ CommentService --> MySQL CommentService --> Redis MQ --> Schedule Schedule --> ContentService Monitor --> AuthService Monitor --> ContentService Monitor --> LikeService Monitor --> CommentService

3.1.2 核心业务流程时序图

主要展示用户登录、内容发布、点赞等核心业务流程的交互时序。

image-20250828142944999

sequenceDiagram
    participant Client as 客户端
    participant Gateway as API网关
    participant Auth as 认证服务
    participant Content as 内容服务
    participant Like as 点赞服务
    participant MySQL as MySQL数据库
    participant Redis as Redis缓存

    Note over Client,Redis: 用户登录流程
    Client->>Gateway: POST /api/v1/auth/login
    Gateway->>Auth: 转发登录请求
    Auth->>MySQL: 验证用户凭证
    MySQL-->>Auth: 返回用户信息
    Auth->>Redis: 缓存用户会话
    Auth-->>Gateway: 返回JWT令牌
    Gateway-->>Client: 登录成功响应

    Note over Client,Redis: 发布内容流程
    Client->>Gateway: POST /api/v1/posts
    Gateway->>Auth: 验证JWT令牌
    Auth-->>Gateway: 令牌有效
    Gateway->>Content: 转发发布请求
    Content->>MySQL: 插入内容记录
    MySQL-->>Content: 插入成功
    Content->>Redis: 更新Feed缓存
    Content-->>Gateway: 发布成功
    Gateway-->>Client: 返回成功响应

    Note over Client,Redis: 点赞流程
    Client->>Gateway: POST /api/v1/posts/{id}/like
    Gateway->>Auth: 验证权限
    Auth-->>Gateway: 权限验证通过
    Gateway->>Like: 处理点赞请求
    Like->>Redis: 检查点赞状态
    Redis-->>Like: 返回当前状态
    Like->>MySQL: 更新点赞记录
    Like->>Redis: 更新点赞计数
    Like->>Content: 异步检查置顶条件
    Like-->>Gateway: 点赞操作完成
    Gateway-->>Client: 返回操作结果

3.1.3 智能置顶算法流程图

展示自动置顶机制的具体执行流程,确保24小时内高质量内容能够获得更多曝光。

mermaid-diagram-2025-08-28-143154

flowchart TD
    A[定时任务启动
每小时执行] --> B[获取所有企业列表] B --> C{遍历每个企业} C --> D[查询24小时内
点赞数>10的内容] D --> E{是否有符合条件的内容?} E -->|否| F[清空当前置顶内容] E -->|是| G[按点赞数降序排序] G --> H[取前3名内容] H --> I[清空旧置顶状态] I --> J[设置新置顶内容] J --> K[更新is_pinned=1
设置pinned_at时间] K --> L[清理Redis置顶缓存] L --> M[预热新置顶内容到缓存] F --> N[继续下一个企业] M --> N N --> C C -->|所有企业处理完成| O[任务完成
等待下次执行] style A fill:#e1f5fe style O fill:#c8e6c9 style K fill:#fff3e0

3.2 数据库设计

3.2.1 数据库ER图

系统核心实体关系图,展示企业、用户、内容、点赞、评论之间的关联关系。

mermaid-diagram-2025-08-28-143437

erDiagram
    enterprises {
        bigint id PK "企业ID"
        varchar name "企业名称"
        varchar code "企业代码"
        varchar logo "企业logo"
        tinyint status "状态"
        timestamp created_at "创建时间"
        timestamp updated_at "更新时间"
    }
    
    users {
        bigint id PK "用户ID"
        bigint enterprise_id FK "企业ID"
        varchar username "用户名"
        varchar email "邮箱"
        varchar nickname "昵称"
        varchar avatar "头像URL"
        varchar password_hash "密码哈希"
        tinyint status "状态"
        timestamp created_at "创建时间"
        timestamp updated_at "更新时间"
    }
    
    posts {
        bigint id PK "内容ID"
        bigint user_id FK "发布用户ID"
        bigint enterprise_id FK "企业ID"
        text content "内容文本"
        json images "图片URL列表"
        int like_count "点赞数"
        int comment_count "评论数"
        tinyint is_pinned "是否置顶"
        timestamp pinned_at "置顶时间"
        tinyint status "状态"
        timestamp created_at "创建时间"
        timestamp updated_at "更新时间"
    }
    
    likes {
        bigint id PK "点赞ID"
        bigint post_id FK "内容ID"
        bigint user_id FK "用户ID"
        timestamp created_at "点赞时间"
    }
    
    comments {
        bigint id PK "评论ID"
        bigint post_id FK "内容ID"
        bigint user_id FK "评论用户ID"
        bigint parent_id FK "父评论ID"
        text content "评论内容"
        tinyint status "状态"
        timestamp created_at "创建时间"
        timestamp updated_at "更新时间"
    }
    
    enterprises ||--o{ users : "包含"
    enterprises ||--o{ posts : "拥有"
    users ||--o{ posts : "发布"
    users ||--o{ likes : "点赞"
    users ||--o{ comments : "评论"
    posts ||--o{ likes : "被点赞"
    posts ||--o{ comments : "被评论"
    comments ||--o{ comments : "回复"

3.2.2 分库分表策略

****分表策略****:

  • 按企业ID进行分表,每个企业的数据存储在独立的表中
  • 分表规则:posts_${enterprise_id % 100}
  • 支持最大10,000个企业,平均每表100个企业

****索引优化策略****:

1
2
3
4
5
6
7
8
-- 企业圈动态查询优化索引
CREATE INDEX idx_posts_enterprise_created ON posts_${shard} (enterprise_id, created_at DESC, status);

-- 置顶内容查询优化索引
CREATE INDEX idx_posts_pinned_likes ON posts_${shard} (enterprise_id, is_pinned DESC, like_count DESC);

-- 24小时热门内容查询索引
CREATE INDEX idx_posts_hot_24h ON posts_${shard} (enterprise_id, created_at, like_count, status);

3.2.3 企业表 (enterprises)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE `enterprises` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '企业ID',
`name` varchar(100) NOT NULL COMMENT '企业名称',
`code` varchar(50) NOT NULL COMMENT '企业代码',
`logo` varchar(255) DEFAULT '' COMMENT '企业logo',
`description` text DEFAULT '' COMMENT '企业描述',
`industry` varchar(50) DEFAULT '' COMMENT '所属行业',
`employee_count` int(11) DEFAULT 0 COMMENT '员工数量',
`status` tinyint(1) DEFAULT 1 COMMENT '状态: 1-正常, 0-禁用',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`),
KEY `idx_name` (`name`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='企业表';

3.2.4 用户表 (users)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`enterprise_id` bigint(20) unsigned NOT NULL COMMENT '企业ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`email` varchar(100) NOT NULL COMMENT '邮箱',
`password_hash` varchar(255) NOT NULL COMMENT '密码哈希',
`nickname` varchar(50) DEFAULT '' COMMENT '昵称',
`avatar` varchar(255) DEFAULT '' COMMENT '头像URL',
`department` varchar(100) DEFAULT '' COMMENT '部门',
`position` varchar(100) DEFAULT '' COMMENT '职位',
`phone` varchar(20) DEFAULT '' COMMENT '手机号',
`last_login_at` timestamp NULL DEFAULT NULL COMMENT '最后登录时间',
`status` tinyint(1) DEFAULT 1 COMMENT '状态: 1-正常, 0-禁用',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
KEY `idx_enterprise_id` (`enterprise_id`),
KEY `idx_status` (`status`),
KEY `idx_last_login` (`last_login_at`),
CONSTRAINT `fk_users_enterprise` FOREIGN KEY (`enterprise_id`) REFERENCES `enterprises` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

3.2.5 企业圈内容表 (posts)

支持分表存储,按企业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
-- 分表模板,实际会创建 posts_0 到 posts_99 共100个表
CREATE TABLE `posts_${shard}` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '内容ID',
`user_id` bigint(20) unsigned NOT NULL COMMENT '发布用户ID',
`enterprise_id` bigint(20) unsigned NOT NULL COMMENT '企业ID',
`content` text NOT NULL COMMENT '内容文本',
`images` json DEFAULT NULL COMMENT '图片URL列表',
`video_url` varchar(255) DEFAULT '' COMMENT '视频URL',
`location` varchar(200) DEFAULT '' COMMENT '发布位置',
`tags` json DEFAULT NULL COMMENT '标签列表',
`like_count` int(11) DEFAULT 0 COMMENT '点赞数',
`comment_count` int(11) DEFAULT 0 COMMENT '评论数',
`share_count` int(11) DEFAULT 0 COMMENT '分享数',
`view_count` int(11) DEFAULT 0 COMMENT '浏览数',
`is_pinned` tinyint(1) DEFAULT 0 COMMENT '是否置顶: 1-是, 0-否',
`pinned_at` timestamp NULL DEFAULT NULL COMMENT '置顶时间',
`pinned_order` int(11) DEFAULT 0 COMMENT '置顶排序: 1,2,3',
`status` tinyint(1) DEFAULT 1 COMMENT '状态: 1-正常, 0-删除',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_enterprise` (`user_id`, `enterprise_id`),
KEY `idx_enterprise_created` (`enterprise_id`, `created_at` DESC, `status`),
KEY `idx_enterprise_pinned` (`enterprise_id`, `is_pinned` DESC, `like_count` DESC),
KEY `idx_hot_content_24h` (`enterprise_id`, `created_at`, `like_count`, `status`),
CONSTRAINT `fk_posts_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
CONSTRAINT `fk_posts_enterprise` FOREIGN KEY (`enterprise_id`) REFERENCES `enterprises` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='企业圈内容表';

3.2.6 点赞表 (likes)

支持分表存储,与posts表采用相同的分片策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 分表模板,与posts表保持一致的分片策略
CREATE TABLE `likes_${shard}` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '点赞ID',
`post_id` bigint(20) unsigned NOT NULL COMMENT '内容ID',
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户ID',
`enterprise_id` bigint(20) unsigned NOT NULL COMMENT '企业ID',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '点赞时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_post_user` (`post_id`, `user_id`),
KEY `idx_user_enterprise` (`user_id`, `enterprise_id`),
KEY `idx_post_created` (`post_id`, `created_at`),
KEY `idx_enterprise_created` (`enterprise_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='点赞表';

3.2.7 评论表 (comments)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 分表模板,与posts表保持一致的分片策略
CREATE TABLE `comments_${shard}` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '评论ID',
`post_id` bigint(20) unsigned NOT NULL COMMENT '内容ID',
`user_id` bigint(20) unsigned NOT NULL COMMENT '评论用户ID',
`enterprise_id` bigint(20) unsigned NOT NULL COMMENT '企业ID',
`parent_id` bigint(20) unsigned DEFAULT 0 COMMENT '父评论ID,0为顶级评论',
`root_id` bigint(20) unsigned DEFAULT 0 COMMENT '根评论ID',
`reply_to_user_id` bigint(20) unsigned DEFAULT 0 COMMENT '回复的用户ID',
`content` text NOT NULL COMMENT '评论内容',
`like_count` int(11) DEFAULT 0 COMMENT '评论点赞数',
`reply_count` int(11) DEFAULT 0 COMMENT '回复数',
`status` tinyint(1) DEFAULT 1 COMMENT '状态: 1-正常, 0-删除',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_post_id` (`post_id`, `status`, `created_at`),
KEY `idx_user_enterprise` (`user_id`, `enterprise_id`),
KEY `idx_parent_id` (`parent_id`),
KEY `idx_root_id` (`root_id`),
CONSTRAINT `fk_comments_post` FOREIGN KEY (`post_id`) REFERENCES `posts_${shard}` (`id`),
CONSTRAINT `fk_comments_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表';

3.2.8 数据库设计总结

****设计原则****:

  1. ****数据隔离****:通过enterprise_id实现严格的企业数据隔离
  2. ****水平扩展****:采用分表策略支持大规模数据存储
  3. ****查询优化****:针对核心查询场景设计复合索引
  4. ****数据完整性****:使用外键约束保证数据一致性
  5. ****软删除****:重要数据采用status字段实现软删除

****核心查询场景及优化****:

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
-- 场景1:获取企业圈动态(置顶+普通内容)
-- 查询策略:先查置顶,再查普通,合并结果
SELECT * FROM posts_${shard}
WHERE enterprise_id = ? AND status = 1 AND is_pinned = 1
ORDER BY like_count DESC;

SELECT * FROM posts_${shard}
WHERE enterprise_id = ? AND status = 1 AND is_pinned = 0
ORDER BY created_at DESC
LIMIT ? OFFSET ?;

-- 场景2:24小时热门内容查询(置顶算法核心)
SELECT id, like_count, created_at
FROM posts_${shard}
WHERE enterprise_id = ?
AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND like_count > 10
AND status = 1
ORDER BY like_count DESC, created_at DESC
LIMIT 3;

-- 场景3:用户点赞状态批量查询
SELECT post_id
FROM likes_${shard}
WHERE user_id = ? AND post_id IN (?, ?, ?);

3.3 系统功能设计

3.3.1 用户认证与权限管理

****功能描述****:

  • 用户注册:企业员工通过企业邀请码注册
  • 用户登录:用户名/邮箱 + 密码登录,返回JWT令牌
  • 权限验证:基于企业ID的数据隔离,确保跨企业数据安全
  • 会话管理:JWT令牌有效期管理,支持令牌刷新

****技术实现方案****:

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
46
47
48
49
50
51
type AuthService struct {
userRepo repository.UserRepository
redisClient redis.Client
jwtSecret string
tokenExpiry time.Duration
}

// 用户登录
func (s *AuthService) Login(ctx context.Context, req *LoginRequest) (*LoginResponse, error) {
// 1. 验证用户凭证
user, err := s.userRepo.GetByUsername(ctx, req.Username)
if err != nil || !s.verifyPassword(req.Password, user.PasswordHash) {
return nil, errors.New("用户名或密码错误")
}

// 2. 生成JWT令牌
token, err := s.generateJWT(user.ID, user.EnterpriseID)
if err != nil {
return nil, err
}

// 3. 缓存用户会话信息
s.cacheUserSession(ctx, user.ID, user)

// 4. 更新最后登录时间
s.userRepo.UpdateLastLogin(ctx, user.ID)

return &LoginResponse{
Token: token,
User: s.buildUserVO(user),
}, nil
}

// 企业权限验证中间件
func (s *AuthService) EnterpriseAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
userID := c.GetInt64("user_id")
enterpriseID := c.GetInt64("enterprise_id")

// 从缓存中获取用户信息
user := s.getUserFromCache(c.Request.Context(), userID)
if user == nil || user.EnterpriseID != enterpriseID {
c.JSON(403, gin.H{"error": "无权访问该企业内容"})
c.Abort()
return
}

c.Set("user", user)
c.Next()
}
}

3.3.2 企业圈内容管理

****功能描述****:

  • 内容发布:支持文本、图片、视频、位置等多媒体内容
  • 内容删除:用户只能删除自己发布的内容
  • 内容编辑:支持发布后24小时内编辑
  • 动态Feed:获取企业内容动态,置顶内容优先展示

****核心业务逻辑****:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
type ContentService struct {
postRepo repository.PostRepository
userRepo repository.UserRepository
redisClient redis.Client
messageQueue mq.MessageQueue
}

// 发布内容
func (s *ContentService) CreatePost(ctx context.Context, req *CreatePostRequest) (*PostVO, error) {
// 1. 参数验证
if err := s.validateCreateRequest(req); err != nil {
return nil, err
}

// 2. 构建内容对象
post := &model.Post{
UserID: req.UserID,
EnterpriseID: req.EnterpriseID,
Content: req.Content,
Images: req.Images,
VideoURL: req.VideoURL,
Location: req.Location,
Tags: req.Tags,
Status: 1,
CreatedAt: time.Now(),
}

// 3. 数据库写入
shard := s.getShardByEnterpriseID(req.EnterpriseID)
if err := s.postRepo.Create(ctx, shard, post); err != nil {
return nil, err
}

// 4. 异步更新Feed缓存
s.messageQueue.Publish("feed.update", &FeedUpdateMessage{
PostID: post.ID,
EnterpriseID: req.EnterpriseID,
Action: "create",
})

return s.buildPostVO(ctx, post), nil
}

// 获取企业圈动态Feed
func (s *ContentService) GetEnterpriseFeed(ctx context.Context, enterpriseID int64, userID int64, page, size int) (*FeedResponse, error) {
// 1. 尝试从缓存获取
cacheKey := fmt.Sprintf("feed:%d:%d:%d", enterpriseID, page, size)
if cachedFeed := s.getFeedFromCache(ctx, cacheKey); cachedFeed != nil {
return cachedFeed, nil
}

// 2. 获取置顶内容(仅第一页显示)
var pinnedPosts []*model.Post
if page == 1 {
pinnedPosts = s.getPinnedPosts(ctx, enterpriseID)
}

// 3. 获取普通内容
shard := s.getShardByEnterpriseID(enterpriseID)
offset := (page - 1) * size
normalPosts, total, err := s.postRepo.GetByEnterpriseID(ctx, shard, enterpriseID, offset, size)
if err != nil {
return nil, err
}

// 4. 合并结果
allPosts := append(pinnedPosts, normalPosts...)

// 5. 构建响应对象,包含用户点赞状态
posts := make([]*PostVO, 0, len(allPosts))
for _, post := range allPosts {
postVO := s.buildPostVO(ctx, post)
postVO.IsLiked = s.checkUserLiked(ctx, post.ID, userID)
posts = append(posts, postVO)
}

response := &FeedResponse{
Posts: posts,
Pagination: &PaginationVO{
Page: page,
Size: size,
Total: total,
HasMore: offset+size < int(total),
},
}

// 6. 缓存结果(缓存5分钟)
s.cacheFeedResult(ctx, cacheKey, response, 5*time.Minute)

return response, nil
}

3.3.3 智能点赞系统

****功能描述****:

  • 点赞/取消点赞:支持快速响应,防止重复点赞
  • 实时计数:使用Redis实现高性能计数器
  • 点赞列表:支持分页获取点赞用户列表
  • 并发安全:使用分布式锁防止并发问题

****技术实现****:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
type LikeService struct {
likeRepo repository.LikeRepository
postRepo repository.PostRepository
redisClient redis.Client
lockManager distributed.LockManager
}

// 点赞/取消点赞 - 支持高并发
func (s *LikeService) ToggleLike(ctx context.Context, postID, userID, enterpriseID int64) (*LikeResult, error) {
// 1. 分布式锁防止并发点赞
lockKey := fmt.Sprintf("like:lock:%d:%d", postID, userID)
lock, err := s.lockManager.Acquire(ctx, lockKey, 5*time.Second)
if err != nil {
return nil, errors.New("系统繁忙,请稍后重试")
}
defer lock.Release()

// 2. 检查当前点赞状态
shard := s.getShardByEnterpriseID(enterpriseID)
exists, err := s.likeRepo.CheckExists(ctx, shard, postID, userID)
if err != nil {
return nil, err
}

var isLiked bool
var likeCount int64

if exists {
// 取消点赞
if err := s.likeRepo.Delete(ctx, shard, postID, userID); err != nil {
return nil, err
}
likeCount = s.decrementLikeCount(ctx, postID)
isLiked = false
} else {
// 添加点赞
like := &model.Like{
PostID: postID,
UserID: userID,
EnterpriseID: enterpriseID,
CreatedAt: time.Now(),
}
if err := s.likeRepo.Create(ctx, shard, like); err != nil {
return nil, err
}
likeCount = s.incrementLikeCount(ctx, postID)
isLiked = true
}

// 3. 异步检查置顶条件
go s.checkPinnedCondition(postID, enterpriseID, likeCount)

return &LikeResult{
IsLiked: isLiked,
LikeCount: likeCount,
}, nil
}

// 高性能点赞计数器
func (s *LikeService) incrementLikeCount(ctx context.Context, postID int64) int64 {
key := fmt.Sprintf("post:like_count:%d", postID)
count := s.redisClient.Incr(ctx, key).Val()

// 设置过期时间24小时
s.redisClient.Expire(ctx, key, 24*time.Hour)

return count
}

3.3.4 智能置顶算法系统

****功能描述****:

  • 自动检测:24小时内发布且点赞数超过10的内容
  • 智能排序:点赞数前三的内容自动置顶
  • 动态更新:每小时重新计算置顶内容
  • 公平机制:相同点赞数的内容按发布时间排序

****核心算法实现****:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
type PinnedService struct {
postRepo repository.PostRepository
redisClient redis.Client
scheduler *cron.Cron
}

// 定时任务:更新置顶内容
func (s *PinnedService) UpdatePinnedPosts() {
enterprises, err := s.getAllActiveEnterprises()
if err != nil {
log.Error("获取企业列表失败", zap.Error(err))
return
}

for _, enterpriseID := range enterprises {
if err := s.updateEnterpriseTopPosts(enterpriseID); err != nil {
log.Error("更新企业置顶内容失败",
zap.Int64("enterprise_id", enterpriseID),
zap.Error(err))
}
}
}

// 更新单个企业的置顶内容
func (s *PinnedService) updateEnterpriseTopPosts(enterpriseID int64) error {
// 1. 获取24小时内的热门内容(点赞数>10)
shard := s.getShardByEnterpriseID(enterpriseID)
start := time.Now().Add(-24 * time.Hour)

hotPosts, err := s.postRepo.GetHotPostsByTimeRange(context.Background(), shard, enterpriseID, start, 10)
if err != nil {
return err
}

// 2. 获取实时点赞数(从Redis获取最新数据)
for _, post := range hotPosts {
realLikeCount := s.getRealTimeLikeCount(post.ID)
if realLikeCount > 0 {
post.LikeCount = int(realLikeCount)
}
}

// 3. 按点赞数排序(点赞数相同时按创建时间排序)
sort.Slice(hotPosts, func(i, j int) bool {
if hotPosts[i].LikeCount == hotPosts[j].LikeCount {
return hotPosts[i].CreatedAt.After(hotPosts[j].CreatedAt)
}
return hotPosts[i].LikeCount > hotPosts[j].LikeCount
})

// 4. 清空旧的置顶状态
if err := s.postRepo.ClearPinnedStatus(context.Background(), shard, enterpriseID); err != nil {
return err
}

// 5. 设置新的置顶内容(前3名)
topCount := len(hotPosts)
if topCount > 3 {
topCount = 3
}

for i := 0; i < topCount; i++ {
post := hotPosts[i]
err := s.postRepo.SetPinnedStatus(context.Background(), shard, post.ID, i+1)
if err != nil {
log.Error("设置置顶状态失败", zap.Int64("post_id", post.ID), zap.Error(err))
}
}

// 6. 更新缓存
s.updatePinnedCache(enterpriseID, hotPosts[:topCount])

return nil
}

// 实时检查置顶条件(点赞触发)
func (s *PinnedService) CheckPinnedCondition(postID, enterpriseID int64, likeCount int64) {
// 只有点赞数达到10时才触发检查
if likeCount == 10 {
// 检查是否为24小时内发布
shard := s.getShardByEnterpriseID(enterpriseID)
post, err := s.postRepo.GetByID(context.Background(), shard, postID)
if err != nil {
return
}

if time.Since(post.CreatedAt) <= 24*time.Hour {
// 异步触发置顶更新
go s.updateEnterpriseTopPosts(enterpriseID)
}
}
}

3.3.5 评论系统

****功能描述****:

  • 顶级评论:直接对内容进行评论
  • 回复评论:支持二级回复(回复具体的评论)
  • 评论删除:评论者和内容发布者均可删除
  • 评论点赞:支持对评论进行点赞

****技术实现****:

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
46
47
48
49
50
51
52
53
54
55
56
57
type CommentService struct {
commentRepo repository.CommentRepository
postRepo repository.PostRepository
redisClient redis.Client
}

// 发表评论
func (s *CommentService) CreateComment(ctx context.Context, req *CreateCommentRequest) (*CommentVO, error) {
// 1. 验证目标内容是否存在
shard := s.getShardByEnterpriseID(req.EnterpriseID)
post, err := s.postRepo.GetByID(ctx, shard, req.PostID)
if err != nil || post.Status != 1 {
return nil, errors.New("目标内容不存在或已删除")
}

// 2. 如果是回复评论,验证父评论
var rootID int64 = 0
if req.ParentID > 0 {
parentComment, err := s.commentRepo.GetByID(ctx, shard, req.ParentID)
if err != nil {
return nil, errors.New("父评论不存在")
}
// 确定根评论ID
if parentComment.RootID > 0 {
rootID = parentComment.RootID
} else {
rootID = parentComment.ID
}
}

// 3. 创建评论
comment := &model.Comment{
PostID: req.PostID,
UserID: req.UserID,
EnterpriseID: req.EnterpriseID,
ParentID: req.ParentID,
RootID: rootID,
ReplyToUserID: req.ReplyToUserID,
Content: req.Content,
Status: 1,
CreatedAt: time.Now(),
}

if err := s.commentRepo.Create(ctx, shard, comment); err != nil {
return nil, err
}

// 4. 更新内容的评论数
s.postRepo.IncrementCommentCount(ctx, shard, req.PostID)

// 5. 如果是回复,更新父评论的回复数
if req.ParentID > 0 {
s.commentRepo.IncrementReplyCount(ctx, shard, req.ParentID)
}

return s.buildCommentVO(ctx, comment), nil
}

3.3.6 缓存策略设计

****Redis数据结构设计****:

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
// 点赞计数器
Key: "post:like_count:{post_id}"
Type: String
TTL: 24小时
Value: 点赞数量

// 用户会话缓存
Key: "user:session:{user_id}"
Type: Hash
TTL: 7天
Fields: {"id", "enterprise_id", "nickname", "avatar"}

// 企业Feed缓存
Key: "feed:{enterprise_id}:{page}:{size}"
Type: String (JSON)
TTL: 5分钟
Value: 序列化的动态列表

// 置顶内容缓存
Key: "pinned:posts:{enterprise_id}"
Type: List
TTL: 1小时
Value: 置顶内容ID列表(按排序)

// 热门内容24小时榜单
Key: "hot:posts:24h:{enterprise_id}"
Type: Sorted Set
TTL: 1小时
Score: 点赞数,Member: post_id

3.4 接口设计

3.4.1 用户认证接口

用户注册
1
2
3
4
5
6
7
8
9
10
11
12
13
POST /api/v1/auth/register
Content-Type: application/json

{
"username": "zhangsan", // 必填,用户名,3-20字符
"password": "password123", // 必填,密码,8-32字符
"email": "zhangsan@company.com", // 必填,邮箱地址
"nickname": "张三", // 必填,昵称,1-20字符
"phone": "13800138000", // 可选,手机号
"department": "技术部", // 可选,部门
"position": "高级工程师", // 可选,职位
"enterprise_code": "TECH001" // 必填,企业邀请码
}

****响应示例****:

1
2
3
4
5
6
7
8
9
10
{
"code": 200,
"message": "注册成功",
"data": {
"user_id": 1001,
"username": "zhangsan",
"nickname": "张三",
"enterprise_name": "科技有限公司"
}
}
用户登录
1
2
3
4
5
6
7
POST /api/v1/auth/login
Content-Type: application/json

{
"username": "zhangsan", // 用户名或邮箱
"password": "password123" // 密码
}

****响应示例****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 7200,
"user": {
"id": 1001,
"username": "zhangsan",
"nickname": "张三",
"avatar": "https://cdn.example.com/avatars/1001.jpg",
"department": "技术部",
"position": "高级工程师",
"enterprise": {
"id": 100,
"name": "科技有限公司",
"logo": "https://cdn.example.com/logos/100.jpg"
}
}
}
}
刷新令牌
1
2
POST /api/v1/auth/refresh
Authorization: Bearer {token}
用户登出
1
2
POST /api/v1/auth/logout
Authorization: Bearer {token}

3.4.2 企业圈内容接口

发布内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/v1/posts
Authorization: Bearer {token}
Content-Type: application/json

{
"content": "今天的工作总结,团队协作很棒!", // 必填,内容文本,1-2000字符
"images": [ // 可选,图片URL数组,最多9张
"https://cdn.example.com/images/001.jpg",
"https://cdn.example.com/images/002.jpg"
],
"video_url": "https://cdn.example.com/video.mp4", // 可选,视频URL
"location": "北京市朝阳区望京SOHO", // 可选,位置信息
"tags": ["工作总结", "团队协作"] // 可选,标签数组
}

****响应示例****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"code": 200,
"message": "发布成功",
"data": {
"id": 123456,
"content": "今天的工作总结,团队协作很棒!",
"images": [
"https://cdn.example.com/images/001.jpg",
"https://cdn.example.com/images/002.jpg"
],
"video_url": "https://cdn.example.com/video.mp4",
"location": "北京市朝阳区望京SOHO",
"tags": ["工作总结", "团队协作"],
"like_count": 0,
"comment_count": 0,
"view_count": 0,
"is_pinned": false,
"created_at": "2024-12-19T10:00:00Z"
}
}
获取企业圈动态
1
2
GET /api/v1/posts?page=1&size=20&sort=created_at
Authorization: Bearer {token}

****查询参数****:

  • page: 页码,默认1
  • size: 每页数量,默认20,最大100
  • sort: 排序方式,可选值:created_at(时间)、like_count(点赞数)

****响应示例****:

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
{
"code": 200,
"message": "获取成功",
"data": {
"posts": [
{
"id": 123456,
"user": {
"id": 1001,
"nickname": "张三",
"avatar": "https://cdn.example.com/avatars/1001.jpg",
"department": "技术部",
"position": "高级工程师"
},
"content": "今天的工作总结,团队协作很棒!",
"images": [
"https://cdn.example.com/images/001.jpg"
],
"video_url": "",
"location": "北京市朝阳区望京SOHO",
"tags": ["工作总结", "团队协作"],
"like_count": 25,
"comment_count": 8,
"view_count": 156,
"share_count": 3,
"is_liked": true,
"is_pinned": true,
"pinned_order": 1,
"can_edit": false,
"can_delete": false,
"created_at": "2024-12-19T08:30:00Z",
"updated_at": "2024-12-19T08:30:00Z"
}
],
"pagination": {
"page": 1,
"size": 20,
"total": 156,
"has_more": true
}
}
}
获取单个内容详情
1
2
GET /api/v1/posts/{post_id}
Authorization: Bearer {token}
编辑内容
1
2
3
4
5
6
7
8
9
PUT /api/v1/posts/{post_id}
Authorization: Bearer {token}
Content-Type: application/json

{
"content": "修改后的内容",
"images": ["https://cdn.example.com/images/new.jpg"],
"tags": ["新标签"]
}

****业务规则****:

  • 只能编辑自己发布的内容
  • 发布后24小时内可编辑
  • 编辑后重置点赞和评论计数
删除内容
1
2
DELETE /api/v1/posts/{post_id}
Authorization: Bearer {token}

****响应示例****:

1
2
3
4
5
{
"code": 200,
"message": "删除成功",
"data": null
}

3.4.3 点赞相关接口

点赞/取消点赞
1
2
POST /api/v1/posts/{post_id}/like
Authorization: Bearer {token}

****路径参数****:

  • post_id: 内容ID,必填

****响应示例****:

1
2
3
4
5
6
7
8
9
{
"code": 200,
"message": "操作成功",
"data": {
"is_liked": true, // 当前点赞状态
"like_count": 26, // 最新点赞总数
"action": "liked" // 操作类型:liked/unliked
}
}
获取点赞列表
1
2
GET /api/v1/posts/{post_id}/likes?page=1&size=20
Authorization: Bearer {token}

****查询参数****:

  • page: 页码,默认1
  • size: 每页数量,默认20,最大50

****响应示例****:

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
{
"code": 200,
"message": "获取成功",
"data": {
"users": [
{
"id": 1001,
"nickname": "张三",
"avatar": "https://cdn.example.com/avatars/1001.jpg",
"department": "技术部",
"position": "高级工程师",
"liked_at": "2024-12-19T10:30:00Z"
},
{
"id": 1002,
"nickname": "李四",
"avatar": "https://cdn.example.com/avatars/1002.jpg",
"department": "产品部",
"position": "产品经理",
"liked_at": "2024-12-19T09:15:00Z"
}
],
"pagination": {
"page": 1,
"size": 20,
"total": 26,
"has_more": true
}
}
}
批量检查点赞状态
1
2
3
4
5
6
7
POST /api/v1/posts/likes/batch-check
Authorization: Bearer {token}
Content-Type: application/json

{
"post_ids": [123456, 123457, 123458] // 内容ID数组,最多100个
}

****响应示例****:

1
2
3
4
5
6
7
8
9
10
11
{
"code": 200,
"message": "获取成功",
"data": {
"like_status": {
"123456": true,
"123457": false,
"123458": true
}
}
}

3.4.4 评论相关接口

发表评论
1
2
3
4
5
6
7
8
9
POST /api/v1/posts/{post_id}/comments
Authorization: Bearer {token}
Content-Type: application/json

{
"content": "非常赞同你的观点!", // 必填,评论内容,1-500字符
"parent_id": 0, // 可选,父评论ID,0为顶级评论
"reply_to_user_id": 1002 // 可选,回复的用户ID
}

****响应示例****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"code": 200,
"message": "评论成功",
"data": {
"id": 789012,
"content": "非常赞同你的观点!",
"parent_id": 0,
"root_id": 0,
"reply_to_user_id": 0,
"user": {
"id": 1001,
"nickname": "张三",
"avatar": "https://cdn.example.com/avatars/1001.jpg",
"department": "技术部"
},
"like_count": 0,
"reply_count": 0,
"can_delete": true,
"created_at": "2024-12-19T11:15:00Z"
}
}
获取评论列表
1
2
GET /api/v1/posts/{post_id}/comments?page=1&size=20&sort=created_at
Authorization: Bearer {token}

****查询参数****:

  • page: 页码,默认1
  • size: 每页数量,默认20,最大100
  • sort: 排序方式,可选值:created_at(时间)、like_count(点赞数)

****响应示例****:

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
46
47
48
49
50
51
{
"code": 200,
"message": "获取成功",
"data": {
"comments": [
{
"id": 789012,
"content": "非常赞同你的观点!",
"parent_id": 0,
"root_id": 0,
"user": {
"id": 1001,
"nickname": "张三",
"avatar": "https://cdn.example.com/avatars/1001.jpg",
"department": "技术部"
},
"reply_to_user": null,
"like_count": 3,
"reply_count": 2,
"is_liked": false,
"can_delete": true,
"replies": [
{
"id": 789013,
"content": "我也这么认为",
"parent_id": 789012,
"root_id": 789012,
"user": {
"id": 1002,
"nickname": "李四",
"avatar": "https://cdn.example.com/avatars/1002.jpg"
},
"reply_to_user": {
"id": 1001,
"nickname": "张三"
},
"like_count": 1,
"created_at": "2024-12-19T11:20:00Z"
}
],
"created_at": "2024-12-19T11:15:00Z"
}
],
"pagination": {
"page": 1,
"size": 20,
"total": 8,
"has_more": false
}
}
}
回复评论
1
2
3
4
5
6
7
POST /api/v1/comments/{comment_id}/replies
Authorization: Bearer {token}
Content-Type: application/json

{
"content": "我也这么认为"
}
评论点赞
1
2
POST /api/v1/comments/{comment_id}/like
Authorization: Bearer {token}
删除评论
1
2
DELETE /api/v1/comments/{comment_id}
Authorization: Bearer {token}

****响应示例****:

1
2
3
4
5
{
"code": 200,
"message": "删除成功",
"data": null
}

3.4.5 文件上传接口

图片上传
1
2
3
4
5
POST /api/v1/upload/images
Authorization: Bearer {token}
Content-Type: multipart/form-data

files: [File, File, ...] // 支持批量上传,最多9张图片

****响应示例****:

1
2
3
4
5
6
7
8
9
10
{
"code": 200,
"message": "上传成功",
"data": {
"urls": [
"https://cdn.example.com/images/20241219/001.jpg",
"https://cdn.example.com/images/20241219/002.jpg"
]
}
}
视频上传
1
2
3
4
5
POST /api/v1/upload/video
Authorization: Bearer {token}
Content-Type: multipart/form-data

file: File // 单个视频文件,最大100MB

3.4.6 统计接口

获取企业圈统计数据
1
2
GET /api/v1/statistics/enterprise
Authorization: Bearer {token}

****响应示例****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"code": 200,
"message": "获取成功",
"data": {
"total_posts": 1250, // 企业总内容数
"total_users": 89, // 企业总用户数
"today_posts": 15, // 今日新增内容
"today_active_users": 67, // 今日活跃用户
"hot_posts": [ // 当前热门内容(前3)
{
"id": 123456,
"content": "工作总结摘要...",
"like_count": 35,
"is_pinned": true
}
]
}
}
获取个人统计数据
1
2
GET /api/v1/statistics/personal
Authorization: Bearer {token}

3.4.7 通用响应格式

所有API接口均采用统一的响应格式:

****成功响应****:

1
2
3
4
5
6
7
{
"code": 200,
"message": "success",
"data": {
// 具体数据内容
}
}

****错误响应****:

1
2
3
4
5
{
"code": 400, // 错误码
"message": "参数错误", // 错误信息
"data": null
}

****常见错误码****:

  • 200: 请求成功
  • 400: 请求参数错误
  • 401: 未授权(token无效或过期)
  • 403: 无权限访问(跨企业访问等)
  • 404: 资源不存在
  • 409: 资源冲突(重复点赞等)
  • 422: 业务逻辑错误(超出编辑时限等)
  • 429: 请求频率限制
  • 500: 服务器内部错误
  • 503: 服务暂不可用

****错误响应详细示例****:

1
2
3
4
5
6
7
8
9
10
11
{
"code": 422,
"message": "内容发布失败",
"data": null,
"error_details": {
"error_code": "POST_EDIT_TIMEOUT",
"error_msg": "发布时间超过24小时,无法编辑",
"field": "created_at",
"timestamp": "2024-12-19T10:00:00Z"
}
}

3.5 部署架构设计

3.5.1 容器化部署

****Docker镜像构建****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -o enterprise-circle ./cmd/server

FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/enterprise-circle .
COPY --from=builder /app/configs ./configs
EXPOSE 8080
CMD ["./enterprise-circle"]

****Kubernetes部署配置****:

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
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: enterprise-circle-api
spec:
replicas: 3
selector:
matchLabels:
app: enterprise-circle-api
template:
metadata:
labels:
app: enterprise-circle-api
spec:
containers:
- name: api
image: enterprise-circle:v1.1
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: "mysql-cluster"
- name: REDIS_HOST
value: "redis-cluster"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"

3.5.2 监控部署

****Prometheus监控配置****:

1
2
3
4
5
6
7
8
9
10
# prometheus.yml
global:
scrape_interval: 15s

scrape_configs:
- job_name: 'enterprise-circle'
static_configs:
- targets: ['api:8080']
metrics_path: '/metrics'
scrape_interval: 10s

四、技术实现要点

4.1 高并发优化策略

4.1.1 分层缓存体系

****L1缓存 - 本地缓存****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用sync.Map实现高性能本地缓存
type LocalCache struct {
enterpriseCache sync.Map // 企业信息缓存
userCache sync.Map // 用户基础信息缓存
configCache sync.Map // 配置信息缓存
}

// 企业信息本地缓存(变化频率低)
func (c *LocalCache) GetEnterprise(enterpriseID int64) *model.Enterprise {
if val, ok := c.enterpriseCache.Load(enterpriseID); ok {
return val.(*model.Enterprise)
}
return nil
}

****L2缓存 - Redis分布式缓存****:

1
2
3
4
5
6
7
8
// Redis缓存策略配置
var CacheConfig = map[string]CacheStrategy{
"user:session": {TTL: 7 * 24 * time.Hour, Type: "hash"},
"post:detail": {TTL: 1 * time.Hour, Type: "string"},
"post:like_count": {TTL: 24 * time.Hour, Type: "string"},
"feed:enterprise": {TTL: 5 * time.Minute, Type: "string"},
"hot:posts:24h": {TTL: 1 * time.Hour, Type: "zset"},
}

4.1.2 数据库集群架构

****MySQL读写分离配置****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 数据库配置
database:
master:
host: mysql-master.example.com
port: 3306
max_open_conns: 200
max_idle_conns: 50

slaves:
- host: mysql-slave1.example.com
port: 3306
weight: 1
- host: mysql-slave2.example.com
port: 3306
weight: 1

sharding:
strategy: "enterprise_id"
shard_count: 100
rule: "enterprise_id % 100"

****分表路由策略****:

1
2
3
4
5
6
7
8
9
10
11
12
13
type ShardingRouter struct {
shardCount int
}

func (r *ShardingRouter) GetShard(enterpriseID int64) string {
shard := enterpriseID % int64(r.shardCount)
return fmt.Sprintf("%d", shard)
}

func (r *ShardingRouter) GetTableName(baseTable string, enterpriseID int64) string {
shard := r.GetShard(enterpriseID)
return fmt.Sprintf("%s_%s", baseTable, shard)
}

4.1.3 异步处理架构

****消息队列设计****:

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
// 消息队列主题设计
const (
TopicFeedUpdate = "feed.update" // Feed更新
TopicLikeEvent = "like.event" // 点赞事件
TopicCommentEvent = "comment.event" // 评论事件
TopicPinnedUpdate = "pinned.update" // 置顶更新
TopicStatistics = "statistics" // 统计数据
)

// 异步消息处理器
type AsyncProcessor struct {
contentService *ContentService
likeService *LikeService
redisClient redis.Client
}

// 处理Feed更新消息
func (p *AsyncProcessor) HandleFeedUpdate(msg *FeedUpdateMessage) error {
switch msg.Action {
case "create":
return p.contentService.InvalidateFeedCache(msg.EnterpriseID)
case "delete":
return p.contentService.RemoveFromFeedCache(msg.PostID, msg.EnterpriseID)
case "update":
return p.contentService.UpdateFeedCache(msg.PostID, msg.EnterpriseID)
}
return nil
}

4.1.4 连接池优化

****数据库连接池配置****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// MySQL连接池优化配置
func NewMySQLConfig() *mysql.Config {
return &mysql.Config{
MaxOpenConns: 200, // 最大连接数
MaxIdleConns: 50, // 最大空闲连接数
ConnMaxLifetime: 60 * time.Minute, // 连接最大生存时间
ConnMaxIdleTime: 10 * time.Minute, // 连接最大空闲时间
}
}

// Redis连接池配置
func NewRedisConfig() *redis.Options {
return &redis.Options{
PoolSize: 100, // 连接池大小
MinIdleConns: 20, // 最小空闲连接数
MaxRetries: 3, // 最大重试次数
PoolTimeout: 5 * time.Second, // 获取连接超时
}
}

4.2 数据一致性保障

4.2.1 分布式锁实现

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
type DistributedLock struct {
redisClient redis.Client
keyPrefix string
}

// 获取分布式锁
func (l *DistributedLock) Acquire(ctx context.Context, key string, expiry time.Duration) (*Lock, error) {
lockKey := l.keyPrefix + key
lockValue := uuid.New().String()

// 使用SET NX EX原子操作获取锁
success := l.redisClient.SetNX(ctx, lockKey, lockValue, expiry).Val()
if !success {
return nil, errors.New("获取锁失败")
}

return &Lock{
key: lockKey,
value: lockValue,
redis: l.redisClient,
}, nil
}

// 释放锁(使用Lua脚本保证原子性)
func (lock *Lock) Release() error {
script := `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
return lock.redis.Eval(context.Background(), script, []string{lock.key}, lock.value).Err()
}

4.2.2 最终一致性方案

****数据同步策略****:

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
// 定期同步Redis计数器到MySQL
func (s *SyncService) SyncLikeCountsToMySQL() {
// 1. 获取所有待同步的计数器
pattern := "post:like_count:*"
keys := s.redisClient.Keys(context.Background(), pattern).Val()

// 2. 批量获取计数值
pipe := s.redisClient.Pipeline()
for _, key := range keys {
pipe.Get(context.Background(), key)
}
results, err := pipe.Exec(context.Background())
if err != nil {
return
}

// 3. 批量更新数据库
updates := make(map[int64]int64)
for i, result := range results {
postID := s.extractPostIDFromKey(keys[i])
count := result.(*redis.StringCmd).Val()
if countInt, err := strconv.ParseInt(count, 10, 64); err == nil {
updates[postID] = countInt
}
}

// 4. 事务更新MySQL
s.postRepo.BatchUpdateLikeCount(context.Background(), updates)
}

4.3 性能监控与告警

4.3.1 关键指标监控

****API性能指标****:

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
type Metrics struct {
// API响应时间分布
APILatency *prometheus.HistogramVec
// API请求频率
APIRequestTotal *prometheus.CounterVec
// API错误率
APIErrorRate *prometheus.CounterVec
// 数据库连接池使用率
DBPoolUsage prometheus.Gauge
// Redis连接池使用率
RedisPoolUsage prometheus.Gauge
// 置顶算法执行时间
PinnedAlgorithmDuration prometheus.Histogram
}

// 监控中间件
func MetricsMiddleware(metrics *Metrics) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.FullPath()
method := c.Request.Method

c.Next()

duration := time.Since(start)
status := c.Writer.Status()

// 记录响应时间
metrics.APILatency.WithLabelValues(method, path).Observe(duration.Seconds())

// 记录请求计数
metrics.APIRequestTotal.WithLabelValues(method, path, strconv.Itoa(status)).Inc()

// 记录错误率
if status >= 400 {
metrics.APIErrorRate.WithLabelValues(method, path).Inc()
}
}
}

4.3.2 业务监控指标

****关键业务指标****:

  • ****内容发布量****:每分钟、每小时、每日发布量统计
  • ****用户活跃度****:DAU、MAU统计
  • ****点赞互动率****:平均点赞数、点赞参与度
  • ****置顶命中率****:24小时内达到置顶条件的内容比例
  • ****系统稳定性****:API可用率、平均响应时间

****告警规则配置****:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
alerts:
- name: "API响应时间过长"
condition: "api_latency_p99 > 1000ms"
severity: "warning"

- name: "数据库连接池使用率过高"
condition: "db_pool_usage > 0.8"
severity: "critical"

- name: "Redis连接池使用率过高"
condition: "redis_pool_usage > 0.9"
severity: "warning"

- name: "错误率过高"
condition: "api_error_rate > 0.01"
severity: "critical"

4.4 安全性设计

4.4.1 认证安全

****JWT令牌安全策略****:

1
2
3
4
5
6
7
8
9
10
11
12
13
type JWTConfig struct {
SecretKey string // 密钥,定期轮换
AccessTokenTTL time.Duration // 访问令牌有效期:2小时
RefreshTokenTTL time.Duration // 刷新令牌有效期:7天
Issuer string // 签发者
Algorithm string // 签名算法:HS256
}

// 令牌黑名单机制
func (s *AuthService) AddToBlacklist(ctx context.Context, tokenID string) error {
key := fmt.Sprintf("blacklist:token:%s", tokenID)
return s.redisClient.Set(ctx, key, "1", 7*24*time.Hour).Err()
}

4.4.2 数据安全

****敏感数据处理****:

  • 密码使用bcrypt加密存储
  • 用户数据按企业ID严格隔离
  • API接口添加频率限制
  • 关键操作记录审计日志
1
2
3
4
5
6
7
8
9
10
11
12
13
// 频率限制中间件
func RateLimitMiddleware() gin.HandlerFunc {
limiter := rate.NewLimiter(rate.Every(time.Second), 100) // 每秒100次

return func(c *gin.Context) {
if !limiter.Allow() {
c.JSON(429, gin.H{"error": "请求频率过高,请稍后重试"})
c.Abort()
return
}
c.Next()
}
}

五、附录

5.1 名词解释

*术语* *解释*
企业圈 类似微信朋友圈的企业内部社交功能,支持内容发布、点赞、评论等交互
智能置顶 基于点赞数的自动置顶算法,24小时内点赞数超过10的内容参与排名,前3名自动置顶
JWT JSON Web Token,用于分布式系统中的用户身份验证和授权的令牌标准
QPS Queries Per Second,系统每秒处理的查询请求数量,衡量系统吞吐能力的关键指标
分库分表 数据库水平拆分策略,按照特定规则将数据分布到多个数据库或表中
读写分离 数据库架构模式,写操作在主库执行,读操作在从库执行,提升并发性能
分布式锁 在分布式环境中实现互斥访问的机制,防止并发操作导致的数据不一致
Feed流 用户动态流,按时间或算法排序展示的内容列表
缓存击穿 缓存失效时大量请求同时访问数据库的现象
缓存雪崩 大量缓存同时失效导致数据库压力激增的现象
热点数据 访问频率高的数据,需要特殊的缓存和优化策略

5.2 参考资料

****技术框架文档****:

  1. Gin Web Framework 官方文档
  2. Go语言官方文档
  3. MySQL 8.0 官方参考手册
  4. Redis 官方文档

****架构设计参考****:

  1. 微服务架构设计模式
  2. 高并发系统设计原理
  3. 分布式系统设计模式

****数据库设计参考****:

  1. MySQL 高可用架构设计
  2. 数据库分库分表实践
  3. Redis 集群架构指南

****API设计规范****:

  1. RESTful API 设计指南
  2. OpenAPI 3.0 规范
  3. HTTP状态码规范

****安全设计参考****:

  1. JWT 官方规范文档
  2. OWASP API安全指南
  3. 密码安全最佳实践

****性能优化参考****:

  1. Go语言性能优化实践
  2. 缓存设计模式
  3. 负载均衡策略

****文档版本****:v1.1

****编写日期****:2024-12-19

****最后更新****:2024-12-19

****文档状态****:待评审

****架构师****:系统架构团队