use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
use chrono::NaiveDate;
use serde::{ Deserialize, Serialize };
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct ChargeVipInfo {
#[serde(rename = "vipDueMsec")]
pub vip_due_msec: i64,
#[serde(rename = "vipStatus")]
pub vip_status: i32,
#[serde(rename = "vipType")]
pub vip_type: i32,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct ChargeUser {
pub uname: String,
pub avatar: String,
pub mid: i64,
pub pay_mid: i64,
pub rank: i32,
pub vip_info: ChargeVipInfo,
pub message: String,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct ChargeMonthUpData {
pub count: i32,
#[serde(default)]
pub list: Vec<ChargeUser>,
pub total_count: i32,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct VideoShowInfoHighLevel {
pub privilege_type: i32,
pub title: String,
pub sub_title: String,
pub show_button: bool,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct VideoShowInfo {
pub show: bool,
pub state: i32,
pub title: String,
pub jump_url: String,
pub icon: String,
pub high_level: VideoShowInfoHighLevel,
pub with_qa_id: i64,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct VideoElecShowData {
pub show_info: VideoShowInfo,
pub av_count: i32,
pub count: i32,
pub total_count: i32,
#[serde(default)]
pub list: Vec<ChargeUser>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RechargePage {
pub current_page: u64,
pub page_size: u64,
pub total_count: u64,
pub total_page: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RechargeRecord {
pub mid: u64,
pub name: String,
pub avatar: String,
pub original_third_coin: f64,
pub brokerage: f64,
pub remark: String,
pub ctime: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RechargeData {
pub page: RechargePage,
pub result: Vec<RechargeRecord>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ElecRankPager {
pub current: u64,
pub size: u64,
pub total: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ElecRankRecord {
pub aid: u64,
pub bvid: String,
pub elec_num: f64,
pub title: String,
pub uname: String,
pub avatar: String,
pub ctime: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ElecRankData {
pub list: Vec<ElecRankRecord>,
pub pager: ElecRankPager,
}
impl BpiClient {
pub async fn electric_month_up_list(
&self,
up_mid: i64
) -> Result<BpiResponse<ChargeMonthUpData>, BpiError> {
self
.get("https://api.bilibili.com/x/ugcpay-rank/elec/month/up")
.query(&[("up_mid", up_mid)])
.send_bpi("获取空间充电公示列表").await
}
pub async fn electric_video_show(
&self,
mid: i64,
aid: Option<i64>,
bvid: Option<&str>
) -> Result<BpiResponse<VideoElecShowData>, BpiError> {
let mut req = self
.get("https://api.bilibili.com/x/web-interface/elec/show")
.query(&[("mid", mid)]);
if let Some(a) = aid {
req = req.query(&[("aid", a)]);
}
if let Some(b) = bvid {
req = req.query(&[("bvid", b)]);
}
req.send_bpi("获取视频充电鸣谢").await
}
pub async fn electric_recharge_list(
&self,
page: u64,
page_size: u64,
begin_time: Option<NaiveDate>,
end_time: Option<NaiveDate>
) -> Result<BpiResponse<RechargeData>, BpiError> {
let mut req = self
.get("https://pay.bilibili.com/bk/brokerage/listForCustomerRechargeRecord")
.query(&[("customerId", "10026")])
.query(
&[
("currentPage", page),
("pageSize", page_size),
]
);
if let Some(begin) = begin_time {
req = req.query(&[("beginTime", begin.format("%Y-%m-%d").to_string())]);
}
if let Some(end) = end_time {
req = req.query(&[("endTime", end.format("%Y-%m-%d").to_string())]);
}
req.send_bpi("获取收到的充电列表").await
}
pub async fn electric_rank_recent(
&self,
pn: Option<u64>,
ps: Option<u64>
) -> Result<BpiResponse<ElecRankData>, BpiError> {
let mut req = self.get("https://member.bilibili.com/x/h5/elec/rank/recent");
if let Some(page) = pn {
req = req.query(&[("pn", page)]);
}
if let Some(size) = ps {
req = req.query(&[("ps", size)]);
}
req.send_bpi("获取历史充电数据").await
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::{ Duration, Utc };
use tracing::info;
#[tokio::test]
async fn test_electric_month_up_list() {
let bpi = BpiClient::new();
let resp = bpi.electric_month_up_list(53456).await;
assert!(resp.is_ok());
}
#[tokio::test]
async fn test_electric_video_show() {
let bpi = BpiClient::new();
let resp = bpi.electric_video_show(53456, None, Some("BV1Dh411S7sS")).await;
assert!(resp.is_ok());
}
#[tokio::test]
async fn test_get_recharge_list() {
let bpi = BpiClient::new();
let resp = bpi.electric_recharge_list(1, 10, None, None).await;
info!("响应: {:?}", resp);
assert!(resp.is_ok());
if let Ok(response) = resp {
let data = response.data.unwrap();
info!("充电总记录数: {}", data.page.total_count);
info!("当前页充电记录数: {}", data.result.len());
if let Some(record) = data.result.first() {
info!("第一条充电记录信息: {:?}", record);
}
}
}
#[tokio::test]
async fn test_get_recharge_list_with_dates() {
let bpi = BpiClient::new();
let now = Utc::now().date_naive();
let start_date = now - Duration::days(30);
let end_date = now;
let resp = bpi.electric_recharge_list(1, 10, Some(start_date), Some(end_date)).await;
info!("响应: {:?}", resp);
assert!(resp.is_ok());
if let Ok(response) = resp {
info!("在日期范围内获取到的总记录数: {}", response.data.unwrap().page.total_count);
}
}
#[tokio::test]
async fn test_get_elec_rank_recent() {
let bpi = BpiClient::new();
let resp = bpi.electric_rank_recent(Some(1), Some(10)).await;
info!("响应: {:?}", resp);
assert!(resp.is_ok());
if let Ok(response) = resp {
let data = response.data.unwrap();
info!("充电总记录数: {}", data.pager.total);
info!("当前页充电记录数: {}", data.list.len());
if let Some(record) = data.list.first() {
info!("第一条充电记录信息: {:?}", record);
}
}
}
}