use reqwest::{
StatusCode,
header::{COOKIE, HeaderValue},
};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use snafu::ensure;
use crate::{
card::utils::card_request_handler,
errors,
errors::{ApiError, card::CardResult},
session::{Client, Session},
utils::{
ApiModel,
consts::{
CARD_BLADE_AUTH_URL, CARD_GET_DORM_FEE_URL, CARD_PAGE_TICKET_POST_FORM_URL,
CARD_PAGE_URL,
},
response_json_map,
},
};
async fn get_page_ticket(client: &Client, session: &Session) -> CardResult<String> {
let res = card_request_handler(client, session, |client| {
client.post(CARD_PAGE_URL).form(&[
("EMenuName", "电费、网费"),
("MenuName", "电费、网费"),
("Url", CARD_PAGE_TICKET_POST_FORM_URL),
("apptype", "4"),
("flowID", "10002"),
])
})
.await?;
ensure!(
res.status() == StatusCode::OK,
errors::WebsiteSnafu {
msg: "Get Page Ticket Error".to_string()
}
);
Ok(regex!("ticket=(.*)\'")
.captures(&res.text().await?)
.and_then(|item| item.get(1))
.ok_or(ApiError::Website {
msg: "Page Ticket Not Found".to_string(),
})?
.as_str()
.to_string())
}
async fn get_synjones_auth(
client: &Client,
session: &Session,
ticket: impl AsRef<str>,
) -> CardResult<String> {
let res = card_request_handler(client, session, |client| {
client
.post(CARD_BLADE_AUTH_URL)
.form(&[("ticket", ticket.as_ref()), ("json", "true")])
})
.await?;
ensure!(
res.status() == StatusCode::OK,
errors::WebsiteSnafu {
msg: "Get Synjones Auth Error".to_string()
}
);
let data = res.json::<Map<String, Value>>().await?;
let token = data
.get("data")
.and_then(|item| item.get("access_token"))
.and_then(|item| item.as_str())
.ok_or(ApiError::Website {
msg: "Synjones Auth Token Not Found".to_string(),
})?;
Ok(format!("bearer {}", token))
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Subsidy {
Huxi {
#[serde(alias = "电剩余补助")]
#[serde(alias = "电剩余补助(度)")]
electricity: String,
#[serde(alias = "水剩余补助")]
#[serde(alias = "水剩余补助(吨)")]
water: String,
},
Old {
#[serde(alias = "补贴余额")]
subsidies: String,
},
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct EnergyFees {
#[serde(alias = "剩余金额")]
#[serde(alias = "现金余额")]
pub balance: String,
#[serde(flatten)]
pub subsidies: Subsidy,
}
impl ApiModel for EnergyFees {}
impl EnergyFees {
pub async fn fetch_self(
client: &Client,
session: &mut Session,
room: impl AsRef<str>,
is_huxi: bool,
) -> CardResult<EnergyFees> {
let card_access_info = session
.access_infos
.card_access_info
.as_ref()
.ok_or(ApiError::NotAccess)?;
if card_access_info.synjones_auth.is_none() {
let ticket = get_page_ticket(client, session).await?;
let synjones_auth = get_synjones_auth(client, session, ticket).await?;
session
.access_infos
.card_access_info
.as_mut()
.unwrap()
.synjones_auth
.replace(synjones_auth);
}
let synjones_auth = session
.access_infos
.card_access_info
.as_ref()
.unwrap()
.synjones_auth
.as_ref()
.unwrap();
let cookie_header = HeaderValue::from_str(&format!("synjones-auth={}", synjones_auth))
.map_err(|err| ApiError::Website {
msg: format!("Set cookies error: {}", err),
})?;
let res = card_request_handler(client, session, |client| {
client
.post(CARD_GET_DORM_FEE_URL)
.form(&[
("feeitemid", if is_huxi { "182" } else { "181" }), ("json", "true"),
("level", "2"),
("room", room.as_ref()),
("type", "IEC"),
])
.header(COOKIE, cookie_header)
})
.await?;
ensure!(
res.status() == StatusCode::OK,
errors::WebsiteSnafu {
msg: "Get Dorm Fee Error".to_string()
}
);
let (mut json, raw_response) = response_json_map(res).await?;
let msg = json
.get("msg")
.and_then(|item| item.as_str())
.ok_or(ApiError::Website {
msg: "Website response format incorrect".to_string(),
})?;
ensure!(
msg == "success",
errors::WebsiteSnafu {
msg: "Website response format incorrect".to_string()
}
);
json.get_mut("map")
.and_then(|item| item.get_mut("showData"))
.map(Value::take)
.map(serde_json::from_value)
.ok_or(ApiError::Website {
msg: "Website response format incorrect".to_string(),
})?
.map_err(|_| ApiError::ModelParse {
msg: "Website response format incorrect".to_string(),
raw_response,
})
}
}