open_lark/service/cloud_docs/assistant/v1/subscription/
get.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    service::cloud_docs::assistant::v1::subscription::SubscriptionService,
17};
18
19/// 获取订阅状态请求
20#[derive(Debug, Serialize, Default, Clone)]
21pub struct GetSubscriptionRequest {
22    #[serde(skip)]
23    api_request: ApiRequest,
24    /// 文档token
25    #[serde(skip)]
26    file_token: String,
27    /// 文档类型
28    #[serde(skip)]
29    file_type: String,
30}
31
32impl GetSubscriptionRequest {
33    pub fn builder() -> GetSubscriptionRequestBuilder {
34        GetSubscriptionRequestBuilder::default()
35    }
36
37    pub fn new(file_token: impl ToString, file_type: FileType) -> Self {
38        Self {
39            file_token: file_token.to_string(),
40            file_type: file_type.to_string(),
41            ..Default::default()
42        }
43    }
44}
45
46#[derive(Default)]
47pub struct GetSubscriptionRequestBuilder {
48    request: GetSubscriptionRequest,
49}
50
51impl GetSubscriptionRequestBuilder {
52    /// 文档token
53    pub fn file_token(mut self, token: impl ToString) -> Self {
54        self.request.file_token = token.to_string();
55        self
56    }
57
58    /// 文档类型
59    pub fn file_type(mut self, file_type: FileType) -> Self {
60        self.request.file_type = file_type.to_string();
61        self
62    }
63
64    /// 设置为多维表格
65    pub fn as_bitable(mut self) -> Self {
66        self.request.file_type = FileType::Bitable.to_string();
67        self
68    }
69
70    /// 设置为文档
71    pub fn as_doc(mut self) -> Self {
72        self.request.file_type = FileType::Doc.to_string();
73        self
74    }
75
76    /// 设置为表格
77    pub fn as_sheet(mut self) -> Self {
78        self.request.file_type = FileType::Sheet.to_string();
79        self
80    }
81
82    /// 设置为Wiki
83    pub fn as_wiki(mut self) -> Self {
84        self.request.file_type = FileType::Wiki.to_string();
85        self
86    }
87
88    pub fn build(mut self) -> GetSubscriptionRequest {
89        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
90        self.request
91    }
92}
93
94impl_executable_builder_owned!(
95    GetSubscriptionRequestBuilder,
96    SubscriptionService,
97    GetSubscriptionRequest,
98    GetSubscriptionResponse,
99    get
100);
101
102/// 文档类型
103#[derive(Debug, Clone)]
104pub enum FileType {
105    /// 多维表格
106    Bitable,
107    /// 文档
108    Doc,
109    /// 电子表格
110    Sheet,
111    /// Wiki
112    Wiki,
113}
114
115impl std::fmt::Display for FileType {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        match self {
118            FileType::Bitable => write!(f, "bitable"),
119            FileType::Doc => write!(f, "doc"),
120            FileType::Sheet => write!(f, "sheet"),
121            FileType::Wiki => write!(f, "wiki"),
122        }
123    }
124}
125
126impl FileType {
127    /// 获取文档类型的中文名称
128    pub fn chinese_name(&self) -> &'static str {
129        match self {
130            FileType::Bitable => "多维表格",
131            FileType::Doc => "文档",
132            FileType::Sheet => "电子表格",
133            FileType::Wiki => "Wiki",
134        }
135    }
136
137    /// 是否支持助手功能
138    pub fn supports_assistant(&self) -> bool {
139        match self {
140            FileType::Bitable => true,
141            FileType::Doc => true,
142            FileType::Sheet => true,
143            FileType::Wiki => true,
144        }
145    }
146}
147
148/// 订阅状态
149#[derive(Debug, Serialize, Deserialize, Clone)]
150#[serde(rename_all = "snake_case")]
151pub enum SubscriptionStatus {
152    /// 已订阅
153    Subscribed,
154    /// 未订阅
155    Unsubscribed,
156    /// 已暂停
157    Paused,
158    /// 已取消
159    Cancelled,
160    /// 未知状态
161    #[serde(other)]
162    Unknown,
163}
164
165impl SubscriptionStatus {
166    /// 是否为活跃状态
167    pub fn is_active(&self) -> bool {
168        matches!(self, SubscriptionStatus::Subscribed)
169    }
170
171    /// 是否可以激活
172    pub fn can_activate(&self) -> bool {
173        matches!(
174            self,
175            SubscriptionStatus::Unsubscribed | SubscriptionStatus::Paused
176        )
177    }
178
179    /// 获取状态描述
180    pub fn description(&self) -> &'static str {
181        match self {
182            SubscriptionStatus::Subscribed => "已订阅",
183            SubscriptionStatus::Unsubscribed => "未订阅",
184            SubscriptionStatus::Paused => "已暂停",
185            SubscriptionStatus::Cancelled => "已取消",
186            SubscriptionStatus::Unknown => "未知状态",
187        }
188    }
189
190    /// 获取状态颜色(用于UI显示)
191    pub fn color(&self) -> &'static str {
192        match self {
193            SubscriptionStatus::Subscribed => "green",
194            SubscriptionStatus::Unsubscribed => "gray",
195            SubscriptionStatus::Paused => "orange",
196            SubscriptionStatus::Cancelled => "red",
197            SubscriptionStatus::Unknown => "gray",
198        }
199    }
200}
201
202/// 订阅详情
203#[derive(Debug, Deserialize, Clone)]
204pub struct SubscriptionDetail {
205    /// 订阅状态
206    pub status: SubscriptionStatus,
207    /// 订阅开始时间(时间戳)
208    pub start_time: Option<i64>,
209    /// 订阅结束时间(时间戳)
210    pub end_time: Option<i64>,
211    /// 最后更新时间(时间戳)
212    pub last_update_time: Option<i64>,
213    /// 订阅者ID
214    pub subscriber_id: Option<String>,
215    /// 订阅者类型
216    pub subscriber_type: Option<String>,
217    /// 订阅配置
218    pub config: Option<serde_json::Value>,
219    /// 扩展信息
220    pub extra: Option<serde_json::Value>,
221}
222
223/// 获取订阅状态响应
224#[derive(Debug, Deserialize)]
225pub struct GetSubscriptionResponse {
226    /// 订阅详情
227    pub subscription: SubscriptionDetail,
228    /// 文档token
229    pub file_token: String,
230    /// 文档类型
231    pub file_type: String,
232}
233
234impl ApiResponseTrait for GetSubscriptionResponse {
235    fn data_format() -> ResponseFormat {
236        ResponseFormat::Data
237    }
238}
239
240/// 获取订阅状态
241pub async fn get_subscription(
242    request: GetSubscriptionRequest,
243    config: &Config,
244    option: Option<RequestOption>,
245) -> SDKResult<BaseResponse<GetSubscriptionResponse>> {
246    let mut api_req = request.api_request;
247    api_req.http_method = Method::GET;
248
249    api_req.api_path = ASSISTANT_V1_FILE_SUBSCRIPTION
250        .replace("{}", &request.file_type)
251        .replace("{}", &request.file_token);
252
253    api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
254
255    let api_resp = Transport::request(api_req, config, option).await?;
256    Ok(api_resp)
257}
258
259impl SubscriptionDetail {
260    /// 是否已订阅
261    pub fn is_subscribed(&self) -> bool {
262        self.status.is_active()
263    }
264
265    /// 是否可以激活订阅
266    pub fn can_activate(&self) -> bool {
267        self.status.can_activate()
268    }
269
270    /// 获取订阅持续时间(秒)
271    pub fn duration_seconds(&self) -> Option<i64> {
272        match (self.start_time, self.end_time) {
273            (Some(start), Some(end)) => Some(end - start),
274            _ => None,
275        }
276    }
277
278    /// 获取订阅持续时间(天)
279    pub fn duration_days(&self) -> Option<f64> {
280        self.duration_seconds().map(|s| s as f64 / 86400.0)
281    }
282
283    /// 获取订阅时间格式化字符串
284    pub fn start_time_formatted(&self) -> Option<String> {
285        self.start_time.map(|timestamp| {
286            let datetime =
287                chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(chrono::Utc::now);
288            datetime.format("%Y-%m-%d %H:%M:%S").to_string()
289        })
290    }
291
292    /// 获取结束时间格式化字符串
293    pub fn end_time_formatted(&self) -> Option<String> {
294        self.end_time.map(|timestamp| {
295            let datetime =
296                chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(chrono::Utc::now);
297            datetime.format("%Y-%m-%d %H:%M:%S").to_string()
298        })
299    }
300
301    /// 获取最后更新时间格式化字符串
302    pub fn last_update_time_formatted(&self) -> Option<String> {
303        self.last_update_time.map(|timestamp| {
304            let datetime =
305                chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(chrono::Utc::now);
306            datetime.format("%Y-%m-%d %H:%M:%S").to_string()
307        })
308    }
309
310    /// 获取订阅摘要
311    pub fn summary(&self) -> String {
312        let mut parts = Vec::new();
313
314        parts.push(format!("状态: {}", self.status.description()));
315
316        if let Some(start) = self.start_time_formatted() {
317            parts.push(format!("开始时间: {start}"));
318        }
319
320        if let Some(end) = self.end_time_formatted() {
321            parts.push(format!("结束时间: {end}"));
322        }
323
324        if let Some(days) = self.duration_days() {
325            parts.push(format!("持续时间: {days:.1} 天"));
326        }
327
328        if let Some(ref subscriber_id) = self.subscriber_id {
329            parts.push(format!("订阅者: {subscriber_id}"));
330        }
331
332        parts.join(" | ")
333    }
334}
335
336impl GetSubscriptionResponse {
337    /// 获取文档类型枚举
338    pub fn file_type_enum(&self) -> FileType {
339        match self.file_type.as_str() {
340            "bitable" => FileType::Bitable,
341            "doc" => FileType::Doc,
342            "sheet" => FileType::Sheet,
343            "wiki" => FileType::Wiki,
344            _ => FileType::Doc, // 默认为文档
345        }
346    }
347
348    /// 获取完整信息摘要
349    pub fn info_summary(&self) -> String {
350        format!(
351            "{} ({}) - {}",
352            self.file_type_enum().chinese_name(),
353            self.file_token,
354            self.subscription.summary()
355        )
356    }
357}
358
359#[cfg(test)]
360#[allow(unused_variables, unused_unsafe)]
361mod tests {
362    use super::*;
363
364    #[test]
365    fn test_get_subscription_request_builder() {
366        let request = GetSubscriptionRequest::builder()
367            .file_token("doccnxxxxxx")
368            .as_doc()
369            .build();
370
371        assert_eq!(request.file_token, "doccnxxxxxx");
372        assert_eq!(request.file_type, "doc");
373    }
374
375    #[test]
376    fn test_file_type_methods() {
377        assert_eq!(FileType::Doc.to_string(), "doc");
378        assert_eq!(FileType::Doc.chinese_name(), "文档");
379        assert!(FileType::Doc.supports_assistant());
380    }
381
382    #[test]
383    fn test_subscription_status_methods() {
384        assert!(SubscriptionStatus::Subscribed.is_active());
385        assert!(!SubscriptionStatus::Unsubscribed.is_active());
386        assert!(SubscriptionStatus::Unsubscribed.can_activate());
387        assert!(!SubscriptionStatus::Subscribed.can_activate());
388
389        assert_eq!(SubscriptionStatus::Subscribed.description(), "已订阅");
390        assert_eq!(SubscriptionStatus::Subscribed.color(), "green");
391    }
392
393    #[test]
394    fn test_subscription_detail_methods() {
395        let detail = SubscriptionDetail {
396            status: SubscriptionStatus::Subscribed,
397            start_time: Some(1700000000),
398            end_time: Some(1700086400),
399            last_update_time: Some(1700043200),
400            subscriber_id: Some("user123".to_string()),
401            subscriber_type: Some("user".to_string()),
402            config: None,
403            extra: None,
404        };
405
406        assert!(detail.is_subscribed());
407        assert!(!detail.can_activate());
408        assert_eq!(detail.duration_seconds(), Some(86400));
409        assert_eq!(detail.duration_days(), Some(1.0));
410    }
411}