提交
This commit is contained in:
1 parent
a88d87047d
commit
c0de1b9bef
19 files changed
+17083
-202
No files matched your search
@@ -22,8 +22,33 @@ HEADER_API_KEY = "X-Jw-Cx-Key"
|
||||
|
||||
VIEW_NAME = "vw_jw_student_change_export"
|
||||
|
||||
# 视图列名(与 vw_jw_student_change_export 一致;筛选近 N 天时使用)
|
||||
COL_SUBMIT_DATE = "提交时间"
|
||||
# 视图列名:API 按「异动生效时间」所在自然年筛选
|
||||
COL_EFFECTIVE_TIME = "异动生效时间"
|
||||
|
||||
# 对外返回字段(与教务对接清单一致;不含视图内部列)
|
||||
EXPORT_COLUMNS = (
|
||||
"提交时间",
|
||||
"异动生效学期",
|
||||
"异动类型",
|
||||
"申请人学号",
|
||||
"姓名",
|
||||
"异动前所在年级",
|
||||
"异动前所在校区",
|
||||
"异动前所在院系",
|
||||
"异动前所在专业",
|
||||
"异动前所在班级",
|
||||
"异动前学制",
|
||||
"异动后所在校区",
|
||||
"异动后所在年级",
|
||||
"异动后所在班级",
|
||||
"异动后所在院系",
|
||||
"异动后所在专业",
|
||||
"异动后学制",
|
||||
"异动后班主任工号",
|
||||
"异动后班主任姓名",
|
||||
"新名字",
|
||||
"审核状态",
|
||||
)
|
||||
|
||||
_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_DEFAULT_CONFIG_PATH = os.path.join(_BASE_DIR, "config.ini")
|
||||
@@ -99,60 +124,37 @@ def server_bind():
|
||||
}
|
||||
|
||||
|
||||
def _api_defaults_from_config():
|
||||
"""从 [api] 读取 page_size、recent_days 默认值(INI 见 config*.ini)。"""
|
||||
page_size = _get_int("api", "page_size", 500)
|
||||
recent_days = _get_int("api", "recent_days", 30)
|
||||
if page_size < 1:
|
||||
page_size = 1
|
||||
if page_size > 5000:
|
||||
page_size = 5000
|
||||
if recent_days < 1:
|
||||
recent_days = 1
|
||||
if recent_days > 3660:
|
||||
recent_days = 3660
|
||||
return page_size, recent_days
|
||||
def _default_filter_year():
|
||||
"""配置文件 default_year;未配置或无效时使用当前自然年。"""
|
||||
raw = _get("api", "default_year", "")
|
||||
if raw:
|
||||
try:
|
||||
year = int(raw)
|
||||
if 2000 <= year <= 2100:
|
||||
return year
|
||||
except ValueError:
|
||||
pass
|
||||
return date.today().year
|
||||
|
||||
|
||||
def _clamp_int(value, lo, hi):
|
||||
v = int(value)
|
||||
if v < lo:
|
||||
return lo
|
||||
if v > hi:
|
||||
return hi
|
||||
return v
|
||||
|
||||
|
||||
def _resolve_query_paging():
|
||||
def _resolve_filter_year():
|
||||
"""
|
||||
合并 URL 参数与配置文件:
|
||||
- page_size:Query `page_size`,未传则用 [api] page_size
|
||||
- recent_days:Query `recent_days` 或 `days`(别名),未传则用 [api] recent_days
|
||||
范围:page_size 1~5000,recent_days 1~3660
|
||||
- year:Query `year`,未传则用 [api] default_year 或当前自然年
|
||||
"""
|
||||
cfg_ps, cfg_rd = _api_defaults_from_config()
|
||||
|
||||
raw_ps = request.args.get("page_size")
|
||||
if raw_ps is not None and str(raw_ps).strip() != "":
|
||||
default_year = _default_filter_year()
|
||||
raw_year = request.args.get("year")
|
||||
if raw_year is not None and str(raw_year).strip() != "":
|
||||
try:
|
||||
page_size = _clamp_int(raw_ps, 1, 5000)
|
||||
year = int(raw_year)
|
||||
except (ValueError, TypeError):
|
||||
return None, None, "page_size 必须为整数"
|
||||
return None, "year 必须为整数"
|
||||
if year < 2000 or year > 2100:
|
||||
return None, "year 有效范围为 2000~2100"
|
||||
else:
|
||||
page_size = cfg_ps
|
||||
year = default_year
|
||||
|
||||
raw_rd = request.args.get("recent_days")
|
||||
if raw_rd is None or str(raw_rd).strip() == "":
|
||||
raw_rd = request.args.get("days")
|
||||
if raw_rd is not None and str(raw_rd).strip() != "":
|
||||
try:
|
||||
recent_days = _clamp_int(raw_rd, 1, 3660)
|
||||
except (ValueError, TypeError):
|
||||
return None, None, "recent_days / days 必须为整数"
|
||||
else:
|
||||
recent_days = cfg_rd
|
||||
|
||||
return page_size, recent_days, None
|
||||
return year, None
|
||||
|
||||
|
||||
def _check_api_key():
|
||||
@@ -178,8 +180,8 @@ def _serialize_value(value):
|
||||
return value
|
||||
|
||||
|
||||
def _row_to_dict(row):
|
||||
return {k: _serialize_value(v) for k, v in row.items()}
|
||||
def _row_to_export_dict(row):
|
||||
return {col: _serialize_value(row.get(col)) for col in EXPORT_COLUMNS}
|
||||
|
||||
|
||||
def _db_connect():
|
||||
@@ -207,41 +209,26 @@ def list_student_changes():
|
||||
if not _check_api_key():
|
||||
return jsonify({"code": 401, "msg": "无效的密钥或未提供请求头 " + HEADER_API_KEY}), 401
|
||||
|
||||
try:
|
||||
page = int(request.args.get("page", "1"))
|
||||
except ValueError:
|
||||
return jsonify({"code": 400, "msg": "page 必须为整数"}), 400
|
||||
|
||||
if page < 1:
|
||||
page = 1
|
||||
|
||||
page_size, recent_days, param_err = _resolve_query_paging()
|
||||
filter_year, param_err = _resolve_filter_year()
|
||||
if param_err:
|
||||
return jsonify({"code": 400, "msg": param_err}), 400
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
# 近 recent_days 天:按视图中的「提交时间」筛选(登记/提交日期)
|
||||
where_recent = (
|
||||
"WHERE `{col}` >= DATE_SUB(CURDATE(), INTERVAL {days} DAY)".format(
|
||||
col=COL_SUBMIT_DATE, days=recent_days
|
||||
select_cols = ", ".join("`%s`" % col for col in EXPORT_COLUMNS)
|
||||
where_year = (
|
||||
"WHERE `{col}` IS NOT NULL AND YEAR(`{col}`) = %s".format(
|
||||
col=COL_EFFECTIVE_TIME
|
||||
)
|
||||
)
|
||||
sql_count = "SELECT COUNT(*) AS cnt FROM `{view}` {where_clause}".format(
|
||||
view=VIEW_NAME, where_clause=where_recent
|
||||
)
|
||||
sql_list = (
|
||||
"SELECT * FROM `{view}` {where_clause} "
|
||||
"ORDER BY `来源记录id` ASC LIMIT %s OFFSET %s"
|
||||
).format(view=VIEW_NAME, where_clause=where_recent)
|
||||
"SELECT {cols} FROM `{view}` {where_clause} "
|
||||
"ORDER BY `来源记录id` ASC"
|
||||
).format(cols=select_cols, view=VIEW_NAME, where_clause=where_year)
|
||||
|
||||
conn = None
|
||||
try:
|
||||
conn = _db_connect()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(sql_count)
|
||||
total = int(cur.fetchone()["cnt"])
|
||||
cur.execute(sql_list, (page_size, offset))
|
||||
cur.execute(sql_list, (filter_year,))
|
||||
rows = cur.fetchall()
|
||||
except pymysql.Error as e:
|
||||
return jsonify({"code": 500, "msg": "数据库查询失败", "detail": str(e)}), 500
|
||||
@@ -249,17 +236,15 @@ def list_student_changes():
|
||||
if conn is not None:
|
||||
conn.close()
|
||||
|
||||
data = [_row_to_dict(r) for r in rows]
|
||||
data = [_row_to_export_dict(r) for r in rows]
|
||||
return jsonify(
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": data,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total": total,
|
||||
"filter_recent_days": recent_days,
|
||||
"filter_by_column": COL_SUBMIT_DATE,
|
||||
"total": len(data),
|
||||
"filter_year": filter_year,
|
||||
"filter_by_column": COL_EFFECTIVE_TIME,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user