569 lines
19 KiB
Markdown
569 lines
19 KiB
Markdown
# MeshRay Service 层架构详解
|
||
|
||
**文档版本**: v1.0
|
||
**更新时间**: 2026-03-24
|
||
**适用范围**: `internal/service/` 模块
|
||
|
||
---
|
||
|
||
## 📊 Service 层在整体架构中的位置
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ Web UI(Vue 3 + Element Plus) │
|
||
└────────────────┬────────────────────────────┘
|
||
│ REST API / WebSocket
|
||
┌────────────────▼────────────────────────────┐
|
||
│ API Handler(Gin 接入层) │
|
||
│ - network_handler.go │
|
||
│ - device_handler.go │
|
||
│ - user_handler.go │
|
||
│ - service_handler.go │
|
||
└────────────────┬────────────────────────────┘
|
||
│ 函数调用
|
||
┌────────────────▼────────────────────────────┐
|
||
│ Service Layer(业务逻辑层)⭐核心 │
|
||
│ - NetworkService ← 组网管理 │
|
||
│ - DeviceService ← 设备管理 │
|
||
│ - PolicyService ← 策略管理 │
|
||
│ - ExternalService ← 外部服务管理 │
|
||
│ - UserService ← 用户认证 │
|
||
│ - MonitorService ← 监控告警 │
|
||
│ - DDNSService ← DDNS 同步 │
|
||
│ - AuditService ← 审计日志 │
|
||
└──────┬──────────────────────────────────────┘
|
||
│
|
||
├──────────────────────────────────────┐
|
||
│ │
|
||
┌──────▼──────┐ ┌────────▼────────┐
|
||
│ Store Layer │ │ Ctr Layer │
|
||
│ (数据持久化) │ │ (实时控制) │
|
||
│ │ │ │
|
||
│ - networks │ │ - wgctrl │
|
||
│ - devices │ │ - core gRPC │
|
||
│ - policies │ │ - 实时调度 │
|
||
│ - users │ │ │
|
||
│ - services │ │ │
|
||
└─────────────┘ └─────────────────┘
|
||
```
|
||
|
||
**关键点**:
|
||
- ✅ **Service 层是业务逻辑的核心**
|
||
- ✅ **Ctr 层只是 Service 层调用的一个执行器**
|
||
- ✅ **很多 Service 功能完全不依赖 Ctr**(如用户登录、策略校验)
|
||
|
||
---
|
||
|
||
## 🎯 Service 层模块划分
|
||
|
||
### **完整的服务模块清单**
|
||
|
||
```
|
||
internal/service/
|
||
├── service.go # Service 层总入口和依赖注入
|
||
│ type ServiceProvider struct {
|
||
│ network *NetworkService
|
||
│ device *DeviceService
|
||
│ policy *PolicyService
|
||
│ external *ExternalService
|
||
│ user *UserService
|
||
│ monitor *MonitorService
|
||
│ ddns *DDNSService
|
||
│ audit *AuditService
|
||
│ }
|
||
│
|
||
├── network.go # 组网管理服务
|
||
│ ├── ListNetworks() []Network # 获取网络列表
|
||
│ ├── CreateNetwork() (*Network, error) # 创建网络
|
||
│ ├── DeleteNetwork() error # 删除网络
|
||
│ ├── UpdateNetwork() error # 更新网络配置
|
||
│ ├── GenerateMeshSeed() (*MeshSeed, error) # 生成 MeshSeed
|
||
│ ├── ParseMeshSeed() (*MeshSeed, error) # 解析 MeshSeed
|
||
│ └── JoinNetwork() error # 加入网络
|
||
│
|
||
├── device.go # 设备管理服务
|
||
│ ├── ListDevices() []Device # 设备列表
|
||
│ ├── AddDevice() (*Device, error) # 添加设备
|
||
│ ├── RemoveDevice() error # 删除设备
|
||
│ ├── RegenerateKey() error # 重新生成密钥
|
||
│ ├── GetDeviceConfig() (*WGConfig, error) # 获取 WG 配置
|
||
│ └── ImportDevice() error # 导入现有设备
|
||
│
|
||
├── policy.go # 策略管理服务
|
||
│ ├── ListPolicies() []Policy # 策略列表
|
||
│ ├── SetPolicy() error # 设置传输策略
|
||
│ ├── GetPolicy() (*Policy, error) # 获取策略
|
||
│ ├── ValidatePolicy() error # 验证策略合法性
|
||
│ ├── GetEffectivePolicy() (*Policy, error) # 获取生效策略
|
||
│ └── ResetPolicy() error # 重置策略
|
||
│
|
||
├── external_service.go # 外部服务管理服务
|
||
│ ├── ListServices() []ExternalService # 服务列表
|
||
│ ├── AddService() error # 添加服务
|
||
│ ├── UpdateService() error # 更新服务
|
||
│ ├── DeleteService() error # 删除服务
|
||
│ ├── TestConnectivity() error # 测试连通性
|
||
│ ├── GetServiceSchema() (string, error) # 获取 JSON Schema
|
||
│ └── ListByCategory() []ExternalService # 按分类筛选
|
||
│
|
||
├── user.go # 用户认证服务
|
||
│ ├── Login() (string, error) # 登录 → JWT Token
|
||
│ ├── Register() error # 注册
|
||
│ ├── ChangePassword() error # 修改密码
|
||
│ ├── VerifyToken() (*Claims, error) # 验证 JWT
|
||
│ ├── RefreshToken() (string, error) # 刷新 Token
|
||
│ └── GetUserByID() (*User, error) # 根据 ID 获取用户
|
||
│
|
||
├── monitor.go # 监控告警服务
|
||
│ ├── GetSystemStats() (*SystemStats, error) # 系统统计
|
||
│ ├── GetNetworkStatus() (*NetworkStatus, error) # 网络状态
|
||
│ ├── CheckAlertRules() ([]Alert, error) # 检查告警规则
|
||
│ ├── SendAlert() error # 发送告警
|
||
│ ├── ListAlertRules() []AlertRule # 告警规则列表
|
||
│ └── AddAlertRule() error # 添加告警规则
|
||
│
|
||
├── ddns.go # DDNS 服务
|
||
│ ├── SyncDDNS() error # 同步 DDNS 记录
|
||
│ ├── GetDDNSStatus() (*DDNSStatus, error) # 获取状态
|
||
│ ├── RefreshDDNS() error # 刷新记录
|
||
│ ├── TestDNSProvider() error # 测试 DNS 服务商
|
||
│ └── GetDDNSHistory() []DDNSRecord # 历史记录
|
||
│
|
||
└── audit.go # 审计日志服务
|
||
├── LogAction() error # 记录操作日志
|
||
├── ListAuditLogs() []AuditLog # 查询日志
|
||
├── ExportAuditLogs() ([]byte, error) # 导出日志
|
||
├── GetAuditStats() (*AuditStats, error) # 统计数据
|
||
└── CleanOldLogs() error # 清理旧日志
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 每个 Service 的职责详解
|
||
|
||
### **1. NetworkService - 组网管理**
|
||
|
||
**职责**:
|
||
- ✅ 组网的创建、删除、更新
|
||
- ✅ MeshSeed 的生成和解析
|
||
- ✅ 网络配置的持久化
|
||
- ✅ 调用 Ctr 执行实际操作
|
||
|
||
**核心方法示例**:
|
||
|
||
```go
|
||
// CreateNetwork 创建网络
|
||
func (s *NetworkService) CreateNetwork(req CreateNetworkRequest) (*Network, error) {
|
||
// ① 业务规则校验
|
||
if req.Name == "" {
|
||
return nil, errors.New("组网名称不能为空")
|
||
}
|
||
|
||
// 检查名称是否重复
|
||
var existing Network
|
||
err := s.store.DB().Where("name = ?", req.Name).First(&existing).Error
|
||
if err == nil {
|
||
return nil, errors.New("组网名称已存在")
|
||
}
|
||
|
||
// ② 数据生成
|
||
networkID := snowflake.Generate()
|
||
secret := generateSecureSecret() // 32 字节随机
|
||
|
||
// ③ 保存到数据库
|
||
network := &model.Network{
|
||
ID: networkID,
|
||
Name: req.Name,
|
||
Secret: secret,
|
||
Subnet: req.Subnet,
|
||
STUNServers: []string{"stun.l.google.com:19302"},
|
||
CreatedAt: time.Now(),
|
||
}
|
||
|
||
err = s.store.DB().Create(network).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// ④ 调用 ctr 执行(事务外,失败需回滚)
|
||
err = s.ctrClient.CreateNetwork(networkID, network.ToConfig())
|
||
if err != nil {
|
||
// 回滚:删除刚创建的 network
|
||
s.store.DB().Delete(network)
|
||
return nil, fmt.Errorf("调用 ctr 失败:%w", err)
|
||
}
|
||
|
||
return network, nil
|
||
}
|
||
|
||
// GenerateMeshSeed 生成组网凭证
|
||
func (s *NetworkService) GenerateMeshSeed(networkID uint64) (*MeshSeed, error) {
|
||
// ① 查询网络信息
|
||
var network model.Network
|
||
err := s.store.DB().First(&network, networkID).Error
|
||
if err != nil {
|
||
return nil, errors.New("网络不存在")
|
||
}
|
||
|
||
// ② 生成 SeedID(16 字节随机 Base64)
|
||
seedID := generateRandomBase64(16)
|
||
|
||
// ③ Ed25519 签名
|
||
signature := ed25519.Sign(privateKey, []byte(network.Secret))
|
||
|
||
// ④ AES-GCM 加密配置
|
||
ciphertext := aesGCM.Encrypt(network.ToJSON())
|
||
|
||
// ⑤ 构建 MeshSeed
|
||
meshSeed := &MeshSeed{
|
||
SeedID: seedID,
|
||
NetworkID: networkID,
|
||
Signature: signature,
|
||
Ciphertext: ciphertext,
|
||
IssuedAt: time.Now(),
|
||
ExpiresAt: time.Now().Add(365 * 24 * time.Hour),
|
||
}
|
||
|
||
// ⑥ 保存到数据库
|
||
s.store.DB().Create(&model.MeshSeed{
|
||
NetworkID: networkID,
|
||
Data: meshSeed.ToJSON(),
|
||
})
|
||
|
||
return meshSeed, nil
|
||
// ✅ 完全不需要调用 ctr!
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **2. DeviceService - 设备管理**
|
||
|
||
**职责**:
|
||
- ✅ WireGuard 密钥对生成
|
||
- ✅ 设备信息管理
|
||
- ✅ WG 配置文件生成
|
||
- ✅ 调用 Ctr 添加 Peer
|
||
|
||
**核心方法示例**:
|
||
|
||
```go
|
||
// AddDevice 添加设备
|
||
func (s *DeviceService) AddDevice(networkID uint64, name string) (*Device, error) {
|
||
// ① 查询网络
|
||
var network model.Network
|
||
err := s.store.DB().First(&network, networkID).Error
|
||
if err != nil {
|
||
return nil, errors.New("网络不存在")
|
||
}
|
||
|
||
// ② 生成 WG 密钥对
|
||
privateKey, publicKey, err := wgtypes.GenerateKey()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// ③ 分配 IP(从子网中自动分配)
|
||
ip := allocateIP(network.Subnet)
|
||
|
||
// ④ 生成设备 ID
|
||
deviceID := snowflake.Generate()
|
||
|
||
// ⑤ 保存到数据库
|
||
device := &model.Device{
|
||
ID: deviceID,
|
||
NetworkID: networkID,
|
||
Name: name,
|
||
PublicKey: publicKey.String(),
|
||
PrivateKey: encryptPrivateKey(privateKey), // 加密存储
|
||
IP: ip,
|
||
AllowedIPs: []string{ip + "/32"},
|
||
}
|
||
|
||
err = s.store.DB().Create(device).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// ⑥ 调用 ctr 添加 Peer(可选,如果立即上线)
|
||
if req.ImmediateConnect {
|
||
err = s.ctrClient.AddPeer(networkID, device.PublicKey, device.AllowedIPs)
|
||
if err != nil {
|
||
s.store.DB().Delete(device)
|
||
return nil, fmt.Errorf("调用 ctr 失败:%w", err)
|
||
}
|
||
}
|
||
|
||
return device, nil
|
||
}
|
||
|
||
// GetDeviceConfig 获取设备配置(生成 WG 配置文件)
|
||
func (s *DeviceService) GetDeviceConfig(deviceID uint64) (string, error) {
|
||
// ① 查询设备信息
|
||
var device model.Device
|
||
err := s.store.DB().Preload("Network").First(&device, deviceID).Error
|
||
if err != nil {
|
||
return "", errors.New("设备不存在")
|
||
}
|
||
|
||
// ② 解密私钥
|
||
privateKey := decryptPrivateKey(device.PrivateKey)
|
||
|
||
// ③ 生成 WG 配置
|
||
config := fmt.Sprintf(`[Interface]
|
||
PrivateKey = %s
|
||
Address = %s
|
||
ListenPort = 51820
|
||
|
||
[Peer]
|
||
PublicKey = %s
|
||
AllowedIPs = %s
|
||
Endpoint = %s:51820
|
||
`, privateKey, device.IP, device.Network.PublicKey, device.AllowedIPs, device.Network.Endpoint)
|
||
|
||
return config, nil
|
||
// ✅ 纯业务逻辑,不需要调用 ctr!
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **3. UserService - 用户认证(完全不依赖 Ctr)**
|
||
|
||
**职责**:
|
||
- ✅ 用户注册、登录
|
||
- ✅ JWT Token 生成和验证
|
||
- ✅ 密码加密存储
|
||
- ✅ 审计日志记录
|
||
|
||
**核心方法示例**:
|
||
|
||
```go
|
||
// Login 用户登录
|
||
func (s *UserService) Login(username, password string) (string, error) {
|
||
// ① 查询用户
|
||
var user model.User
|
||
err := s.store.DB().Where("username = ?", username).First(&user).Error
|
||
if err != nil {
|
||
return "", errors.New("用户名或密码错误")
|
||
}
|
||
|
||
// ② 验证密码(bcrypt)
|
||
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password))
|
||
if err != nil {
|
||
return "", errors.New("用户名或密码错误")
|
||
}
|
||
|
||
// ③ 生成 JWT Token
|
||
claims := jwt.Claims{
|
||
UserID: user.ID,
|
||
Username: user.Username,
|
||
Role: user.Role,
|
||
}
|
||
token := jwt.GenerateToken(claims, jwtSecret, 24*time.Hour)
|
||
|
||
// ④ 记录登录日志
|
||
s.auditService.LogAction(user.ID, "login", "用户登录成功")
|
||
|
||
return token, nil
|
||
// ✅ 完全不需要调用 ctr!
|
||
}
|
||
|
||
// Register 用户注册
|
||
func (s *UserService) Register(username, password, email string) error {
|
||
// ① 检查用户名是否已存在
|
||
var existing model.User
|
||
err := s.store.DB().Where("username = ?", username).First(&existing).Error
|
||
if err == nil {
|
||
return errors.New("用户名已存在")
|
||
}
|
||
|
||
// ② 密码加密(bcrypt)
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// ③ 创建用户
|
||
user := &model.User{
|
||
Username: username,
|
||
Email: email,
|
||
PasswordHash: string(hash),
|
||
Role: "user",
|
||
}
|
||
|
||
err = s.store.DB().Create(user).Error
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// ④ 记录注册日志
|
||
s.auditService.LogAction(user.ID, "register", "用户注册成功")
|
||
|
||
return nil
|
||
// ✅ 完全不需要调用 ctr!
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **4. PolicyService - 策略管理(部分依赖 Ctr)**
|
||
|
||
**职责**:
|
||
- ✅ 9 层传输策略配置
|
||
- ✅ 策略合法性校验
|
||
- ✅ 策略持久化
|
||
- ✅ 调用 Ctr 应用策略
|
||
|
||
**核心方法示例**:
|
||
|
||
```go
|
||
// SetPolicy 设置传输策略
|
||
func (s *PolicyService) SetPolicy(networkID uint64, policy PolicyConfig) error {
|
||
// ① 业务规则校验
|
||
if err := s.validatePolicy(policy); err != nil {
|
||
return fmt.Errorf("策略配置不合法:%w", err)
|
||
}
|
||
|
||
// ② 保存到数据库
|
||
policyModel := &model.Policy{
|
||
NetworkID: networkID,
|
||
Config: policy.ToJSON(),
|
||
UpdatedAt: time.Now(),
|
||
}
|
||
|
||
err := s.store.DB().Save(policyModel).Error
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// ③ 调用 ctr 应用策略(可选,如果网络正在运行)
|
||
if network.IsActive {
|
||
err = s.ctrClient.UpdatePolicy(networkID, policy)
|
||
if err != nil {
|
||
return fmt.Errorf("应用策略失败:%w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// validatePolicy 验证策略合法性(纯业务逻辑)
|
||
func (s *PolicyService) validatePolicy(policy PolicyConfig) error {
|
||
// 检查至少启用了一层
|
||
if !policy.AnyLayerEnabled() {
|
||
return errors.New("至少需要启用一层传输")
|
||
}
|
||
|
||
// 检查优先级顺序
|
||
if !policy.IsValidOrder() {
|
||
return errors.New("传输层优先级顺序不合法")
|
||
}
|
||
|
||
// 检查 STUN/TURN 服务器配置
|
||
if policy.EnableDirectUDP && len(policy.STUNServers) == 0 {
|
||
return errors.New("启用 Direct-UDP 需要配置 STUN 服务器")
|
||
}
|
||
|
||
if policy.EnableTURN && len(policy.TURNServers) == 0 {
|
||
return errors.New("启用 TURN 需要配置 TURN 服务器")
|
||
}
|
||
|
||
return nil
|
||
// ✅ 纯业务逻辑校验,不需要调用 ctr!
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔄 Service 层的通用模式
|
||
|
||
### **标准操作流程**
|
||
|
||
```go
|
||
func (s *XXXService) DoSomething(params Params) (Result, error) {
|
||
// ① 业务规则校验
|
||
if err := s.validate(params); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// ② 数据生成/转换
|
||
data := generateData(params)
|
||
|
||
// ③ 保存到数据库
|
||
err := s.store.DB().Create(&data).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// ④ 调用 ctr 执行(可选,放在事务外)
|
||
err = s.ctrClient.DoSomething(data.ID, data.Config)
|
||
if err != nil {
|
||
// 回滚:删除刚创建的数据
|
||
s.store.DB().Delete(&data)
|
||
return nil, fmt.Errorf("调用 ctr 失败:%w", err)
|
||
}
|
||
|
||
return data, nil
|
||
}
|
||
```
|
||
|
||
### **不调用 Ctr 的场景**
|
||
|
||
以下场景**完全不需要调用 Ctr**:
|
||
|
||
1. ✅ **用户认证**:Login/Register/VerifyToken
|
||
2. ✅ **数据查询**:ListXXX/GetXXX
|
||
3. ✅ **配置生成**:GenerateMeshSeed/GetDeviceConfig
|
||
4. ✅ **策略校验**:ValidatePolicy
|
||
5. ✅ **日志审计**:LogAction/ListAuditLogs
|
||
6. ✅ **统计分析**:GetAuditStats/GetSystemStats
|
||
|
||
---
|
||
|
||
## 📊 Service 与 Ctr 的职责对比
|
||
|
||
| 维度 | Service 层 | Ctr 层 |
|
||
|------|----------|--------|
|
||
| **定位** | 业务逻辑核心 | 实时控制执行器 |
|
||
| **职责** | 业务规则、数据生成、持久化 | 执行 WG/Core 操作 |
|
||
| **依赖数据库** | ✅ 是(直接操作) | ❌ 否(通过参数接收) |
|
||
| **依赖 Ctr** | ⚠️ 部分依赖 | ❌ 不依赖 Service |
|
||
| **主动性** | ✅ 主动发起调用 | ❌ 被动执行 |
|
||
| **可测试性** | ✅ 可 Mock Ctr | ✅ 可独立测试 |
|
||
| **示例方法** | CreateNetwork<br>Login<br>GenerateMeshSeed | CreateDevice<br>AddPeer<br>CreateEngine |
|
||
|
||
---
|
||
|
||
## ✅ 总结
|
||
|
||
### **Service 层的核心价值**
|
||
|
||
1. ✅ **业务逻辑的承载者**
|
||
- 处理所有业务规则
|
||
- 生成和转换数据
|
||
- 持久化到数据库
|
||
|
||
2. ✅ **Ctr 层的调用者**
|
||
- 决定何时调用 Ctr
|
||
- 传递必要的参数
|
||
- 处理 Ctr 的返回结果
|
||
|
||
3. ✅ **前后端的桥梁**
|
||
- 接收 API Handler 的请求
|
||
- 返回处理结果给 Handler
|
||
- 对外暴露完整的业务能力
|
||
|
||
### **设计原则**
|
||
|
||
- ✅ **Service 层是核心**:所有业务逻辑都在这里
|
||
- ✅ **Ctr 层是工具**:只在需要实时控制时调用
|
||
- ✅ **保持解耦**:Service 层可以独立于 Ctr 测试
|
||
- ✅ **事务一致性**:Ctr 失败时需要回滚数据库
|
||
|
||
---
|
||
|
||
*文档版本:v1.0*
|
||
*最后更新:2026-03-24*
|
||
*维护者:MeshRay Team*
|