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