tencentcloud_sms/api/send_sms/
request.rs

1use 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                // 分离成功和失败的号码
62                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                    // 全部成功
69                    info!("所有号码发送成功,数量: {}", succeeded.len());
70                    Ok(succeeded)
71                } else if succeeded.is_empty() {
72                    // 全部失败,返回第一个错误
73                    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                    // 部分失败
82                    warn!(
83                        "部分号码发送失败,成功: {}, 失败: {}",
84                        succeeded.len(),
85                        failed.len()
86                    );
87                    Err(SmsError::PartialFailure { succeeded, failed })
88                }
89            }
90            ResponseBody::Error(api_error) => {
91                // API级别错误
92                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}