bpi_rs/user/relation/
followers.rs1use crate::models::Vip;
5use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
6use serde::{ Deserialize, Serialize };
7#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct OfficialVerify {
12 #[serde(rename = "type")]
14 pub verify_type: i8,
15 pub desc: String,
17}
18
19#[derive(Debug, Clone, Deserialize, Serialize)]
21pub struct RelationListItem {
22 pub mid: u64,
24 pub attribute: u8,
26 pub mtime: Option<u64>,
28 pub tag: Option<Vec<u64>>,
30 pub special: u8,
32 pub contract_info: Option<serde_json::Value>,
33 pub uname: String,
35 pub face: String,
37 pub sign: String,
39 pub face_nft: u8,
41 pub official_verify: OfficialVerify,
43 pub vip: Vip,
45}
46
47#[derive(Debug, Clone, Deserialize, Serialize)]
49pub struct FansListResponseData {
50 pub list: Vec<RelationListItem>,
52 pub offset: String,
54 pub re_version: u32,
55 pub total: u64,
57}
58
59impl BpiClient {
62 pub async fn user_followers(
77 &self,
78 vmid: u64,
79 ps: Option<u32>,
80 pn: Option<u32>,
81 offset: Option<&str>,
82 last_access_ts: Option<u64>,
83 from: Option<&str>
84 ) -> Result<BpiResponse<FansListResponseData>, BpiError> {
85 let mut req = self
86 .get("https://api.bilibili.com/x/relation/fans")
87 .with_bilibili_headers()
88 .query(&[("vmid", &vmid.to_string())]);
89
90 if let Some(p) = ps {
91 req = req.query(&[("ps", &p.to_string())]);
92 }
93 if let Some(p) = pn {
94 req = req.query(&[("pn", &p.to_string())]);
95 }
96 if let Some(o) = offset {
97 req = req.query(&[("offset", o)]);
98 }
99 if let Some(l) = last_access_ts {
100 req = req.query(&[("last_access_ts", &l.to_string())]);
101 }
102 if let Some(f) = from {
103 req = req.query(&[("from", f)]);
104 }
105
106 req.send_bpi("查询用户粉丝明细").await
107 }
108}
109
110#[cfg(test)]
113mod tests {
114 use super::*;
115 use tracing::info;
116
117 const TEST_VMID: u64 = 4279370;
118
119 #[tokio::test]
120 async fn test_user_followers() -> Result<(), BpiError> {
121 let bpi = BpiClient::new();
122 let resp = bpi.user_followers(TEST_VMID, Some(50), Some(1), None, None, None).await?;
123 let data = resp.into_data()?;
124
125 info!("用户粉丝明细: {:?}", data);
126 assert!(!data.list.is_empty());
127 assert_eq!(data.list.len(), 50);
128 assert!(!data.offset.is_empty());
129 assert!(data.total > 0);
130
131 Ok(())
132 }
133}