refactor(plugins): gdgxk 迁到 pugins/gdgxk,插件由目录扫描加载

- platforms/gdgxk.py → pugins/gdgxk/main.py(实现不变,仍是 class Gdgxk(Platform))
- pugins/gdgxk/pugin.json 只放注册信息 id / title / name,不声明任何能力绑定
- pugins/__init__.py:load_plugins() 扫 pugins/*/pugin.json、import 各自的 main.py,
  再用 pugin.json 的 id 校验注册结果;要换加载方式只改这一个函数
- platforms/__init__.py::load_builtin() 调 load_plugins();guanyixue 依赖 jspx/study,
  暂留 platforms/
- engine / routes / quiz / contract / 前端未改动
- 同步 AGENTS.md、docs/architecture.md、docs/OPTIMIZATION.md
This commit is contained in:
study-tool
2026-10-31 15:41:22 +08:00
parent bdd0bdf073
commit f1a57853f4
9 changed files with 62 additions and 19 deletions
+12 -9
View File
@@ -1,17 +1,20 @@
"""平台插件包。
"""平台包。
内置平台在 `load_builtin()` 里导入(导入即注册)。新增平台:
1. 新建 `platforms/<名字>.py`,写一个 `Platform` 子类:类属性声明 `id` / `label` /
`entries`,自己有的阶段实现成方法(见 `base.py`),末尾
`PLATFORM = registry.register(<子类>())`;
2. 在下面的 `load_builtin()` 里加一行 import;
3. 路由 / 引擎不用改 —— 它们只认 `course["platform"]`。
平台实现有两个落点,写法和契约完全一样(`Platform` 子类 + 注册一次):
- `pugins/<名字>/`:插件式平台(`pugin.json` + `main.py`),由 `study_tool.pugins.load_plugins()`
扫描加载,新增插件不需要改这里的任何代码;
- `platforms/<名字>.py`:跟共享实现模块(`jspx.py` / `study.py`)关系紧密的平台,直接在
`load_builtin()` 里 import。
路由 / 引擎不用改 —— 它们只认 `course["platform"]`。
"""
from study_tool.platforms import registry # noqa: F401 供 engine、路由使用
from study_tool.platforms.base import Platform # noqa: F401
def load_builtin():
"""导入内置平台,触发注册。幂等,app 启动时调用一次。"""
from study_tool.platforms import gdgxk # noqa: F401
"""导入内置平台与 `pugins/` 下的插件,触发注册。幂等,app 启动时调用一次。"""
from study_tool.platforms import guanyixue # noqa: F401
from study_tool.pugins import load_plugins
load_plugins()
+26
View File
@@ -0,0 +1,26 @@
"""插件目录:`pugins/<名字>/` = 一个平台插件。
pugin.json 注册信息:id / title / name(只写元数据,不写任何能力绑定)
main.py `Platform` 子类,import 时用 `registry.register(...)` 注册自己
加载 = 扫目录逐个 import `main.py`。插件里不用声明「哪个函数绑定哪个槽位」,
子类实现了哪些方法就具备哪些能力(映射见 `platforms/contract.py`)。
要换加载方式,只改这里的 `load_plugins()`。
"""
import importlib
import json
from pathlib import Path
from study_tool.platforms import registry
HERE = Path(__file__).resolve().parent
def load_plugins():
"""导入 `pugins/` 下的全部插件(导入即注册)。幂等,app 启动时调用一次。"""
for meta in sorted(HERE.glob("*/pugin.json")):
info = json.loads(meta.read_text(encoding="utf-8"))
name = meta.parent.name
importlib.import_module("%s.%s.main" % (__name__, name))
if not registry.get(info.get("id") or ""):
raise RuntimeError("插件 %s 没有注册 id=%r" % (name, info.get("id")))
+1
View File
@@ -0,0 +1 @@
"""广东公需课插件:实现在 `main.py`,注册信息在 `pugin.json`。"""
+5
View File
@@ -0,0 +1,5 @@
{
"id": "gdgxk",
"title": "广东省教师继续教育管理平台",
"name": "gdgxk"
}