#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 目录接口诊断脚本 - 检查正式环境 soulapi 的 /api/miniprogram/book/* 是否正常 用法: python scripts/test/check-catalog-api.py SOUL_TEST_ENV=soulapi python scripts/test/check-catalog-api.py """ import json import sys from pathlib import Path # 加载测试配置 sys.path.insert(0, str(Path(__file__).resolve().parent)) from config import API_BASE, ENV_LABEL, get_env_banner try: import urllib.request import urllib.error _urlopen = urllib.request.urlopen _Request = urllib.request.Request _HTTPError = urllib.error.HTTPError _URLError = urllib.error.URLError except ImportError: import urllib2 _urlopen = urllib2.urlopen _Request = urllib2.Request _HTTPError = urllib2.HTTPError _URLError = urllib2.URLError def fetch(url, timeout=10): """GET 请求,返回 (parsed_json, status_code, error_msg)""" try: req = _Request(url) req.get_method = lambda: "GET" req.add_header("Content-Type", "application/json") resp = _urlopen(req, timeout=timeout) body = resp.read().decode("utf-8", errors="replace") code = getattr(resp, "status", resp.getcode() if hasattr(resp, "getcode") else 200) try: data = json.loads(body) except json.JSONDecodeError: return None, code, "非 JSON 响应: " + body[:200] return data, code, None except _HTTPError as e: try: body = e.read().decode("utf-8", errors="replace") data = json.loads(body) if body else {} except Exception: data = {} return data, e.code, str(e) except _URLError as e: return None, None, str(e.reason) if hasattr(e, "reason") else str(e) except Exception as e: return None, None, str(e) def main(): print(get_env_banner()) base = API_BASE.rstrip("/") endpoints = [ ("/api/miniprogram/book/parts", "目录-篇章列表(目录页主接口)"), ("/api/miniprogram/book/all-chapters", "全书章节(app.loadBookData)"), ("/health", "健康检查"), ] all_ok = True for path, desc in endpoints: url = base + path print(f"\n--- {desc} ---") print(f"URL: {url}") data, code, err = fetch(url) if err: print("[FAIL] 请求失败:", err) all_ok = False continue if code and code != 200: print("[FAIL] HTTP", code) if data: print(f" 响应: {json.dumps(data, ensure_ascii=False)[:300]}") all_ok = False continue if not data: print("[FAIL] 无响应体") all_ok = False continue success = data.get("success") if path == "/health": status = data.get("status", "?") print("[OK] status=%s, version=%s" % (status, data.get("version", "?"))) elif path == "/api/miniprogram/book/parts": parts = data.get("parts") or [] total = data.get("totalSections", 0) fixed = data.get("fixedSections") or [] print("[OK] success=%s, parts=%d, totalSections=%d, fixedSections=%d" % (success, len(parts), total, len(fixed))) if not parts and total == 0: print(" [WARN] 篇章为空! 请检查正式环境数据库 chapters 表") elif parts: print(" 首篇章: id=%s, title=%s" % (parts[0].get("id"), parts[0].get("title"))) elif path == "/api/miniprogram/book/all-chapters": arr = data.get("data") or data.get("chapters") or [] print("[OK] success=%s, data=%d 条" % (success, len(arr))) if not arr: print(" [WARN] 章节列表为空! 请检查 chapters 表") elif arr: first = arr[0] if isinstance(arr[0], dict) else {} print(" 首条: id=%s, partTitle=%s" % (first.get("id"), first.get("partTitle"))) print("\n" + "=" * 50) if all_ok: print("[OK] 所有接口正常,若小程序仍无法加载,请检查:") print(" 1. 微信公众平台 → 服务器域名 → request 合法域名 是否包含 soulapi.quwanzhi.com") print(" 2. 正式版小程序 baseUrl 是否为 https://soulapi.quwanzhi.com") else: print("[FAIL] 存在异常,请根据上述输出排查后端或数据库") print("=" * 50) if __name__ == "__main__": main()