Skip to main content

bpi_rs/login/
login_notice.rs

1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
2use serde::{ Deserialize, Serialize };
3use std::collections::HashMap;
4
5// --- API 结构体 ---
6
7/// 查询指定登录记录的响应数据
8#[derive(Debug, Clone, Deserialize, Serialize)]
9pub struct LoginNoticeData {
10    pub mid: u64,
11    pub device_name: String,
12    pub login_type: String,
13    pub login_time: String,
14    pub location: String,
15    pub ip: String,
16}
17
18/// 最近一周登录情况的响应数据
19#[derive(Debug, Clone, Deserialize, Serialize)]
20pub struct LoginLogData {
21    pub count: u32,
22    pub list: Vec<LoginLogEntry>,
23}
24
25/// 登录日志列表中的单条记录
26#[derive(Debug, Clone, Deserialize, Serialize)]
27pub struct LoginLogEntry {
28    pub ip: String,
29    pub time: u64,
30    pub time_at: String,
31    pub status: bool,
32    #[serde(rename = "type")]
33    pub login_type: u8,
34    #[serde(rename = "geo")]
35    pub location: String,
36}
37
38impl BpiClient {
39    /// 查询指定登录记录。
40    ///
41    /// # 参数
42    /// * `mid` - 用户mid,必须是自己的mid。
43    /// * `buvid` - 可选的设备虚拟ID(web端为buvid3)。
44    pub async fn login_notice(
45        &self,
46        mid: u64,
47        buvid: Option<&str>
48    ) -> Result<BpiResponse<LoginNoticeData>, BpiError> {
49        let mut params = HashMap::new();
50        params.insert("mid", mid.to_string());
51        if let Some(buvid_val) = buvid {
52            params.insert("buvid", buvid_val.to_string());
53        }
54
55        self
56            .get("https://api.bilibili.com/x/safecenter/login_notice")
57            .query(&params)
58            .send_bpi("查询登录记录").await
59    }
60
61    /// 查询最近一周的登录情况。
62    ///
63    /// # 参数
64    /// 无。该接口自动使用当前登录用户的Session。
65    pub async fn login_log(&self) -> Result<BpiResponse<LoginLogData>, BpiError> {
66        self
67            .get("https://api.bilibili.com/x/member/web/login/log")
68            .query(
69                &[
70                    ("jsonp", "jsonp"),
71                    ("web_location", "333.33"),
72                ]
73            )
74            .send_bpi("查询最近一周登录情况").await
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[tokio::test]
83    async fn test_get_login_notice() -> Result<(), BpiError> {
84        let bpi = BpiClient::new();
85        let mid = 4279370;
86
87        let resp = bpi.login_notice(mid, None).await?;
88        let data = resp.into_data()?;
89
90        println!("指定登录记录:");
91        println!("  设备名: {}", data.device_name);
92        println!("  登录方式: {}", data.login_type);
93        println!("  登录时间: {}", data.login_time);
94        println!("  登录位置: {}", data.location);
95        println!("  IP: {}", data.ip);
96
97        assert_eq!(data.mid, mid);
98
99        Ok(())
100    }
101
102    #[tokio::test]
103    async fn test_get_login_log() -> Result<(), BpiError> {
104        let bpi = BpiClient::new();
105
106        let resp = bpi.login_log().await?;
107        let data = resp.into_data()?;
108
109        println!("最近一周登录记录 (共 {} 条):", data.count);
110        for entry in data.list {
111            println!("  时间: {} ({})", entry.time_at, entry.time);
112            println!("    IP: {}", entry.ip);
113            println!("    位置: {}", entry.location);
114            println!("    登录成功: {}", entry.status);
115            println!("    登录类型: {}", entry.login_type);
116        }
117
118        Ok(())
119    }
120}