133 lines
4.2 KiB
Python
Executable File
133 lines
4.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
教务同步只读 API 联调脚本(Python 3.7+,仅标准库)
|
|
|
|
调用两个业务接口并打印成功结果:
|
|
- GET /api/v1/students 学生基本信息(第 1 页)
|
|
- GET /api/v1/student-changes 学生异动(按年)
|
|
|
|
用法:
|
|
python test_jw_cx_api.py --prod
|
|
python test_jw_cx_api.py --base-url http://web.hzdyjsxy.cn:18081/jw-cx-api --year 2026
|
|
|
|
环境变量:JW_CX_BASE_URL、JW_CX_API_KEY
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
HEADER_API_KEY = "X-Jw-Cx-Key"
|
|
DEFAULT_BASE_URL = "http://127.0.0.1:5050"
|
|
PROD_BASE_URL = "http://web.hzdyjsxy.cn:18081"
|
|
DEFAULT_API_KEY = "JWCX001"
|
|
|
|
|
|
def _get_json(url, api_key, timeout=120):
|
|
req = urllib.request.Request(url, method="GET")
|
|
req.add_header(HEADER_API_KEY, api_key)
|
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
body = resp.read().decode("utf-8")
|
|
return resp.status, json.loads(body)
|
|
|
|
|
|
def _join_url(base, path):
|
|
return base.rstrip("/") + "/" + path.lstrip("/")
|
|
|
|
|
|
def fetch_students(base_url, api_key, page=1, page_size=10):
|
|
qs = urllib.parse.urlencode({"page": page, "page_size": page_size})
|
|
url = _join_url(base_url, "/api/v1/students?" + qs)
|
|
status, data = _get_json(url, api_key)
|
|
if status != 200 or data.get("code") != 200:
|
|
raise RuntimeError("学生基本信息接口失败: status=%s body=%s" % (status, data))
|
|
return data
|
|
|
|
|
|
def fetch_student_changes(base_url, api_key, year):
|
|
qs = urllib.parse.urlencode({"year": year})
|
|
url = _join_url(base_url, "/api/v1/student-changes?" + qs)
|
|
status, data = _get_json(url, api_key)
|
|
if status != 200 or data.get("code") != 200:
|
|
raise RuntimeError("学生异动接口失败: status=%s body=%s" % (status, data))
|
|
return data
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="教务同步 API:获取学生信息与学生异动")
|
|
parser.add_argument("--base-url", default=None, help="API 根地址")
|
|
parser.add_argument("--prod", action="store_true", help="使用 %s" % PROD_BASE_URL)
|
|
parser.add_argument(
|
|
"--api-key",
|
|
default=os.environ.get("JW_CX_API_KEY", DEFAULT_API_KEY),
|
|
help="请求头 %s" % HEADER_API_KEY,
|
|
)
|
|
parser.add_argument(
|
|
"--year",
|
|
type=int,
|
|
default=None,
|
|
help="异动筛选年份,默认当前自然年",
|
|
)
|
|
parser.add_argument(
|
|
"--page-size",
|
|
type=int,
|
|
default=10,
|
|
help="学生基本信息每页条数,默认 10",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.base_url:
|
|
base_url = args.base_url
|
|
elif args.prod:
|
|
base_url = PROD_BASE_URL
|
|
else:
|
|
base_url = os.environ.get("JW_CX_BASE_URL", DEFAULT_BASE_URL)
|
|
|
|
year = args.year if args.year is not None else time.localtime().tm_year
|
|
|
|
print("BASE_URL: %s" % base_url)
|
|
print("")
|
|
|
|
try:
|
|
students = fetch_students(base_url, args.api_key, page=1, page_size=args.page_size)
|
|
print("=== 学生基本信息 GET /api/v1/students ===")
|
|
print(
|
|
"total=%s page=%s/%s 本页=%s条"
|
|
% (
|
|
students.get("total"),
|
|
students.get("page"),
|
|
students.get("total_pages"),
|
|
len(students.get("data") or []),
|
|
)
|
|
)
|
|
if students.get("data"):
|
|
print("首条:", json.dumps(students["data"][0], ensure_ascii=False, indent=2))
|
|
print("")
|
|
|
|
changes = fetch_student_changes(base_url, args.api_key, year)
|
|
print("=== 学生异动 GET /api/v1/student-changes?year=%s ===" % year)
|
|
print("total=%s filter_year=%s" % (changes.get("total"), changes.get("filter_year")))
|
|
if changes.get("data"):
|
|
print("首条:", json.dumps(changes["data"][0], ensure_ascii=False, indent=2))
|
|
elif changes.get("total") == 0:
|
|
print("(当年无异动记录)")
|
|
|
|
except (urllib.error.URLError, urllib.error.HTTPError, RuntimeError, ValueError) as e:
|
|
print("调用失败: %s" % e, file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print("")
|
|
print("调用成功")
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|