diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..0f84189 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,16 @@ +from flask import Flask + + +def create_app(config_object=None): + app = Flask(__name__) + + if config_object: + if isinstance(config_object, dict): + app.config.update(config_object) + else: + app.config.from_object(config_object) + + from app.api.health import bp as health_bp + + app.register_blueprint(health_bp) + return app diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..c8b0ac5 --- /dev/null +++ b/app/api/__init__.py @@ -0,0 +1 @@ +"""API blueprints.""" diff --git a/app/api/health.py b/app/api/health.py new file mode 100644 index 0000000..42e671b --- /dev/null +++ b/app/api/health.py @@ -0,0 +1,9 @@ +from flask import Blueprint, jsonify + + +bp = Blueprint("health", __name__) + + +@bp.get("/health") +def health_check(): + return jsonify({"ok": True}) diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..e40e554 --- /dev/null +++ b/app/config.py @@ -0,0 +1,2 @@ +class BaseConfig: + JSON_AS_ASCII = False diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 00f105c..4e85660 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -39,8 +39,8 @@ | 用途 | 命令 | | --- | --- | | 安装依赖 | `python -m pip install -r requirements.txt` | -| 本地开发 | 待定 | -| 测试 | 待定 | +| 本地开发 | `python run.py` | +| 测试 | `python -m unittest discover -s tests` | | 格式化 / 静态检查 | 待定 | 当前可用文档检查: diff --git a/docs/06-tasks.md b/docs/06-tasks.md index 6c696b5..fe12c5d 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -18,7 +18,7 @@ | ID | 任务 | 依赖 | 验收要点 | 状态 | | --- | --- | --- | --- | --- | -| T-001 | 初始化 Flask 项目骨架 | - | 使用当前系统 Python 3.8,不创建虚拟环境;创建 `requirements.txt` 并固定 `Flask==3.0.3`;使用 application factory 模式实现 `create_app(config_object=None)`;本地 health check API 可启动;真实命令同步到技术栈和当前状态 | TODO | +| T-001 | 初始化 Flask 项目骨架 | - | 使用当前系统 Python 3.8,不创建虚拟环境;创建 `requirements.txt` 并固定 `Flask==3.0.3`;使用 application factory 模式实现 `create_app(config_object=None)`;本地 health check API 可启动;真实命令同步到技术栈和当前状态 | DONE | | T-002 | 建立基础配置与目录 | T-001 | 目录符合架构文档;配置从环境变量读取;无真实密钥 | TODO | | T-003 | 建立最小测试框架 | T-001 | pytest 可运行;至少有 `create_app` / health check 测试,能通过测试配置创建 Flask app | TODO | diff --git a/docs/current-state.md b/docs/current-state.md index 93da48c..215bdb2 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -5,13 +5,13 @@ ## 当前快照 - 日期:2026-07-04 -- 阶段:MVP 起步 / 文档与逆向资料整理 -- 技术栈:目标为当前系统 Python 3.8、Flask 3.0.3 application factory、Redis;代码尚未初始化;不创建虚拟环境 -- 生产代码:暂无 -- 测试:暂无 +- 阶段:MVP 起步 / Flask 地基已初始化 +- 技术栈:当前系统 Python 3.8、Flask 3.0.3 application factory;不创建虚拟环境 +- 生产代码:`app/__init__.py`、`app/api/health.py`、`app/config.py`、`run.py` +- 测试:`tests/test_app_factory.py` - 数据 / 资料:`reverse_file/` 下已有 CHIS HAR、前端脚本和 schema;`D:\hans\chupd\chis\login_client_v2.py` 可作为登录链路参考 -- 标准启动路径:待 T-001 初始化后补齐 -- 标准验证路径:当前仅文档文件检查 +- 标准启动路径:`python run.py` +- 标准验证路径:`python -m unittest discover -s tests` - 当前 blocker:无硬阻塞;后续实现需要把 Django 登录代码迁移为 Flask 版本,并通过配置提供 CHIS public key ## 当前目录要点 @@ -22,17 +22,17 @@ | `reverse_file/` | 已有 | CHIS 逆向资料,当前重要事实来源 | | `reverse_file/20260704_query_health_check.har` | 已有 | 体检详情只读查询 HAR,包含 `getHMNIListOfHTML` 和 `getCheckInfoDetail` | | `D:\hans\chupd\chis\login_client_v2.py` | 外部参考 | CHIS 登录、SM2 加密、角色选择、cookie 拼接、`getLanderInfo` 会话验证 | -| `app/` | 待建 | Flask 应用代码 | -| `tests/` | 待建 | 测试目录 | -| `requirements.txt` | 待建 | Python 依赖 | +| `app/` | 已有 | Flask application factory 和 health check API | +| `tests/` | 已有 | `unittest` 测试 | +| `requirements.txt` | 已有 | Python 依赖,固定 `Flask==3.0.3` | ## 任务看板状态 任务状态以 [06-tasks.md](06-tasks.md) 为准。 -- 已完成:DOC-001 建立 harness coding 项目文档;DOC-002 更新 CHIS 登录与只读查询验证路线;DOC-003 明确使用系统 Python 3.8;DOC-004 明确 Flask factory 模式;DOC-005 明确日志与请求归档方案。详见 [../progress.md](../progress.md)。 +- 已完成:DOC-001 建立 harness coding 项目文档;DOC-002 更新 CHIS 登录与只读查询验证路线;DOC-003 明确使用系统 Python 3.8;DOC-004 明确 Flask factory 模式;DOC-005 明确日志与请求归档方案;T-001 初始化 Flask 项目骨架。详见 [../progress.md](../progress.md)。 - 正在进行:无。 -- 下一个可领取任务:T-001 初始化 Flask 项目骨架。 +- 下一个可领取任务:T-002 建立基础配置与目录。 ## 已确认技术事实 @@ -50,8 +50,9 @@ ## 当前可运行内容 ```powershell -Get-ChildItem -Recurse -File # PowerShell -# bash 等价: find . -type f -not -path './.git/*' +python -m pip install -r requirements.txt +python -m unittest discover -s tests +python run.py ``` ## 开始编码前检查 diff --git a/progress.md b/progress.md index 67ecafc..99e726b 100644 --- a/progress.md +++ b/progress.md @@ -70,3 +70,12 @@ - 阻塞:无。 - 决策:运行日志写入 `logs/`,每天一个综合日志文件,包含多级别摘要日志,保留 1 年;每个 API 请求在 `archives/` 写入一个完整原始 JSON 归档文件,包含 `api` 和 `chis` 请求 / 响应数组,archive 不脱敏,仅用于内网前置机本地受控排查;`logs/` 和 `archives/` 不提交 git。 - 下一步:T-001 初始化 Flask 项目骨架。 + +## 2026-07-04 T-001 初始化 Flask 项目骨架 + +- 状态:DONE +- 变更:新增 `requirements.txt`、`run.py`、`app/__init__.py`、`app/config.py`、`app/api/__init__.py`、`app/api/health.py`、`tests/test_app_factory.py`;更新任务状态、技术栈命令和当前状态。 +- 验证:先执行 `python -m unittest discover -s tests` 看到 `ModuleNotFoundError: No module named 'app'`;实现后执行 `python -m pip install -r requirements.txt` 成功,执行 `python -m unittest discover -s tests` 结果 `Ran 1 test ... OK`,临时启动 `python run.py` 后用 `curl.exe --noproxy "*" -s http://127.0.0.1:5000/health` 返回 `{"ok":true}`。 +- 阻塞:无。 +- 决策:T-001 采用标准库 `unittest` 做最小 factory / health check 验证;后续 T-003 再建立 pytest 测试框架。 +- 下一步:T-002 建立基础配置与目录。 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..95fef4e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask==3.0.3 diff --git a/run.py b/run.py new file mode 100644 index 0000000..e275906 --- /dev/null +++ b/run.py @@ -0,0 +1,8 @@ +from app import create_app + + +app = create_app() + + +if __name__ == "__main__": + app.run() diff --git a/tasks.md b/tasks.md index 7970f81..fa0b65d 100644 --- a/tasks.md +++ b/tasks.md @@ -10,7 +10,7 @@ MVP 起步。当前仓库已具备逆向资料和项目文档,尚未初始化 | ID | 任务 | 状态 | | --- | --- | --- | -| T-001 | 初始化 Flask 项目骨架 | TODO | +| T-001 | 初始化 Flask 项目骨架 | DONE | | T-002 | 建立基础配置与目录 | TODO | | T-003 | 建立最小测试框架 | TODO | | T-101 | 迁移 CHIS 登录链路 | TODO | diff --git a/tests/test_app_factory.py b/tests/test_app_factory.py new file mode 100644 index 0000000..a815849 --- /dev/null +++ b/tests/test_app_factory.py @@ -0,0 +1,18 @@ +import unittest + +from app import create_app + + +class AppFactoryTest(unittest.TestCase): + def test_health_check_returns_ok(self): + app = create_app({"TESTING": True}) + client = app.test_client() + + response = client.get("/health") + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.get_json(), {"ok": True}) + + +if __name__ == "__main__": + unittest.main()