open_lark/service/cloud_docs/assistant/v1/subscription/
create.rs

1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    core::{
6        api_req::ApiRequest,
7        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8        config::Config,
9        constants::AccessTokenType,
10        http::Transport,
11        req_option::RequestOption,
12        SDKResult,
13    },
14    impl_executable_builder_owned,
15};
16
17use super::{
18    get::{FileType, SubscriptionDetail},
19    SubscriptionService,
20};
21
22/// 创建订阅请求
23#[derive(Debug, Serialize, Default, Clone)]
24pub struct CreateSubscriptionRequest {
25    #[serde(skip)]
26    api_request: ApiRequest,
27    /// 文档token
28    #[serde(skip)]
29    file_token: String,
30    /// 文档类型
31    #[serde(skip)]
32    file_type: String,
33    /// 订阅配置
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub config: Option<SubscriptionConfig>,
36    /// 扩展信息
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub extra: Option<serde_json::Value>,
39}
40
41/// 订阅配置
42#[derive(Debug, Serialize, Deserialize, Clone)]
43pub struct SubscriptionConfig {
44    /// 是否启用实时通知
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub enable_notification: Option<bool>,
47    /// 通知频率(秒)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub notification_interval: Option<i32>,
50    /// 订阅优先级
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub priority: Option<SubscriptionPriority>,
53    /// 自动续费
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub auto_renew: Option<bool>,
56    /// 订阅标签
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub tags: Option<Vec<String>>,
59    /// 自定义属性
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub custom_properties: Option<serde_json::Value>,
62}
63
64/// 订阅优先级
65#[derive(Debug, Serialize, Deserialize, Clone)]
66#[serde(rename_all = "snake_case")]
67pub enum SubscriptionPriority {
68    /// 低优先级
69    Low,
70    /// 普通优先级
71    Normal,
72    /// 高优先级
73    High,
74    /// 紧急优先级
75    Urgent,
76}
77
78impl CreateSubscriptionRequest {
79    pub fn builder() -> CreateSubscriptionRequestBuilder {
80        CreateSubscriptionRequestBuilder::default()
81    }
82
83    pub fn new(file_token: impl ToString, file_type: FileType) -> Self {
84        Self {
85            file_token: file_token.to_string(),
86            file_type: file_type.to_string(),
87            ..Default::default()
88        }
89    }
90}
91
92#[derive(Default)]
93pub struct CreateSubscriptionRequestBuilder {
94    request: CreateSubscriptionRequest,
95}
96
97impl CreateSubscriptionRequestBuilder {
98    /// 文档token
99    pub fn file_token(mut self, token: impl ToString) -> Self {
100        self.request.file_token = token.to_string();
101        self
102    }
103
104    /// 文档类型
105    pub fn file_type(mut self, file_type: FileType) -> Self {
106        self.request.file_type = file_type.to_string();
107        self
108    }
109
110    /// 设置为多维表格
111    pub fn as_bitable(mut self) -> Self {
112        self.request.file_type = FileType::Bitable.to_string();
113        self
114    }
115
116    /// 设置为文档
117    pub fn as_doc(mut self) -> Self {
118        self.request.file_type = FileType::Doc.to_string();
119        self
120    }
121
122    /// 设置为表格
123    pub fn as_sheet(mut self) -> Self {
124        self.request.file_type = FileType::Sheet.to_string();
125        self
126    }
127
128    /// 设置为Wiki
129    pub fn as_wiki(mut self) -> Self {
130        self.request.file_type = FileType::Wiki.to_string();
131        self
132    }
133
134    /// 设置订阅配置
135    pub fn config(mut self, config: SubscriptionConfig) -> Self {
136        self.request.config = Some(config);
137        self
138    }
139
140    /// 启用实时通知
141    pub fn with_notification(mut self, enable: bool) -> Self {
142        let mut config = self.request.config.unwrap_or_default();
143        config.enable_notification = Some(enable);
144        self.request.config = Some(config);
145        self
146    }
147
148    /// 设置通知频率(秒)
149    pub fn notification_interval(mut self, interval: i32) -> Self {
150        let mut config = self.request.config.unwrap_or_default();
151        config.notification_interval = Some(interval.max(1));
152        self.request.config = Some(config);
153        self
154    }
155
156    /// 设置订阅优先级
157    pub fn priority(mut self, priority: SubscriptionPriority) -> Self {
158        let mut config = self.request.config.unwrap_or_default();
159        config.priority = Some(priority);
160        self.request.config = Some(config);
161        self
162    }
163
164    /// 设置为高优先级
165    pub fn high_priority(self) -> Self {
166        self.priority(SubscriptionPriority::High)
167    }
168
169    /// 设置为低优先级
170    pub fn low_priority(self) -> Self {
171        self.priority(SubscriptionPriority::Low)
172    }
173
174    /// 启用自动续费
175    pub fn auto_renew(mut self, enable: bool) -> Self {
176        let mut config = self.request.config.unwrap_or_default();
177        config.auto_renew = Some(enable);
178        self.request.config = Some(config);
179        self
180    }
181
182    /// 添加标签
183    pub fn add_tag(mut self, tag: impl ToString) -> Self {
184        let mut config = self.request.config.unwrap_or_default();
185        let mut tags = config.tags.unwrap_or_default();
186        tags.push(tag.to_string());
187        config.tags = Some(tags);
188        self.request.config = Some(config);
189        self
190    }
191
192    /// 添加多个标签
193    pub fn add_tags(mut self, tags: Vec<String>) -> Self {
194        let mut config = self.request.config.unwrap_or_default();
195        let mut existing_tags = config.tags.unwrap_or_default();
196        existing_tags.extend(tags);
197        config.tags = Some(existing_tags);
198        self.request.config = Some(config);
199        self
200    }
201
202    /// 设置扩展信息
203    pub fn extra(mut self, extra: serde_json::Value) -> Self {
204        self.request.extra = Some(extra);
205        self
206    }
207
208    /// 快速创建基础订阅
209    pub fn basic_subscription(mut self) -> Self {
210        let config = SubscriptionConfig {
211            enable_notification: Some(true),
212            notification_interval: Some(3600), // 1小时
213            priority: Some(SubscriptionPriority::Normal),
214            auto_renew: Some(false),
215            tags: Some(vec!["default".to_string()]),
216            custom_properties: None,
217        };
218        self.request.config = Some(config);
219        self
220    }
221
222    /// 快速创建高级订阅
223    pub fn premium_subscription(mut self) -> Self {
224        let config = SubscriptionConfig {
225            enable_notification: Some(true),
226            notification_interval: Some(300), // 5分钟
227            priority: Some(SubscriptionPriority::High),
228            auto_renew: Some(true),
229            tags: Some(vec!["premium".to_string(), "priority".to_string()]),
230            custom_properties: None,
231        };
232        self.request.config = Some(config);
233        self
234    }
235
236    pub fn build(mut self) -> CreateSubscriptionRequest {
237        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
238        self.request
239    }
240}
241
242impl_executable_builder_owned!(
243    CreateSubscriptionRequestBuilder,
244    SubscriptionService,
245    CreateSubscriptionRequest,
246    CreateSubscriptionResponse,
247    create
248);
249
250/// 创建订阅响应
251#[derive(Debug, Deserialize)]
252pub struct CreateSubscriptionResponse {
253    /// 订阅详情
254    pub subscription: SubscriptionDetail,
255    /// 文档token
256    pub file_token: String,
257    /// 文档类型
258    pub file_type: String,
259    /// 创建时间
260    pub create_time: Option<i64>,
261    /// 订阅ID
262    pub subscription_id: Option<String>,
263}
264
265impl ApiResponseTrait for CreateSubscriptionResponse {
266    fn data_format() -> ResponseFormat {
267        ResponseFormat::Data
268    }
269}
270
271/// 创建订阅
272pub async fn create_subscription(
273    request: CreateSubscriptionRequest,
274    config: &Config,
275    option: Option<RequestOption>,
276) -> SDKResult<BaseResponse<CreateSubscriptionResponse>> {
277    let mut api_req = request.api_request;
278    api_req.http_method = Method::POST;
279
280    api_req.api_path = format!(
281        "/open-apis/assistant/v1/file/{}/{}/subscription",
282        request.file_type, request.file_token
283    );
284
285    api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
286
287    let api_resp = Transport::request(api_req, config, option).await?;
288    Ok(api_resp)
289}
290
291impl Default for SubscriptionConfig {
292    fn default() -> Self {
293        Self {
294            enable_notification: Some(true),
295            notification_interval: Some(3600),
296            priority: Some(SubscriptionPriority::Normal),
297            auto_renew: Some(false),
298            tags: None,
299            custom_properties: None,
300        }
301    }
302}
303
304impl SubscriptionPriority {
305    /// 获取优先级描述
306    pub fn description(&self) -> &'static str {
307        match self {
308            SubscriptionPriority::Low => "低优先级",
309            SubscriptionPriority::Normal => "普通优先级",
310            SubscriptionPriority::High => "高优先级",
311            SubscriptionPriority::Urgent => "紧急优先级",
312        }
313    }
314
315    /// 获取优先级数值(用于排序)
316    pub fn value(&self) -> u8 {
317        match self {
318            SubscriptionPriority::Low => 1,
319            SubscriptionPriority::Normal => 2,
320            SubscriptionPriority::High => 3,
321            SubscriptionPriority::Urgent => 4,
322        }
323    }
324
325    /// 获取优先级颜色
326    pub fn color(&self) -> &'static str {
327        match self {
328            SubscriptionPriority::Low => "gray",
329            SubscriptionPriority::Normal => "blue",
330            SubscriptionPriority::High => "orange",
331            SubscriptionPriority::Urgent => "red",
332        }
333    }
334}
335
336impl SubscriptionConfig {
337    /// 是否启用了实时通知
338    pub fn has_notification(&self) -> bool {
339        self.enable_notification.unwrap_or(false)
340    }
341
342    /// 获取通知频率(秒)
343    pub fn get_notification_interval(&self) -> i32 {
344        self.notification_interval.unwrap_or(3600)
345    }
346
347    /// 获取通知频率(分钟)
348    pub fn get_notification_interval_minutes(&self) -> f64 {
349        self.get_notification_interval() as f64 / 60.0
350    }
351
352    /// 获取通知频率(小时)
353    pub fn get_notification_interval_hours(&self) -> f64 {
354        self.get_notification_interval() as f64 / 3600.0
355    }
356
357    /// 是否为高频通知(小于1小时)
358    pub fn is_high_frequency(&self) -> bool {
359        self.get_notification_interval() < 3600
360    }
361
362    /// 获取优先级
363    pub fn get_priority(&self) -> SubscriptionPriority {
364        self.priority
365            .clone()
366            .unwrap_or(SubscriptionPriority::Normal)
367    }
368
369    /// 是否启用自动续费
370    pub fn has_auto_renew(&self) -> bool {
371        self.auto_renew.unwrap_or(false)
372    }
373
374    /// 获取标签列表
375    pub fn get_tags(&self) -> Vec<String> {
376        self.tags.clone().unwrap_or_default()
377    }
378
379    /// 是否包含指定标签
380    pub fn has_tag(&self, tag: &str) -> bool {
381        self.get_tags().contains(&tag.to_string())
382    }
383
384    /// 获取配置摘要
385    pub fn summary(&self) -> String {
386        let mut parts = Vec::new();
387
388        parts.push(format!("优先级: {}", self.get_priority().description()));
389
390        if self.has_notification() {
391            let interval = self.get_notification_interval_hours();
392            if interval < 1.0 {
393                parts.push(format!("通知: 每{:.0}分钟", interval * 60.0));
394            } else {
395                parts.push(format!("通知: 每{interval:.1}小时"));
396            }
397        } else {
398            parts.push("通知: 已禁用".to_string());
399        }
400
401        if self.has_auto_renew() {
402            parts.push("自动续费: 是".to_string());
403        }
404
405        let tags = self.get_tags();
406        if !tags.is_empty() {
407            parts.push(format!("标签: {}", tags.join(", ")));
408        }
409
410        parts.join(" | ")
411    }
412}
413
414impl CreateSubscriptionResponse {
415    /// 获取文档类型枚举
416    pub fn file_type_enum(&self) -> FileType {
417        match self.file_type.as_str() {
418            "bitable" => FileType::Bitable,
419            "doc" => FileType::Doc,
420            "sheet" => FileType::Sheet,
421            "wiki" => FileType::Wiki,
422            _ => FileType::Doc,
423        }
424    }
425
426    /// 获取创建时间格式化字符串
427    pub fn create_time_formatted(&self) -> Option<String> {
428        self.create_time.map(|timestamp| {
429            let datetime =
430                chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(chrono::Utc::now);
431            datetime.format("%Y-%m-%d %H:%M:%S").to_string()
432        })
433    }
434
435    /// 获取完整信息摘要
436    pub fn info_summary(&self) -> String {
437        let mut parts = vec![
438            format!(
439                "{} ({})",
440                self.file_type_enum().chinese_name(),
441                self.file_token
442            ),
443            self.subscription.summary(),
444        ];
445
446        if let Some(ref subscription_id) = self.subscription_id {
447            parts.push(format!("订阅ID: {subscription_id}"));
448        }
449
450        if let Some(create_time) = self.create_time_formatted() {
451            parts.push(format!("创建时间: {create_time}"));
452        }
453
454        parts.join(" | ")
455    }
456}
457
458#[cfg(test)]
459mod tests {
460    use super::*;
461
462    #[test]
463    fn test_create_subscription_request_builder() {
464        let request = CreateSubscriptionRequest::builder()
465            .file_token("doccnxxxxxx")
466            .as_doc()
467            .basic_subscription()
468            .build();
469
470        assert_eq!(request.file_token, "doccnxxxxxx");
471        assert_eq!(request.file_type, "doc");
472        assert!(request.config.is_some());
473    }
474
475    #[test]
476    fn test_subscription_priority_methods() {
477        assert_eq!(SubscriptionPriority::High.description(), "高优先级");
478        assert_eq!(SubscriptionPriority::High.value(), 3);
479        assert_eq!(SubscriptionPriority::High.color(), "orange");
480    }
481
482    #[test]
483    fn test_subscription_config_methods() {
484        let config = SubscriptionConfig::default();
485
486        assert!(config.has_notification());
487        assert_eq!(config.get_notification_interval(), 3600);
488        assert_eq!(config.get_notification_interval_hours(), 1.0);
489        assert!(!config.is_high_frequency());
490        assert!(!config.has_auto_renew());
491    }
492
493    #[test]
494    fn test_subscription_config_builder() {
495        let request = CreateSubscriptionRequest::builder()
496            .file_token("test")
497            .as_doc()
498            .high_priority()
499            .notification_interval(300)
500            .auto_renew(true)
501            .add_tag("important")
502            .build();
503
504        let config = request.config.unwrap();
505        assert_eq!(config.get_priority().value(), 3);
506        assert_eq!(config.get_notification_interval(), 300);
507        assert!(config.has_auto_renew());
508        assert!(config.has_tag("important"));
509    }
510}