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}