Skip to main content

wechat_minapp/link/
short_link.rs

1//! 微信小程序短链接生成模块
2//!
3//! 获取小程序 Short Link,适用于微信内拉起小程序的业务场景。
4//! 目前只开放给电商类目(具体包含以下一级类目:电商平台、商家自营、跨境电商)。通过该接口,可以选择生成到期失效和永久有效的小程序短链。
5//! [官方文档](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/short-link/generateShortLink.html)。
6//!
7//!
8//! ## 示例
9//!
10//! ```no_run
11//! use wechat_minapp::client::WechatMinapp;
12//! use wechat_minapp::Link::{ShortLinkArgs,Link};
13//!
14//! #[tokio::main]
15//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
16//!     // 初始化客户端
17//!     let app_id = "your_app_id";
18//!     let secret = "your_app_secret";
19//!     let client = WechatMinapp::new(app_id, secret);
20//!     let link = Link::new(client);
21//!
22//!     let args = ShortLinkArgs::builder()
23//!     .path("pages/index/index")
24//!     .build()
25//!     .unwrap();
26//!     // 生成短链接
27//!     let short_link = link.short_link(args).await?;
28//!     
29//!     
30//!     Ok(())
31//! }
32//! ```
33//!
34
35use super::Link;
36use crate::utils::{RequestBuilder, ResponseExt};
37use crate::{Result, constants, error::Error};
38use serde::{Deserialize, Serialize};
39use tracing::debug;
40
41/// 短链接
42///
43/// # 示例
44///
45/// ```no_run
46/// use wechat_minapp::client::WechatMinapp;
47/// use wechat_minapp::Link::{ShortLinkArgs,Link};
48///
49/// #[tokio::main]
50/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
51///     // 初始化客户端
52///     let app_id = "your_app_id";
53///     let secret = "your_app_secret";
54///     let client = WechatMinapp::new(app_id, secret);
55///     let link = Link::new(client);
56///
57///     let args = ShortLinkArgs::builder()
58///     .path("pages/index/index")
59///     .build()
60///     .unwrap();
61///     // 生成短链接
62///     let short_link = link.short_link(args).await?;
63///     
64///     
65///     Ok(())
66/// }
67/// ```
68#[derive(Debug, Serialize, Deserialize, Clone)]
69pub struct ShortLink {
70    link: String,
71}
72
73/// 短链接生成参数
74///
75/// 用于配置短链接的生成选项,通过 [`ShortLinkArgs::builder()`] 方法创建。
76#[derive(Debug, Deserialize, Serialize)]
77pub struct ShortLinkArgs {
78    #[serde(rename = "page_url")]
79    path: String,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    page_title: Option<String>,
82    is_permanent: bool,
83}
84
85/// 短链接参数构建器
86///
87/// 提供链式调用的方式构建短链接参数,确保参数的正确性。
88///
89/// # 示例
90///
91/// ```
92/// use wechat_minapp::Link::ShortLinkArgs;
93///
94/// let args = ShortLinkArgs::builder()
95///     .path("pages/index/index")
96///     .page_title("page title")
97///     .with_permanent()
98///     .build()
99///     .unwrap();
100/// ```
101#[derive(Debug, Deserialize)]
102pub struct ShortLinkArgsBuilder {
103    path: Option<String>,
104    page_title: Option<String>,
105    is_permanent: Option<bool>,
106}
107
108impl ShortLinkArgs {
109    pub fn builder() -> ShortLinkArgsBuilder {
110        ShortLinkArgsBuilder::new()
111    }
112
113    pub fn path(&self) -> String {
114        self.path.clone()
115    }
116
117    pub fn page_title(&self) -> Option<String> {
118        self.page_title.clone()
119    }
120
121    pub fn is_permanent(&self) -> bool {
122        self.is_permanent
123    }
124}
125
126impl ShortLinkArgsBuilder {
127    pub fn new() -> Self {
128        ShortLinkArgsBuilder {
129            path: None,
130            page_title: None,
131            is_permanent: None,
132        }
133    }
134
135    pub fn path(mut self, path: impl Into<String>) -> Self {
136        self.path = Some(path.into());
137        self
138    }
139
140    pub fn page_title(mut self, title: impl Into<String>) -> Self {
141        self.page_title = Some(title.into());
142        self
143    }
144    pub fn with_permanent(mut self) -> Self {
145        self.is_permanent = Some(true);
146        self
147    }
148
149    pub fn build(self) -> Result<ShortLinkArgs> {
150        let path = self.path.map_or_else(
151            || {
152                Err(Error::InvalidParameter(
153                    "小程序页面路径不能为空".to_string(),
154                ))
155            },
156            |v| {
157                if v.len() > 1024 {
158                    return Err(Error::InvalidParameter(
159                        "页面路径最大长度 1024 个字符".to_string(),
160                    ));
161                }
162                Ok(v)
163            },
164        )?;
165
166        Ok(ShortLinkArgs {
167            path,
168            page_title: self.page_title,
169            is_permanent: self.is_permanent.unwrap_or(false),
170        })
171    }
172}
173
174impl Default for ShortLinkArgsBuilder {
175    fn default() -> Self {
176        Self::new()
177    }
178}
179
180impl Link {
181    /// 生成短链接
182    ///
183    /// 调用微信小程序短链接生成接口,返回短链接`ShortLink`。
184    ///
185    /// # 参数
186    ///
187    /// - `args`: 短链接生成参数
188    ///
189    /// # 返回
190    ///
191    /// 成功返回 `Ok(ShortLink)`,失败返回错误信息。
192    ///
193    /// # 示例
194    ///
195    /// ```no_run
196    /// use wechat_minapp::client::WechatMinapp;
197    /// use wechat_minapp::Link::{ShortLinkArgs,Link};
198    ///
199    /// #[tokio::main]
200    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
201    ///     // 初始化客户端
202    ///     let app_id = "your_app_id";
203    ///     let secret = "your_app_secret";
204    ///     let client = WechatMinapp::new(app_id, secret);
205    ///     let link = Link::new(client);
206    ///
207    ///     let args = ShortLinkArgs::builder()
208    ///     .path("pages/index/index")
209    ///     .build()
210    ///     .unwrap();
211    ///     // 生成短链接
212    ///     let short_link = link.short_link(args).await?;
213    ///     
214    ///     
215    ///     Ok(())
216    /// }
217    /// ```
218    ///
219    /// # 错误
220    ///
221    /// - 网络错误
222    /// - 认证错误(access_token 无效)
223    /// - 微信 API 返回错误
224    /// - 参数序列化错误
225    pub async fn short_link(&self, args: ShortLinkArgs) -> Result<ShortLink> {
226        debug!("get qr code args {:?}", &args);
227
228        let query = serde_json::json!({
229            "access_token":self.client.token().await?
230        });
231
232        let body = serde_json::to_value(ShortLinkArgs {
233            path: args.path.clone(),
234            page_title: args.page_title,
235            is_permanent: args.is_permanent,
236        })?;
237
238        let request = RequestBuilder::new(constants::SHORT_LINK_END_POINT)
239            .query(query)
240            .body(body)
241            .build()?;
242
243        let client = &self.client.client;
244
245        let response = client.execute(request).await?;
246
247        debug!("response: {:#?}", response);
248        response.to_json::<ShortLink>()
249    }
250}