import base64 from typing import List from uuid import uuid4 from fastapi import UploadFile import httpx import config from services.student_cache import get_student_by_token, save_student from exceptions.unauthorized import UnauthorizedError SEX_MAP = { 0: "男", 1: "女", } class XBXS: def __init__( self, token: str | None = None, UA: str = config.DEFAULT_USER_AGENT, ) -> None: self.token = token self.client = httpx.AsyncClient( trust_env=False, base_url=config.BASE_URL, timeout=httpx.Timeout( connect=10.0, read=30.0, write=30.0, pool=10.0, ), limits=httpx.Limits( max_connections=1, # 每个请求使用单独连接,不使用连接池 max_keepalive_connections=1, ), headers={ "user-agent": UA, "accept": "*/*", "accept-language": "zh-CN,zh-Hans;q=0.9", "accept-encoding": "gzip, deflate, br", }, proxy="http://127.0.0.1:9000" if config.DEBUG else None, verify=False if config.DEBUG else True, ) if token: self.client.headers["Authorization"] = f"Bearer {token}" async def get_captcha(self): resp = await self.client.get("/xiaobei-api/captchaImage") resp.raise_for_status() result = resp.json() if "uuid" not in result: raise RuntimeError(f"captcha error: {result}") return { "showCode": result.get("showCode", ""), "uuid": result.get("uuid", ""), } async def login(self, username: str, password: str): data = await self.get_captcha() resp = await self.client.post( "/xiaobei-api/userLogin", json={ "username": username, "password": base64.b64encode(password.encode()).decode(), "code": data.get("showCode", ""), "uuid": data.get("uuid", ""), "appUuid": "", }, ) resp.raise_for_status() result = resp.json() self.token = result.get("token") self.client.headers["Authorization"] = f"Bearer {self.token}" return result async def get_info(self): resp = await self.client.get("/xiaobei-api/getInfo") resp.raise_for_status() result = resp.json() if result.get("code") == "401": raise UnauthorizedError("token 已失效,请重新登录") return { "userName": result.get("user", {}).get("userName", ""), "nickName": result.get("user", {}).get("nickName", ""), "roles": result.get("roles", []), "phonenumber": result.get("user", {}).get("phonenumber", ""), "bindphonenumber": result.get("user", {}).get("bindphonenumber", ""), "idCard": result.get("user", {}).get("idCard", ""), "trtcId": result.get("user", {}).get("trtcId", ""), } async def get_student_info(self): resp = await self.client.get("/xiaobei-api/student/studentInfo") resp.raise_for_status() result = resp.json() sex = result.get("data", {}).get("studentSex", "") sex_text = SEX_MAP.get(sex, "未知") if result.get("code") == "401": raise UnauthorizedError("token 已失效,请重新登录") return { "id": result.get("data", {}).get("id", ""), "studentNumber": result.get("data", {}).get("studentNumber", ""), "deptId": result.get("data", {}).get("deptId", ""), "deptName": result.get("data", {}).get("deptName", ""), "studentName": result.get("data", {}).get("studentName", ""), "studentIpone": result.get("data", {}).get("studentIpone", ""), "studentIdCard": result.get("data", {}).get("studentIdCard", ""), "studentSex": sex_text, "studentSchoolTime": result.get("data", {}).get("studentSchoolTime", ""), "schoolId": result.get("data", {}).get("schoolId", ""), "schoolName": result.get("data", {}).get("schoolName", ""), "collegeId": result.get("data", {}).get("collegeId", ""), "collegeName": result.get("data", {}).get("collegeName", ""), "professionalId": result.get("data", {}).get("professionalId", ""), "professionalName": result.get("data", {}).get("professionalName", ""), } async def dep_id(self): resp = await self.client.get("/xiaobei-api/student/deptId") resp.raise_for_status() result = resp.json() if result.get("code") == "401": raise UnauthorizedError("token 已失效,请重新登录") return result async def get_user_sig2(self): resp = await self.client.get("/xiaobei-api/trtc/genUserSig2") resp.raise_for_status() result = resp.json() if result.get("code") == "401": raise UnauthorizedError("token 已失效,请重新登录") return result async def get_know_list(self): resp = await self.client.get("/xiaobei-api/student/know/list") resp.raise_for_status() result = resp.json() if result.get("code") == "401": raise UnauthorizedError("token 已失效,请重新登录") if result.get("total", 0) > 0: pass return result async def get_know(self, know: str): resp = await self.client.get(f"/xiaobei-api/student/know/{know}") resp.raise_for_status() result = resp.json() if result.get("code") == "401": raise UnauthorizedError("token 已失效,请重新登录") if result.get("total", 0) > 0: pass return result async def update_student_know( self, knowingID: str, remark: str, address: str, location: str, images: List[UploadFile], ): files = [] for i, image in enumerate(images): files.append( ( f"file{i}", ( f"{uuid4()}.jpg", image.file, image.content_type or "image/jpeg", ), ) ) resp = await self.client.post( "/xiaobei-api/student/know/updateStudentKnow", data={ "address": address, "remark": remark, "knowingId": knowingID, "location": location, "size": len(images), }, files=files, ) resp.raise_for_status() async def get_student_info_cached(self): cached = get_student_by_token(str(self.token)) if cached: return cached student = await self.get_student_info() save_student(str(self.token), student) return student