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        http::Transport,
11        req_option::RequestOption,
12        SDKResult,
13    },
14    impl_executable_builder_owned,
15    service::cloud_docs::assistant::v1::subscription::SubscriptionService,
16};
17
18/// 获取订阅状态请求
19#[derive(Debug, Serialize, Default, Clone)]
20pub struct GetSubscriptionRequest {
21    #[serde(skip)]
22    api_request: ApiRequest,
23    /// 文档token
24    #[serde(skip)]
25    file_token: String,
26    /// 文档类型
27    #[serde(skip)]
28    file_type: String,
29}
30
31impl GetSubscriptionRequest {
32    pub fn builder() -> GetSubscriptionRequestBuilder {
33        GetSubscriptionRequestBuilder::default()
34    }
35
36    pub fn new(file_token: impl ToString, file_type: FileType) -> Self {
37        Self {
38            file_token: file_token.to_string(),
39            file_type: file_type.to_string(),
40            ..Default::default()
41        }
42    }
43}
44
45#[derive(Default)]
46pub struct GetSubscriptionRequestBuilder {
47    request: GetSubscriptionRequest,
48}
49
50impl GetSubscriptionRequestBuilder {
51    /// 文档token
52    pub fn file_token(mut self, token: impl ToString) -> Self {
53        self.request.file_token = token.to_string();
54        self
55    }
56
57    /// 文档类型
58    pub fn file_type(mut self, file_type: FileType) -> Self {
59        self.request.file_type = file_type.to_string();
60        self
61    }
62
63    /// 设置为多维表格
64    pub fn as_bitable(mut self) -> Self {
65        self.request.file_type = FileType::Bitable.to_string();
66        self
67    }
68
69    /// 设置为文档
70    pub fn as_doc(mut self) -> Self {
71        self.request.file_type = FileType::Doc.to_string();
72        self
73    }
74
75    /// 设置为表格
76    pub fn as_sheet(mut self) -> Self {
77        self.request.file_type = FileType::Sheet.to_string();
78        self
79    }
80
81    /// 设置为Wiki
82    pub fn as_wiki(mut self) -> Self {
83        self.request.file_type = FileType::Wiki.to_string();
84        self
85    }
86
87    pub fn build(mut self) -> GetSubscriptionRequest {
88        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
89        self.request
90    }
91}
92
93impl_executable_builder_owned!(
94    GetSubscriptionRequestBuilder,
95    SubscriptionService,
96    GetSubscriptionRequest,
97    GetSubscriptionResponse,
98    get
99);
100
101/// 文档类型
102#[derive(Debug, Clone)]
103pub enum FileType {
104    /// 多维表格
105    Bitable,
106    /// 文档
107    Doc,
108    /// 电子表格
109    Sheet,
110    /// Wiki
111    Wiki,
112}
113
114impl std::fmt::Display for FileType {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        match self {
117            FileType::Bitable => write!(f, "bitable"),
118            FileType::Doc => write!(f, "doc"),
119            FileType::Sheet => write!(f, "sheet"),
120            FileType::Wiki => write!(f, "wiki"),
121        }
122    }
123}
124
125impl FileType {
126    /// 获取文档类型的中文名称
127    pub fn chinese_name(&self) -> &'static str {
128        match self {
129            FileType::Bitable => "多维表格",
130            FileType::Doc => "文档",
131            FileType::Sheet => "电子表格",
132            FileType::Wiki => "Wiki",
133        }
134    }
135
136    /// 是否支持助手功能
137    pub fn supports_assistant(&self) -> bool {
138        match self {
139            FileType::Bitable => true,
140            FileType::Doc => true,
141            FileType::Sheet => true,
142            FileType::Wiki => true,
143        }
144    }
145}
146
147/// 订阅状态
148#[derive(Debug, Serialize, Deserialize, Clone)]
149#[serde(rename_all = "snake_case")]
150pub enum SubscriptionStatus {
151    /// 已订阅
152    Subscribed,
153    /// 未订阅
154    Unsubscribed,
155    /// 已暂停
156    Paused,
157    /// 已取消
158    Cancelled,
159    /// 未知状态
160    #[serde(other)]
161    Unknown,
162}
163
164impl SubscriptionStatus {
165    /// 是否为活跃状态
166    pub fn is_active(&self) -> bool {
167        matches!(self, SubscriptionStatus::Subscribed)
168    }
169
170    /// 是否可以激活
171    pub fn can_activate(&self) -> bool {
172        matches!(
173            self,
174            SubscriptionStatus::Unsubscribed | SubscriptionStatus::Paused
175        )
176    }
177
178    /// 获取状态描述
179    pub fn description(&self) -> &'static str {
180        match self {
181            SubscriptionStatus::Subscribed => "已订阅",
182            SubscriptionStatus::Unsubscribed => "未订阅",
183            SubscriptionStatus::Paused => "已暂停",
184            SubscriptionStatus::Cancelled => "已取消",
185            SubscriptionStatus::Unknown => "未知状态",
186        }
187    }
188
189    /// 获取状态颜色(用于UI显示)
190    pub fn color(&self) -> &'static str {
191        match self {
192            SubscriptionStatus::Subscribed => "green",
193            SubscriptionStatus::Unsubscribed => "gray",
194            SubscriptionStatus::Paused => "orange",
195            SubscriptionStatus::Cancelled => "red",
196            SubscriptionStatus::Unknown => "gray",
197        }
198    }
199}
200
201/// 订阅详情
202#[derive(Debug, Deserialize, Clone)]
203pub struct SubscriptionDetail {
204    /// 订阅状态
205    pub status: SubscriptionStatus,
206    /// 订阅开始时间(时间戳)
207    pub start_time: Option<i64>,
208    /// 订阅结束时间(时间戳)
209    pub end_time: Option<i64>,
210    /// 最后更新时间(时间戳)
211    pub last_update_time: Option<i64>,
212    /// 订阅者ID
213    pub subscriber_id: Option<String>,
214    /// 订阅者类型
215    pub subscriber_type: Option<String>,
216    /// 订阅配置
217    pub config: Option<serde_json::Value>,
218    /// 扩展信息
219    pub extra: Option<serde_json::Value>,
220}
221
222/// 获取订阅状态响应
223#[derive(Debug, Deserialize)]
224pub struct GetSubscriptionResponse {
225    /// 订阅详情
226    pub subscription: SubscriptionDetail,
227    /// 文档token
228    pub file_token: String,
229    /// 文档类型
230    pub file_type: String,
231}
232
233impl ApiResponseTrait for GetSubscriptionResponse {
234    fn data_format() -> ResponseFormat {
235        ResponseFormat::Data
236    }
237}
238
239/// 获取订阅状态
240pub async fn get_subscription(
241    request: GetSubscriptionRequest,
242    config: &Config,
243    option: Option<RequestOption>,
244) -> SDKResult<BaseResponse<GetSubscriptionResponse>> {
245    let mut api_req = request.api_request;
246    api_req.http_method = Method::GET;
247
248    api_req.api_path = format!(
249        "/open-apis/assistant/v1/file/{}/{}/subscription",
250        request.file_type, request.file_token
251    );
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)]
360mod tests {
361    use super::*;
362
363    #[test]
364    fn test_get_subscription_request_builder() {
365        let request = GetSubscriptionRequest::builder()
366            .file_token("doccnxxxxxx")
367            .as_doc()
368            .build();
369
370        assert_eq!(request.file_token, "doccnxxxxxx");
371        assert_eq!(request.file_type, "doc");
372    }
373
374    #[test]
375    fn test_file_type_methods() {
376        assert_eq!(FileType::Doc.to_string(), "doc");
377        assert_eq!(FileType::Doc.chinese_name(), "文档");
378        assert!(FileType::Doc.supports_assistant());
379    }
380
381    #[test]
382    fn test_subscription_status_methods() {
383        assert!(SubscriptionStatus::Subscribed.is_active());
384        assert!(!SubscriptionStatus::Unsubscribed.is_active());
385        assert!(SubscriptionStatus::Unsubscribed.can_activate());
386        assert!(!SubscriptionStatus::Subscribed.can_activate());
387
388        assert_eq!(SubscriptionStatus::Subscribed.description(), "已订阅");
389        assert_eq!(SubscriptionStatus::Subscribed.color(), "green");
390    }
391
392    #[test]
393    fn test_subscription_detail_methods() {
394        let detail = SubscriptionDetail {
395            status: SubscriptionStatus::Subscribed,
396            start_time: Some(1700000000),
397            end_time: Some(1700086400),
398            last_update_time: Some(1700043200),
399            subscriber_id: Some("user123".to_string()),
400            subscriber_type: Some("user".to_string()),
401            config: None,
402            extra: None,
403        };
404
405        assert!(detail.is_subscribed());
406        assert!(!detail.can_activate());
407        assert_eq!(detail.duration_seconds(), Some(86400));
408        assert_eq!(detail.duration_days(), Some(1.0));
409    }
410}