tencentcloud_sms/api/send_sms/
request.rs1use super::response::{ResponseBody, SendSmsResponse, SendStatus};
2use crate::api::common::ApiRequest;
3use crate::logging::{debug, error, info, warn};
4use crate::{SmsError, SmsResult};
5use serde::Serialize;
6use typed_builder::TypedBuilder;
7use validator::Validate;
8
9#[derive(Debug, Clone, Serialize, TypedBuilder, Validate)]
10#[serde(rename_all = "PascalCase")]
11pub struct SendSmsRequest {
12 #[validate(length(min = 1, max = 200, message = "手机号数量必须在1-200之间"))]
13 #[builder(setter(transform = |phones: Vec<impl Into<String>>| phones.into_iter().map(Into::into).collect()))]
14 phone_number_set: Vec<String>,
15
16 #[validate(length(min = 1, message = "SmsSdkAppId不能为空"))]
17 #[builder(setter(transform = |id: impl Into<String>| id.into()))]
18 sms_sdk_app_id: String,
19
20 #[validate(length(min = 1, message = "TemplateId不能为空"))]
21 #[builder(setter(transform = |id: impl Into<String>| id.into()))]
22 template_id: String,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
25 #[builder(default, setter(transform = |s: impl Into<String>| Some(s.into())))]
26 sign_name: Option<String>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
29 #[builder(default, setter(transform = |params: Vec<impl Into<String>>| Some(params.into_iter().map(Into::into).collect())))]
30 template_param_set: Option<Vec<String>>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
33 #[builder(default, setter(transform = |s: impl Into<String>| Some(s.into())))]
34 extend_code: Option<String>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
37 #[builder(default, setter(transform = |s: impl Into<String>| Some(s.into())))]
38 session_context: Option<String>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
41 #[builder(default, setter(transform = |s: impl Into<String>| Some(s.into())))]
42 sender_id: Option<String>,
43}
44
45impl ApiRequest for SendSmsRequest {
46 type Response = SendSmsResponse;
47 type Output = Vec<SendStatus>;
48
49 const ACTION: &'static str = "SendSms";
50
51 fn parse_response(response_text: &str) -> SmsResult<Self::Response> {
52 serde_json::from_str(response_text).map_err(Into::into)
53 }
54
55 fn handle_response(response: Self::Response) -> SmsResult<Self::Output> {
56 match response.response {
57 ResponseBody::Success(success) => {
58 let request_id = success.request_id.clone();
59 debug!("API 返回成功响应,request_id: {}", request_id);
60
61 let (succeeded, failed): (Vec<_>, Vec<_>) = success
63 .send_status_set
64 .into_iter()
65 .partition(|s| s.is_success());
66
67 if failed.is_empty() {
68 info!("所有号码发送成功,数量: {}", succeeded.len());
70 Ok(succeeded)
71 } else if succeeded.is_empty() {
72 let first_error = &failed[0];
74 warn!("所有号码发送失败,错误码: {}", first_error.code);
75 Err(SmsError::Business {
76 code: first_error.code.to_string_owned(),
77 message: first_error.message.clone(),
78 request_id,
79 })
80 } else {
81 warn!(
83 "部分号码发送失败,成功: {}, 失败: {}",
84 succeeded.len(),
85 failed.len()
86 );
87 Err(SmsError::PartialFailure { succeeded, failed })
88 }
89 }
90 ResponseBody::Error(api_error) => {
91 error!(
93 "API 返回错误响应,request_id: {}, 错误码: {}",
94 api_error.request_id, api_error.error.code
95 );
96 Err(crate::api::common::classify_error(
97 &api_error.error.code,
98 &api_error.error.message,
99 api_error.request_id,
100 ))
101 }
102 }
103 }
104}