Skip to main content

bpi_rs/electric/
charge_list.rs

1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
2use chrono::NaiveDate;
3use serde::{ Deserialize, Serialize };
4
5#[derive(Debug, Serialize, Clone, Deserialize)]
6pub struct ChargeVipInfo {
7    /// 大会员过期时间(恒为 0)
8    #[serde(rename = "vipDueMsec")]
9    pub vip_due_msec: i64,
10
11    /// 大会员状态(包月充电时恒为 0;自定义充电:0=无, 1=有)
12    #[serde(rename = "vipStatus")]
13    pub vip_status: i32,
14
15    /// 大会员类型(包月充电时恒为 0;自定义充电:0=无, 1=月大会员, 2=年度及以上大会员)
16    #[serde(rename = "vipType")]
17    pub vip_type: i32,
18}
19
20#[derive(Debug, Serialize, Clone, Deserialize)]
21pub struct ChargeUser {
22    /// 充电用户昵称
23    pub uname: String,
24
25    /// 充电用户头像 url
26    pub avatar: String,
27
28    /// 充电对象 mid
29    pub mid: i64,
30
31    /// 充电用户 mid(支付id?)
32    pub pay_mid: i64,
33
34    /// 充电用户排名(取决于充电多少)
35    pub rank: i32,
36
37    /// 充电用户会员信息
38    pub vip_info: ChargeVipInfo,
39
40    /// 充电留言(为空表示无留言)
41    pub message: String,
42}
43
44#[derive(Debug, Serialize, Clone, Deserialize)]
45pub struct ChargeMonthUpData {
46    /// 本月充电人数
47    pub count: i32,
48
49    /// 本月充电用户列表
50    #[serde(default)]
51    pub list: Vec<ChargeUser>,
52
53    /// 总计充电次数
54    pub total_count: i32,
55}
56
57/// 视频充电展示信息(高阶信息)
58#[derive(Debug, Serialize, Clone, Deserialize)]
59pub struct VideoShowInfoHighLevel {
60    /// 权限类型
61    pub privilege_type: i32,
62    /// 主标题
63    pub title: String,
64    /// 副标题
65    pub sub_title: String,
66    /// 是否显示按钮
67    pub show_button: bool,
68}
69
70/// 视频充电展示信息
71#[derive(Debug, Serialize, Clone, Deserialize)]
72pub struct VideoShowInfo {
73    /// 是否显示
74    pub show: bool,
75
76    /// 充电功能开启状态
77    /// - `-1`: 未开通
78    /// - `1`: 开通
79    /// - `2`: 包月、自定义充电
80    /// - `3`: 包月高档、自定义充电
81    pub state: i32,
82
83    /// 充电按钮显示文字
84    pub title: String,
85
86    /// 充电跳转 URL 支付页面
87    pub jump_url: String,
88
89    /// 图标 URL
90    pub icon: String,
91
92    /// 充电专属视频信息
93    pub high_level: VideoShowInfoHighLevel,
94
95    /// 充电问答 ID
96    pub with_qa_id: i64,
97}
98
99/// 视频充电展示数据
100#[derive(Debug, Serialize, Clone, Deserialize)]
101pub struct VideoElecShowData {
102    /// 展示选项
103    pub show_info: VideoShowInfo,
104    /// 目标视频充电人数
105    pub av_count: i32,
106    /// 本月充电人数
107    pub count: i32,
108    /// 总计充电人数
109    pub total_count: i32,
110    /// 本月充电用户列表
111    #[serde(default)]
112    pub list: Vec<ChargeUser>,
113}
114
115// 充电列表分页信息
116#[derive(Debug, Clone, Deserialize, Serialize)]
117#[serde(rename_all = "camelCase")]
118pub struct RechargePage {
119    /// 当前页数
120    pub current_page: u64,
121    /// 当前分页大小
122    pub page_size: u64,
123    /// 记录总数
124    pub total_count: u64,
125    /// 总页数
126    pub total_page: u64,
127}
128
129/// 充电信息本体
130#[derive(Debug, Clone, Deserialize, Serialize)]
131#[serde(rename_all = "camelCase")]
132pub struct RechargeRecord {
133    /// 充电人mid
134    pub mid: u64,
135    /// 充电人昵称
136    pub name: String,
137    /// 充电人头像
138    pub avatar: String,
139    /// 原始B币数
140    pub original_third_coin: f64,
141    /// 实际收到的贝壳数
142    pub brokerage: f64,
143    /// 充电渠道 Web/安卓/iOS
144    pub remark: String,
145    /// 充电时间 yyyy-MM-dd HH:mm:ss
146    pub ctime: String,
147}
148
149/// 充电列表数据
150#[derive(Debug, Clone, Deserialize, Serialize)]
151#[serde(rename_all = "camelCase")]
152pub struct RechargeData {
153    /// 分页信息
154    pub page: RechargePage,
155    /// 充电信息本体
156    pub result: Vec<RechargeRecord>,
157}
158
159/// 充电列表分页信息
160#[derive(Debug, Clone, Deserialize, Serialize)]
161pub struct ElecRankPager {
162    /// 当前页数
163    pub current: u64,
164    /// 当前分页大小
165    pub size: u64,
166    /// 记录总数
167    pub total: u64,
168}
169
170/// 充电信息本体
171#[derive(Debug, Clone, Deserialize, Serialize)]
172pub struct ElecRankRecord {
173    /// 0
174    pub aid: u64,
175    /// 空
176    pub bvid: String,
177    /// 充电电池数
178    pub elec_num: f64,
179    /// 空
180    pub title: String,
181    /// 充电人昵称
182    pub uname: String,
183    /// 充电人头像
184    pub avatar: String,
185    /// 充电时间 yyyy-MM-dd HH:mm:ss
186    pub ctime: String,
187}
188
189/// 历史充电数据
190#[derive(Debug, Clone, Deserialize, Serialize)]
191pub struct ElecRankData {
192    /// 充电信息本体
193    pub list: Vec<ElecRankRecord>,
194    /// 分页信息
195    pub pager: ElecRankPager,
196}
197
198impl BpiClient {
199    /// 获取空间充电公示列表
200
201    pub async fn electric_month_up_list(
202        &self,
203        up_mid: i64
204    ) -> Result<BpiResponse<ChargeMonthUpData>, BpiError> {
205        self
206            .get("https://api.bilibili.com/x/ugcpay-rank/elec/month/up")
207            .query(&[("up_mid", up_mid)])
208            .send_bpi("获取空间充电公示列表").await
209    }
210
211    /// 获取视频充电鸣谢名单
212    ///
213    /// # 文档
214    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
215    ///
216    /// # 参数
217    ///
218    /// | 名称 | 类型 | 说明 |
219    /// | ---- | ---- | ---- |
220    /// | `mid` | i64 | up 主 mid |
221    /// | `aid` | `Option<i64>` | 稿件 avid |
222    /// | `bvid` | `Option<&str>` | 稿件 bvid |
223    pub async fn electric_video_show(
224        &self,
225        mid: i64,
226        aid: Option<i64>,
227        bvid: Option<&str>
228    ) -> Result<BpiResponse<VideoElecShowData>, BpiError> {
229        let mut req = self
230            .get("https://api.bilibili.com/x/web-interface/elec/show")
231            .query(&[("mid", mid)]);
232        if let Some(a) = aid {
233            req = req.query(&[("aid", a)]);
234        }
235        if let Some(b) = bvid {
236            req = req.query(&[("bvid", b)]);
237        }
238        req.send_bpi("获取视频充电鸣谢").await
239    }
240
241    /// 获取我收到的充电列表
242    ///
243    /// # 文档
244    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
245    ///
246    /// # 参数
247    ///
248    /// | 名称 | 类型 | 说明 |
249    /// | ---- | ---- | ---- |
250    /// | `page` | u64 | 页数 |
251    /// | `page_size` | u64 | 分页大小 `[1,50]` |
252    /// | `begin_time` | `Option<NaiveDate>` | 开始日期 YYYY-MM-DD |
253    /// | `end_time` | `Option<NaiveDate>` | 结束日期 YYYY-MM-DD |
254    pub async fn electric_recharge_list(
255        &self,
256        page: u64,
257        page_size: u64,
258        begin_time: Option<NaiveDate>,
259        end_time: Option<NaiveDate>
260    ) -> Result<BpiResponse<RechargeData>, BpiError> {
261        let mut req = self
262            .get("https://pay.bilibili.com/bk/brokerage/listForCustomerRechargeRecord")
263            .query(&[("customerId", "10026")])
264            .query(
265                &[
266                    ("currentPage", page),
267                    ("pageSize", page_size),
268                ]
269            );
270
271        if let Some(begin) = begin_time {
272            req = req.query(&[("beginTime", begin.format("%Y-%m-%d").to_string())]);
273        }
274        if let Some(end) = end_time {
275            req = req.query(&[("endTime", end.format("%Y-%m-%d").to_string())]);
276        }
277
278        req.send_bpi("获取收到的充电列表").await
279    }
280
281    /// 获取历史充电数据
282    ///
283    /// # 文档
284    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
285    ///
286    /// # 参数
287    ///
288    /// | 名称 | 类型 | 说明 |
289    /// | ---- | ---- | ---- |
290    /// | `pn` | `Option<u64>` | 页数,默认 1 |
291    /// | `ps` | `Option<u64>` | 分页大小,默认 10,范围 `[1,20]` |
292    pub async fn electric_rank_recent(
293        &self,
294        pn: Option<u64>,
295        ps: Option<u64>
296    ) -> Result<BpiResponse<ElecRankData>, BpiError> {
297        let mut req = self.get("https://member.bilibili.com/x/h5/elec/rank/recent");
298
299        if let Some(page) = pn {
300            req = req.query(&[("pn", page)]);
301        }
302        if let Some(size) = ps {
303            req = req.query(&[("ps", size)]);
304        }
305
306        req.send_bpi("获取历史充电数据").await
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use super::*;
313    use chrono::{ Duration, Utc };
314    use tracing::info;
315
316    #[tokio::test]
317    async fn test_electric_month_up_list() {
318        let bpi = BpiClient::new();
319        let resp = bpi.electric_month_up_list(53456).await;
320        assert!(resp.is_ok());
321    }
322
323    #[tokio::test]
324    async fn test_electric_video_show() {
325        let bpi = BpiClient::new();
326        let resp = bpi.electric_video_show(53456, None, Some("BV1Dh411S7sS")).await;
327        assert!(resp.is_ok());
328    }
329
330    #[tokio::test]
331    async fn test_get_recharge_list() {
332        let bpi = BpiClient::new();
333        // 测试获取第一页,每页10条记录
334        let resp = bpi.electric_recharge_list(1, 10, None, None).await;
335        info!("响应: {:?}", resp);
336        assert!(resp.is_ok());
337
338        if let Ok(response) = resp {
339            let data = response.data.unwrap();
340            info!("充电总记录数: {}", data.page.total_count);
341            info!("当前页充电记录数: {}", data.result.len());
342            if let Some(record) = data.result.first() {
343                info!("第一条充电记录信息: {:?}", record);
344            }
345        }
346    }
347
348    #[tokio::test]
349    async fn test_get_recharge_list_with_dates() {
350        let bpi = BpiClient::new();
351        let now = Utc::now().date_naive();
352        let start_date = now - Duration::days(30);
353        let end_date = now;
354
355        let resp = bpi.electric_recharge_list(1, 10, Some(start_date), Some(end_date)).await;
356        info!("响应: {:?}", resp);
357        assert!(resp.is_ok());
358
359        if let Ok(response) = resp {
360            info!("在日期范围内获取到的总记录数: {}", response.data.unwrap().page.total_count);
361        }
362    }
363
364    #[tokio::test]
365    async fn test_get_elec_rank_recent() {
366        let bpi = BpiClient::new();
367        // 测试获取第一页,每页10条记录
368        let resp = bpi.electric_rank_recent(Some(1), Some(10)).await;
369        info!("响应: {:?}", resp);
370        assert!(resp.is_ok());
371
372        if let Ok(response) = resp {
373            let data = response.data.unwrap();
374
375            info!("充电总记录数: {}", data.pager.total);
376            info!("当前页充电记录数: {}", data.list.len());
377            if let Some(record) = data.list.first() {
378                info!("第一条充电记录信息: {:?}", record);
379            }
380        }
381    }
382}