open_lark/service/group/
mod.rs

1//! 群组(Group)服务
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//! ```rust
46//! use open_lark::prelude::*;
47//!
48//! let client = LarkClient::builder("app_id", "app_secret")
49//!     .with_app_type(AppType::SelfBuild)
50//!     .build();
51//!
52//! // 获取群组服务
53//! let group = &client.group;
54//!
55//! // 创建群组
56//! // let create_request = CreateGroupRequest::builder()
57//! //     .name("项目讨论组")
58//! //     .description("项目开发讨论群组")
59//! //     .members(vec!["user1", "user2", "user3"])
60//! //     .build();
61//! // let new_group = group.v1.group.create(create_request, None).await?;
62//!
63//! // 添加群成员
64//! // let add_member_request = AddGroupMemberRequest::builder()
65//! //     .group_id("group_123")
66//! //     .user_ids(vec!["user4", "user5"])
67//! //     .build();
68//! // group.v1.member.add(add_member_request, None).await?;
69//!
70//! // 发布群公告
71//! // let announcement_request = CreateAnnouncementRequest::builder()
72//! //     .group_id("group_123")
73//! //     .title("重要通知")
74//! //     .content("项目进度更新,请大家查看")
75//! //     .build();
76//! // group.v1.announcement.create(announcement_request, None).await?;
77//!
78//! // 配置群菜单
79//! // let menu_request = SetGroupMenuRequest::builder()
80//! //     .group_id("group_123")
81//! //     .menu_items(vec![
82//! //         MenuItem::new("项目文档", "https://docs.company.com"),
83//! //         MenuItem::new("会议室预约", "https://meeting.company.com")
84//! //     ])
85//! //     .build();
86//! // group.v1.menu.set(menu_request, None).await?;
87//! ```
88//!
89//! # API版本
90//!
91//! 当前支持v1版本,提供完整的群组功能:
92//! - 群组全生命周期管理
93//! - 群成员权限控制
94//! - 群公告发布系统
95//! - 会话标签页定制
96//! - 群菜单配置管理
97//!
98//! # 群组协作特性
99//!
100//! - 👥 多人实时协作
101//! - 📱 跨平台同步支持
102//! - 🔔 智能消息提醒
103//! - 📊 群组数据分析
104//! - 🔐 企业级安全控制
105//!
106//! # 团队管理
107//!
108//! - 🎯 高效的团队沟通
109//! - 📋 项目协作管理
110//! - 📊 团队活跃度监控
111//! - 🏆 团队文化建设
112//! - 📈 协作效率提升
113
114use crate::core::config::Config;
115
116/// 群组服务 v1 版本
117pub mod v1;
118
119/// 群组服务
120///
121/// 企业级群组协作的统一入口,提供群组管理、群成员管理、
122/// 群公告发布、会话标签页、群菜单配置等完整的群组功能。
123///
124/// # 服务架构
125///
126/// - **v1**: 群组API v1版本,提供完整功能集
127///
128/// # 核心特性
129///
130/// - 👥 全面的群组管理功能
131/// - 👑 灵活的权限控制系统
132/// - 📢 专业的公告发布机制
133/// - 🏷️ 个性化标签页配置
134/// - 🍔 自定义群菜单系统
135///
136/// # 适用场景
137///
138/// - 团队项目协作
139/// - 部门内部沟通
140/// - 跨部门工作协调
141/// - 企业公告发布
142/// - 工作流程管理
143///
144/// # 最佳实践
145///
146/// - 合理设置群组权限
147/// - 定期发布群公告
148/// - 优化群菜单配置
149/// - 监控群组活跃度
150/// - 建立群组规范
151pub struct GroupService {
152    /// v1版本API服务
153    pub v1: v1::V1,
154}
155
156impl GroupService {
157    /// 创建新的群组服务实例
158    ///
159    /// # 参数
160    /// - `config`: 客户端配置,包含认证信息和API设置
161    ///
162    /// # 返回值
163    /// 配置完成的群组服务实例
164    pub fn new(config: Config) -> Self {
165        Self {
166            v1: v1::V1::new(config),
167        }
168    }
169}
170
171#[cfg(test)]
172#[allow(unused_variables, unused_unsafe)]
173mod tests {
174    use super::*;
175    use crate::core::config::Config;
176
177    fn create_test_config() -> Config {
178        Config::default()
179    }
180
181    #[test]
182    fn test_group_service_creation() {
183        let config = create_test_config();
184        let group_service = GroupService::new(config);
185
186        // Verify service structure
187    }
188
189    #[test]
190    fn test_group_service_with_custom_config() {
191        let config = Config::builder()
192            .app_id("group_app")
193            .app_secret("group_secret")
194            .req_timeout(std::time::Duration::from_millis(14000))
195            .base_url("https://group.api.com")
196            .build();
197
198        let group_service = GroupService::new(config);
199
200        // Verify service creation with custom config
201    }
202
203    #[test]
204    fn test_group_service_configuration_scenarios() {
205        let test_configs = vec![
206            Config::builder()
207                .app_id("group_basic")
208                .app_secret("basic_secret")
209                .build(),
210            Config::builder()
211                .app_id("group_timeout")
212                .app_secret("timeout_secret")
213                .req_timeout(std::time::Duration::from_millis(16000))
214                .build(),
215            Config::builder()
216                .app_id("group_custom")
217                .app_secret("custom_secret")
218                .base_url("https://custom.group.com")
219                .build(),
220            Config::builder()
221                .app_id("group_enterprise")
222                .app_secret("enterprise_secret")
223                .req_timeout(std::time::Duration::from_millis(30000))
224                .base_url("https://enterprise.group.com")
225                .enable_token_cache(false)
226                .build(),
227        ];
228
229        for config in test_configs {
230            let group_service = GroupService::new(config);
231
232            // Each configuration should create a valid service
233        }
234    }
235
236    #[test]
237    fn test_group_service_multiple_instances() {
238        let config1 = create_test_config();
239        let config2 = Config::builder()
240            .app_id("group2")
241            .app_secret("secret2")
242            .build();
243
244        let group_service1 = GroupService::new(config1);
245        let group_service2 = GroupService::new(config2);
246
247        // Services should be independent instances
248        let service1_ptr = std::ptr::addr_of!(group_service1) as *const _;
249        let service2_ptr = std::ptr::addr_of!(group_service2) as *const _;
250
251        assert_ne!(
252            service1_ptr, service2_ptr,
253            "Services should be independent instances"
254        );
255
256        // Each service should have valid v1 API
257    }
258
259    #[test]
260    fn test_group_service_config_cloning_behavior() {
261        let original_config = create_test_config();
262
263        // Test that the service works with cloned configs
264        let group_service1 = GroupService::new(original_config.clone());
265        let group_service2 = GroupService::new(original_config);
266
267        // Both should work independently
268
269        // But should be different service instances
270        let service1_ptr = std::ptr::addr_of!(group_service1) as *const _;
271        let service2_ptr = std::ptr::addr_of!(group_service2) as *const _;
272        assert_ne!(service1_ptr, service2_ptr);
273    }
274
275    #[test]
276    fn test_group_service_v1_api_structure() {
277        let config = create_test_config();
278        let group_service = GroupService::new(config);
279
280        // Verify that the v1 API is properly structured
281
282        // Test that service maintains proper memory layout
283    }
284
285    #[test]
286    fn test_group_service_v1_chat_service_access() {
287        let config = create_test_config();
288        let group_service = GroupService::new(config);
289
290        // Verify that chat service is accessible and properly configured
291        let chat_ptr = std::ptr::addr_of!(group_service.v1.chat) as *const u8;
292        assert!(
293            !chat_ptr.is_null(),
294            "Chat service should be properly instantiated"
295        );
296    }
297
298    #[test]
299    fn test_group_service_v1_member_service_access() {
300        let config = create_test_config();
301        let group_service = GroupService::new(config);
302
303        // Verify that chat member service is accessible and properly configured
304        let member_ptr = std::ptr::addr_of!(group_service.v1.chat_member) as *const u8;
305        assert!(
306            !member_ptr.is_null(),
307            "Chat member service should be properly instantiated"
308        );
309    }
310
311    #[test]
312    fn test_group_service_v1_announcement_service_access() {
313        let config = create_test_config();
314        let group_service = GroupService::new(config);
315
316        // Verify that chat announcement service is accessible
317        let announcement_ptr = std::ptr::addr_of!(group_service.v1.chat_announcement) as *const u8;
318        assert!(
319            !announcement_ptr.is_null(),
320            "Chat announcement service should be properly instantiated"
321        );
322    }
323
324    #[test]
325    fn test_group_service_v1_tab_service_access() {
326        let config = create_test_config();
327        let group_service = GroupService::new(config);
328
329        // Verify that chat tab service is accessible
330        let tab_ptr = std::ptr::addr_of!(group_service.v1.chat_tab) as *const u8;
331        assert!(
332            !tab_ptr.is_null(),
333            "Chat tab service should be properly instantiated"
334        );
335    }
336
337    #[test]
338    fn test_group_service_v1_menu_tree_service_access() {
339        let config = create_test_config();
340        let group_service = GroupService::new(config);
341
342        // Verify that chat menu tree service is accessible
343        let menu_ptr = std::ptr::addr_of!(group_service.v1.chat_menu_tree) as *const u8;
344        assert!(
345            !menu_ptr.is_null(),
346            "Chat menu tree service should be properly instantiated"
347        );
348    }
349
350    #[test]
351    fn test_group_service_v1_services_independence() {
352        let config = create_test_config();
353        let group_service = GroupService::new(config);
354
355        // Verify that all sub-services are independent instances
356        let chat_ptr = std::ptr::addr_of!(group_service.v1.chat) as *const _;
357        let member_ptr = std::ptr::addr_of!(group_service.v1.chat_member) as *const _;
358        let announcement_ptr = std::ptr::addr_of!(group_service.v1.chat_announcement) as *const _;
359        let tab_ptr = std::ptr::addr_of!(group_service.v1.chat_tab) as *const _;
360        let menu_ptr = std::ptr::addr_of!(group_service.v1.chat_menu_tree) as *const _;
361
362        assert_ne!(
363            chat_ptr, member_ptr,
364            "Chat and member services should be independent"
365        );
366        assert_ne!(
367            chat_ptr, announcement_ptr,
368            "Chat and announcement services should be independent"
369        );
370        assert_ne!(
371            chat_ptr, tab_ptr,
372            "Chat and tab services should be independent"
373        );
374        assert_ne!(
375            chat_ptr, menu_ptr,
376            "Chat and menu services should be independent"
377        );
378        assert_ne!(
379            member_ptr, announcement_ptr,
380            "Member and announcement services should be independent"
381        );
382        assert_ne!(
383            member_ptr, tab_ptr,
384            "Member and tab services should be independent"
385        );
386        assert_ne!(
387            member_ptr, menu_ptr,
388            "Member and menu services should be independent"
389        );
390        assert_ne!(
391            announcement_ptr, tab_ptr,
392            "Announcement and tab services should be independent"
393        );
394        assert_ne!(
395            announcement_ptr, menu_ptr,
396            "Announcement and menu services should be independent"
397        );
398        assert_ne!(
399            tab_ptr, menu_ptr,
400            "Tab and menu services should be independent"
401        );
402    }
403
404    #[test]
405    fn test_group_service_with_different_timeouts() {
406        let fast_config = Config::builder()
407            .app_id("fast_group")
408            .app_secret("fast_secret")
409            .req_timeout(std::time::Duration::from_millis(5000))
410            .build();
411
412        let slow_config = Config::builder()
413            .app_id("slow_group")
414            .app_secret("slow_secret")
415            .req_timeout(std::time::Duration::from_millis(60000))
416            .build();
417
418        let fast_service = GroupService::new(fast_config);
419        let slow_service = GroupService::new(slow_config);
420
421        // Both services should be created successfully regardless of timeout settings
422        let fast_ptr = std::ptr::addr_of!(fast_service) as *const _;
423        let slow_ptr = std::ptr::addr_of!(slow_service) as *const _;
424        assert_ne!(
425            fast_ptr, slow_ptr,
426            "Services with different configs should be independent"
427        );
428    }
429
430    #[test]
431    fn test_group_service_with_different_base_urls() {
432        let dev_config = Config::builder()
433            .app_id("dev_group")
434            .app_secret("dev_secret")
435            .base_url("https://dev.group.api")
436            .build();
437
438        let prod_config = Config::builder()
439            .app_id("prod_group")
440            .app_secret("prod_secret")
441            .base_url("https://api.group.lark.com")
442            .build();
443
444        let dev_service = GroupService::new(dev_config);
445        let prod_service = GroupService::new(prod_config);
446
447        // Both services should be created successfully with different base URLs
448        let dev_ptr = std::ptr::addr_of!(dev_service) as *const _;
449        let prod_ptr = std::ptr::addr_of!(prod_service) as *const _;
450        assert_ne!(
451            dev_ptr, prod_ptr,
452            "Services with different base URLs should be independent"
453        );
454    }
455
456    #[test]
457    fn test_group_service_v1_struct_memory_layout() {
458        let config = create_test_config();
459        let group_service = GroupService::new(config);
460
461        // Test that the V1 struct is properly aligned and accessible
462        let v1_ptr = std::ptr::addr_of!(group_service.v1) as *const u8;
463        assert!(
464            !v1_ptr.is_null(),
465            "V1 service should be properly instantiated"
466        );
467
468        // Verify all services are properly embedded within V1
469        let chat_offset =
470            unsafe { std::ptr::addr_of!(group_service.v1.chat) as usize - v1_ptr as usize };
471        let member_offset =
472            unsafe { std::ptr::addr_of!(group_service.v1.chat_member) as usize - v1_ptr as usize };
473        let announcement_offset = unsafe {
474            std::ptr::addr_of!(group_service.v1.chat_announcement) as usize - v1_ptr as usize
475        };
476        let tab_offset =
477            unsafe { std::ptr::addr_of!(group_service.v1.chat_tab) as usize - v1_ptr as usize };
478        let menu_offset = unsafe {
479            std::ptr::addr_of!(group_service.v1.chat_menu_tree) as usize - v1_ptr as usize
480        };
481
482        // All offsets should be different, indicating proper struct layout
483        let offsets = vec![
484            chat_offset,
485            member_offset,
486            announcement_offset,
487            tab_offset,
488            menu_offset,
489        ];
490        let mut unique_offsets = offsets.clone();
491        unique_offsets.sort();
492        unique_offsets.dedup();
493
494        assert_eq!(
495            offsets.len(),
496            unique_offsets.len(),
497            "All services should have unique memory positions within V1"
498        );
499    }
500
501    #[test]
502    fn test_group_service_config_propagation() {
503        let config = Config::builder()
504            .app_id("config_test")
505            .app_secret("config_secret")
506            .base_url("https://config.test.com")
507            .req_timeout(std::time::Duration::from_millis(45000))
508            .enable_token_cache(false)
509            .build();
510
511        let group_service = GroupService::new(config);
512
513        // All sub-services should be properly instantiated with the config
514        // We can't directly access their configs, but we can verify they exist
515        assert!(!(std::ptr::addr_of!(group_service.v1.chat) as *const u8).is_null());
516        assert!(!(std::ptr::addr_of!(group_service.v1.chat_member) as *const u8).is_null());
517        assert!(!(std::ptr::addr_of!(group_service.v1.chat_announcement) as *const u8).is_null());
518        assert!(!(std::ptr::addr_of!(group_service.v1.chat_tab) as *const u8).is_null());
519        assert!(!(std::ptr::addr_of!(group_service.v1.chat_menu_tree) as *const u8).is_null());
520    }
521}