# 使用 Ex 字段区分群类型方案

## 一、方案概述

使用群组的 `Ex` 扩展字段来区分不同类型的群，在客户端根据 `Ex` 字段控制功能展示，服务端保持统一的验证逻辑。

## 二、Ex 字段格式建议

### 2.1 JSON 格式

```json
{
  "groupCategory": "super",  // 群类型：super(超级群)、normal(普通群)、work(工作群)
  "features": {
    "allowLink": true,        // 是否允许发送链接
    "allowQRCode": true,      // 是否允许发送二维码
    "showMemberList": true,   // 是否显示成员列表
    "showAdminPanel": false   // 是否显示管理面板
  },
  "custom": {}                // 其他自定义字段
}
```

### 2.2 简化格式（如果只需要区分类型）

```json
{
  "type": "super"  // super, normal, work
}
```

## 三、实现方式

### 3.1 创建群组时设置 Ex

**创建群组时**，在 `GroupInfo.Ex` 中设置群类型：

```go
// 创建超级群
groupInfo := &sdkws.GroupInfo{
    GroupName: "超级群",
    Ex: `{"type":"super"}`,
    // ... 其他字段
}

// 创建普通群
groupInfo := &sdkws.GroupInfo{
    GroupName: "普通群",
    Ex: `{"type":"normal"}`,
    // ... 其他字段
}
```

### 3.2 客户端解析 Ex 字段

**客户端获取群信息后**，解析 `Ex` 字段：

```javascript
// 示例：JavaScript/TypeScript
function getGroupType(groupInfo) {
    try {
        const ex = JSON.parse(groupInfo.Ex || '{}');
        return ex.type || 'normal';  // 默认为普通群
    } catch (e) {
        return 'normal';
    }
}

// 根据群类型控制功能显示
function shouldShowFeature(groupInfo, feature) {
    const type = getGroupType(groupInfo);
    
    switch (feature) {
        case 'memberList':
            // 超级群可能不显示成员列表
            return type !== 'super';
        case 'adminPanel':
            // 只有工作群显示管理面板
            return type === 'work';
        case 'linkMessage':
            // 根据类型决定是否允许发送链接
            return type !== 'super';  // 超级群不允许发送链接
        default:
            return true;
    }
}
```

### 3.3 更新群信息时保持 Ex 字段

**使用 `SetGroupInfoEx` 接口更新群信息时**，可以更新 `Ex` 字段：

```go
// 更新群扩展信息
req := &pbgroup.SetGroupInfoExReq{
    GroupID: groupID,
    Ex: &wrapperspb.StringValue{
        Value: `{"type":"super","features":{"allowLink":false}}`,
    },
}
```

## 四、优势

### 4.1 服务端优势

1. **统一验证逻辑**：所有群组都使用 `WorkingGroup` 的完整验证，安全可靠
2. **无需修改代码**：不需要修改服务端的验证、推送等逻辑
3. **向后兼容**：不影响现有群组，新群组通过 Ex 字段区分
4. **灵活扩展**：Ex 字段可以存储任意 JSON 数据，便于后续扩展

### 4.2 客户端优势

1. **完全可控**：客户端可以根据业务需求灵活控制功能展示
2. **易于实现**：只需要解析 JSON 并做条件判断
3. **不影响现有功能**：对没有 Ex 字段或 Ex 字段格式不正确的群组，可以设置默认行为

## 五、注意事项

### 5.1 Ex 字段格式

- **建议使用 JSON 格式**：便于解析和扩展
- **字段命名规范**：使用小驼峰或下划线命名
- **版本兼容**：如果后续需要修改格式，考虑版本号字段

### 5.2 默认值处理

- **客户端**：如果 Ex 字段为空或解析失败，应设置合理的默认值
- **服务端**：创建群组时，如果没有设置 Ex，可以设置默认值

### 5.3 数据一致性

- **更新 Ex 字段时**：确保 JSON 格式正确
- **验证**：客户端解析 Ex 字段时，应该处理 JSON 解析错误

## 六、示例代码

### 6.1 Go 服务端示例

```go
// 创建群组时设置 Ex
func createSuperGroup(req *pbgroup.CreateGroupReq) {
    req.GroupInfo.Ex = `{"type":"super","features":{"allowLink":false}}`
    // ... 创建群组
}

// 解析 Ex 字段（如果需要）
func parseGroupEx(ex string) map[string]interface{} {
    var result map[string]interface{}
    if err := json.Unmarshal([]byte(ex), &result); err != nil {
        return make(map[string]interface{})
    }
    return result
}
```

### 6.2 客户端示例（React/TypeScript）

```typescript
interface GroupEx {
    type?: 'super' | 'normal' | 'work';
    features?: {
        allowLink?: boolean;
        allowQRCode?: boolean;
        showMemberList?: boolean;
    };
}

function parseGroupEx(ex: string): GroupEx {
    try {
        return JSON.parse(ex || '{}') as GroupEx;
    } catch {
        return { type: 'normal' };
    }
}

function useGroupFeatures(groupInfo: GroupInfo) {
    const ex = parseGroupEx(groupInfo.Ex);
    
    return {
        canSendLink: ex.features?.allowLink !== false,
        canSendQRCode: ex.features?.allowQRCode !== false,
        showMemberList: ex.features?.showMemberList !== false,
        isSuperGroup: ex.type === 'super',
    };
}
```

## 七、总结

使用 `Ex` 字段区分群类型是一个**保守、安全、灵活**的方案：

- ✅ **服务端**：保持统一逻辑，无需修改验证和推送代码
- ✅ **客户端**：完全可控，根据业务需求灵活展示功能
- ✅ **扩展性**：Ex 字段可以存储任意扩展信息
- ✅ **兼容性**：不影响现有群组和功能

这个方案避免了修改服务端核心逻辑带来的风险，同时提供了足够的灵活性来满足不同的业务需求。










