Skip to main content

wechat_mp_sdk/api/
analytics.rs

1//! Analytics API
2
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use super::{WechatApi, WechatContext};
10use crate::error::WechatError;
11
12#[non_exhaustive]
13#[derive(Debug, Clone, Serialize)]
14pub struct AnalyticsDateRangeRequest {
15    pub begin_date: String,
16    pub end_date: String,
17}
18
19impl AnalyticsDateRangeRequest {
20    pub fn new(begin_date: impl Into<String>, end_date: impl Into<String>) -> Self {
21        Self {
22            begin_date: begin_date.into(),
23            end_date: end_date.into(),
24        }
25    }
26}
27
28#[non_exhaustive]
29#[derive(Debug, Clone, Serialize)]
30pub struct PerformanceDataRequest {
31    pub cost_time_type: i32,
32    pub default_start_time: i64,
33    pub default_end_time: i64,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub device: Option<String>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub networktype: Option<String>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub scene: Option<i32>,
40}
41
42#[non_exhaustive]
43#[derive(Debug, Clone, Deserialize, Serialize)]
44pub struct AnalyticsResponse {
45    #[serde(default)]
46    pub(crate) errcode: i32,
47    #[serde(default)]
48    pub(crate) errmsg: String,
49    #[serde(flatten)]
50    pub extra: HashMap<String, Value>,
51}
52
53pub struct AnalyticsApi {
54    context: Arc<WechatContext>,
55}
56
57impl AnalyticsApi {
58    pub fn new(context: Arc<WechatContext>) -> Self {
59        Self { context }
60    }
61
62    pub async fn get_daily_summary(
63        &self,
64        request: &AnalyticsDateRangeRequest,
65    ) -> Result<AnalyticsResponse, WechatError> {
66        self.post_datacube("/datacube/getweanalysisappiddailysummarytrend", request)
67            .await
68    }
69
70    pub async fn get_daily_visit_trend(
71        &self,
72        request: &AnalyticsDateRangeRequest,
73    ) -> Result<AnalyticsResponse, WechatError> {
74        self.post_datacube("/datacube/getweanalysisappiddailyvisittrend", request)
75            .await
76    }
77
78    pub async fn get_weekly_visit_trend(
79        &self,
80        request: &AnalyticsDateRangeRequest,
81    ) -> Result<AnalyticsResponse, WechatError> {
82        self.post_datacube("/datacube/getweanalysisappidweeklyvisittrend", request)
83            .await
84    }
85
86    pub async fn get_monthly_visit_trend(
87        &self,
88        request: &AnalyticsDateRangeRequest,
89    ) -> Result<AnalyticsResponse, WechatError> {
90        self.post_datacube("/datacube/getweanalysisappidmonthlyvisittrend", request)
91            .await
92    }
93
94    pub async fn get_daily_retain(
95        &self,
96        request: &AnalyticsDateRangeRequest,
97    ) -> Result<AnalyticsResponse, WechatError> {
98        self.post_datacube("/datacube/getweanalysisappiddailyretaininfo", request)
99            .await
100    }
101
102    pub async fn get_weekly_retain(
103        &self,
104        request: &AnalyticsDateRangeRequest,
105    ) -> Result<AnalyticsResponse, WechatError> {
106        self.post_datacube("/datacube/getweanalysisappidweeklyretaininfo", request)
107            .await
108    }
109
110    pub async fn get_monthly_retain(
111        &self,
112        request: &AnalyticsDateRangeRequest,
113    ) -> Result<AnalyticsResponse, WechatError> {
114        self.post_datacube("/datacube/getweanalysisappidmonthlyretaininfo", request)
115            .await
116    }
117
118    pub async fn get_visit_page(
119        &self,
120        request: &AnalyticsDateRangeRequest,
121    ) -> Result<AnalyticsResponse, WechatError> {
122        self.post_datacube("/datacube/getweanalysisappidvisitpage", request)
123            .await
124    }
125
126    pub async fn get_visit_distribution(
127        &self,
128        request: &AnalyticsDateRangeRequest,
129    ) -> Result<AnalyticsResponse, WechatError> {
130        self.post_datacube("/datacube/getweanalysisappidvisitdistribution", request)
131            .await
132    }
133
134    pub async fn get_user_portrait(
135        &self,
136        request: &AnalyticsDateRangeRequest,
137    ) -> Result<AnalyticsResponse, WechatError> {
138        self.post_datacube("/datacube/getweanalysisappiduserportrait", request)
139            .await
140    }
141
142    pub async fn get_performance_data(
143        &self,
144        request: &PerformanceDataRequest,
145    ) -> Result<AnalyticsResponse, WechatError> {
146        self.post_datacube("/wxaapi/log/get_performance", request)
147            .await
148    }
149
150    async fn post_datacube<B: Serialize>(
151        &self,
152        endpoint: &str,
153        body: &B,
154    ) -> Result<AnalyticsResponse, WechatError> {
155        let response: AnalyticsResponse = self.context.authed_post(endpoint, body).await?;
156        WechatError::check_api(response.errcode, &response.errmsg)?;
157        Ok(response)
158    }
159}
160
161impl WechatApi for AnalyticsApi {
162    fn context(&self) -> &WechatContext {
163        &self.context
164    }
165
166    fn api_name(&self) -> &'static str {
167        "analytics"
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[test]
176    fn analytics_response_deserializes() {
177        let json = r#"{"errcode":0,"errmsg":"ok","list":[{"k":"v"}]}"#;
178        let response: AnalyticsResponse = serde_json::from_str(json).unwrap();
179        assert_eq!(response.errcode, 0);
180        assert!(response.extra.contains_key("list"));
181    }
182
183    #[test]
184    fn analytics_date_range_request_serializes() {
185        let request = AnalyticsDateRangeRequest::new("20240101", "20240102");
186        let value = serde_json::to_value(request).unwrap();
187        assert_eq!(value["begin_date"], "20240101");
188        assert_eq!(value["end_date"], "20240102");
189    }
190}