批次6:D4文档治理收口 + D13锁文件流程固化
D4 文档/规划/历史混放收口:
- docs/TECH_DEBT.md §2 由'按批次回顾'精简为'按主题摘要',
与§3重复内容(批次0-4详细展开)整体迁入
docs/archive/2026-09_governance_batches.md
- docs/STATUS.md 顶部 2026-09-17 之前条目迁入
docs/archive/2026-09_status_history.md,仅保留指针
- D2 历史口径补齐为 2026-09-18 批次4后续专项清偿
- D4 标已清偿
- AGENTS.md / docs/archive/README.md 同步导航
D13 锁文件流程固化(镜像引入主体已清偿,仅剩锁文件落盘):
- 新增 deploy/generate_lockfiles.{sh,bat}:在 moldinsight conda
环境(仅项目依赖)执行 pip freeze --exclude pythonocc-core,
产出 deploy/requirements-{base,moldinsight}.lock.txt
- deploy/Dockerfile.moldinsight 注释改为指向生成脚本
- docs/OPERATIONS.md §2.1 增加完整流程说明
- tests/test_lockfile_generation.py 加锁文件存在性+体积契约;
tests/conftest.py 注册 --run-lockfile-check 选项,
默认 skip(仓库单测不阻塞),CI 镜像构建 job 显式启用 fail-fast
遗留:锁文件本身尚未落盘(本机Miniforge跨项目开发栈混装,
污染严重不能直接 pip freeze);待 CI / 生产首次构建时按流程落锁。
测试基线:126 passed, 9 skipped(默认4原有skip + D13新增5skip;
启用 --run-lockfile-check 时严格断言2项锁文件契约)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
- **单数据库是刻意设计**:moldinsight 与 inventory 共享同一 PostgreSQL(如 `STPFile.product_id -> Product.id` 桥接),不拆库。
|
- **单数据库是刻意设计**:moldinsight 与 inventory 共享同一 PostgreSQL(如 `STPFile.product_id -> Product.id` 桥接),不拆库。
|
||||||
- **接口变更三件套**:优先用 Pydantic 请求模型(少用手写 `request.json()` 解析)→ 重新导出根目录 `openapi.json` → 前端 `npm run gen:api` 重新生成类型。三步缺一即契约漂移。
|
- **接口变更三件套**:优先用 Pydantic 请求模型(少用手写 `request.json()` 解析)→ 重新导出根目录 `openapi.json` → 前端 `npm run gen:api` 重新生成类型。三步缺一即契约漂移。
|
||||||
- **历史材料统一进 [docs/archive/](docs/archive/README.md)**,不与当前权威文档混放。
|
- **历史材料统一进 [docs/archive/](docs/archive/README.md)**,不与当前权威文档混放。
|
||||||
|
- **历史批次详细流水账 / 早段 STATUS**:见 [docs/archive/2026-09_governance_batches.md](docs/archive/2026-09_governance_batches.md) 与 [docs/archive/2026-09_status_history.md](docs/archive/2026-09_status_history.md);主骨架权威文档(TECH_DEBT §2 / STATUS 顶部)只保留摘要。
|
||||||
- **配置只走 `.env`**(参照 [.env.example](.env.example) 全键说明):`DB_*`、`SECRET_KEY` 等关键项不设代码兜底(惰性校验,缺失即报),不在代码里给 localhost/弱口令默认值。
|
- **配置只走 `.env`**(参照 [.env.example](.env.example) 全键说明):`DB_*`、`SECRET_KEY` 等关键项不设代码兜底(惰性校验,缺失即报),不在代码里给 localhost/弱口令默认值。
|
||||||
|
|
||||||
## 3. 代码地图
|
## 3. 代码地图
|
||||||
@@ -120,7 +121,7 @@ frontend/ # Vue 3 独立工程:src/modules 按域组织
|
|||||||
migrations/ # 数据库迁移
|
migrations/ # 数据库迁移
|
||||||
scripts/ # 一次性迁移与工具脚本(migrations/ 数据迁移、db/ 索引与审计 SQL、tools/ 检查工具),非运行时代码
|
scripts/ # 一次性迁移与工具脚本(migrations/ 数据迁移、db/ 索引与审计 SQL、tools/ 检查工具),非运行时代码
|
||||||
tests/ # pytest:sqlite+aiosqlite 临时库;pythonocc 缺失时 OCC 契约测试自动 skip
|
tests/ # pytest:sqlite+aiosqlite 临时库;pythonocc 缺失时 OCC 契约测试自动 skip
|
||||||
deploy/ # Dockerfile.* / nginx / build 脚本
|
deploy/ # Dockerfile.* / nginx / build 脚本 / generate_lockfiles.{sh,bat}(D13 锁文件生成入口)
|
||||||
docs/ # 权威文档(本文件 §5 导航)
|
docs/ # 权威文档(本文件 §5 导航)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
# conda 运行时作为最终镜像的执行环境,自带全部动态库。
|
# conda 运行时作为最终镜像的执行环境,自带全部动态库。
|
||||||
FROM continuumio/miniconda3:24.7.1-0
|
FROM continuumio/miniconda3:24.7.1-0
|
||||||
|
|
||||||
# 锁定几何栈核心版本;pip 侧全量版本锁待首次镜像构建成功后由
|
# 锁定几何栈核心版本;pip 侧全量版本锁由
|
||||||
# `pip freeze > deploy/requirements-moldinsight.lock.txt` 生成(D13 遗留项)
|
# `bash deploy/generate_lockfiles.sh` 在 moldinsight conda 环境内执行后生成
|
||||||
|
# (D13,参见 deploy/generate_lockfiles.sh / .bat 与 docs/OPERATIONS.md §2)
|
||||||
|
# 落盘产物为 deploy/requirements-{base,moldinsight}.lock.txt,CI / 离线构建可直接锁定安装
|
||||||
RUN conda create -n moldinsight -c conda-forge -y \
|
RUN conda create -n moldinsight -c conda-forge -y \
|
||||||
python=3.12 \
|
python=3.12 \
|
||||||
pythonocc-core=7.9.0 \
|
pythonocc-core=7.9.0 \
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
@echo off
|
||||||
|
REM ============================================================
|
||||||
|
REM geMoldInsight pip 锁文件生成脚本(TECH_DEBT D13,Windows 版)
|
||||||
|
REM ============================================================
|
||||||
|
REM
|
||||||
|
REM 用法:在 gemold conda 环境内执行 deploy\generate_lockfiles.bat
|
||||||
|
REM
|
||||||
|
REM 产物:
|
||||||
|
REM deploy\requirements-base.lock.txt
|
||||||
|
REM deploy\requirements-moldinsight.lock.txt
|
||||||
|
REM ============================================================
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
cd /d "%~dp0\.."
|
||||||
|
|
||||||
|
where conda >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] conda 未安装或不在 PATH,请先激活 conda 环境 ^(推荐 moldinsight^) 1>&2
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if not "%CONDA_DEFAULT_ENV%"=="moldinsight" if not "%CONDA_DEFAULT_ENV%"=="gemold" (
|
||||||
|
echo [WARN] 当前 conda 环境为 '%CONDA_DEFAULT_ENV%',推荐在 'moldinsight' 内执行
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ==^> 生成 base 锁文件
|
||||||
|
pip freeze --exclude pythonocc-core > deploy\requirements-base.lock.txt
|
||||||
|
|
||||||
|
echo ==^> 生成 moldinsight 锁文件
|
||||||
|
pip freeze --exclude pythonocc-core > deploy\requirements-moldinsight.lock.txt
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo 已生成:
|
||||||
|
echo deploy\requirements-base.lock.txt
|
||||||
|
echo deploy\requirements-moldinsight.lock.txt
|
||||||
|
echo.
|
||||||
|
echo 下一步:提交两个 lock.txt,并按团队策略同步更新 requirements-*.txt 下限。
|
||||||
|
endlocal
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# geMoldInsight pip 锁文件生成脚本(TECH_DEBT D13)
|
||||||
|
# ============================================================
|
||||||
|
#
|
||||||
|
# 设计要点:
|
||||||
|
# - 锁文件必须在构建产出的 conda/minimal 环境里生成(只有 geMoldInsight 依赖 + 基础库),
|
||||||
|
# 本机开发环境的 `pip freeze` 会污染(全开发栈混装),不能直接落锁。
|
||||||
|
# - 仅在带 moldinsight 的 conda 环境内运行 `pip freeze > deploy/requirements-<x>.lock.txt`
|
||||||
|
# 才有意义。
|
||||||
|
# - 锁文件落盘后即可被 CI / 离线构建 / 复现部署直接 `pip install -r` 锁定版本,
|
||||||
|
# 而不再依赖 >= 下限解析。
|
||||||
|
#
|
||||||
|
# 用法(必须在 `gemold` conda 环境内执行):
|
||||||
|
# bash deploy/generate_lockfiles.sh
|
||||||
|
#
|
||||||
|
# 产物:
|
||||||
|
# deploy/requirements-base.lock.txt
|
||||||
|
# deploy/requirements-moldinsight.lock.txt
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
if ! command -v conda >/dev/null 2>&1; then
|
||||||
|
echo "[ERROR] conda 未安装或不在 PATH,请先激活 conda 环境(推荐环境名 moldinsight)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${CONDA_DEFAULT_ENV:-}" != "moldinsight" && "${CONDA_DEFAULT_ENV:-}" != "gemold" ]]; then
|
||||||
|
echo "[WARN] 当前 conda 环境为 '${CONDA_DEFAULT_ENV:-<未激活>}'," \
|
||||||
|
"推荐在 'moldinsight' conda 环境内运行(否则锁文件将含宿主污染)" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> 生成 base 锁文件"
|
||||||
|
pip freeze --exclude pythonocc-core > deploy/requirements-base.lock.txt
|
||||||
|
|
||||||
|
echo "==> 生成 moldinsight 锁文件"
|
||||||
|
pip freeze --exclude pythonocc-core > deploy/requirements-moldinsight.lock.txt
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "已生成:"
|
||||||
|
echo " deploy/requirements-base.lock.txt ($(wc -l < deploy/requirements-base.lock.txt) 行)"
|
||||||
|
echo " deploy/requirements-moldinsight.lock.txt ($(wc -l < deploy/requirements-moldinsight.lock.txt) 行)"
|
||||||
|
echo
|
||||||
|
echo "下一步:"
|
||||||
|
echo " 1. 提交这两个 lock.txt(仅含项目直接依赖 + conda-minimal 环境产出)"
|
||||||
|
echo " 2. 同步更新 deploy/requirements-base.txt / requirements-moldinsight.txt 的版本下限" \
|
||||||
|
"为 lock 中的实际版本(或保留 >=,按团队策略)"
|
||||||
+15
-1
@@ -26,10 +26,24 @@
|
|||||||
## 2. 安装与环境
|
## 2. 安装与环境
|
||||||
|
|
||||||
- 后端依赖:`pip install -r requirements.txt`。
|
- 后端依赖:`pip install -r requirements.txt`。
|
||||||
- **OCC 几何能力**:PythonOCC 不走 pip 主路径,通过 conda 环境提供(本项目实践环境名 `gemold`)。无 OCC 环境时项目可启动,但几何分析契约测试自动 skip。
|
- **OCC 几何能力**:PythonOCC 不走 pip 主路径,通过 conda 环境提供(本项目实践环境名 `gemold` 或 `moldinsight`)。无 OCC 环境时项目可启动,但几何分析契约测试自动 skip。
|
||||||
- 前端:`cd frontend && npm install`。
|
- 前端:`cd frontend && npm install`。
|
||||||
- 数据库迁移:`migrations/`(`alembic.ini` 在仓库根;2026-09-16 由 `alembic/` 改名——原目录名与 alembic 包重名,应用内 import 会被遮蔽导致启动期迁移静默失败);数据修复类一次性脚本在 `scripts/migrations/` 与 `scripts/db/`,**不是运行时代码**,勿在服务内引用。
|
- 数据库迁移:`migrations/`(`alembic.ini` 在仓库根;2026-09-16 由 `alembic/` 改名——原目录名与 alembic 包重名,应用内 import 会被遮蔽导致启动期迁移静默失败);数据修复类一次性脚本在 `scripts/migrations/` 与 `scripts/db/`,**不是运行时代码**,勿在服务内引用。
|
||||||
|
|
||||||
|
### 2.1 pip 锁文件生成(D13 流程)
|
||||||
|
|
||||||
|
`deploy/requirements-{base,moldinsight}.lock.txt` 是项目依赖的**版本锁**,由 conda 环境首次构建成功后一次性落盘:
|
||||||
|
|
||||||
|
- **生成时机**:在 `moldinsight` / `gemold` conda 环境(仅含项目依赖 + conda 基础库,**不能**在混装全开发栈的本机 pip 环境跑)执行 `pip freeze`
|
||||||
|
- **生成命令**:
|
||||||
|
- Linux / macOS:`bash deploy/generate_lockfiles.sh`
|
||||||
|
- Windows:`deploy\generate_lockfiles.bat`
|
||||||
|
- **产物**:
|
||||||
|
- `deploy/requirements-base.lock.txt`
|
||||||
|
- `deploy/requirements-moldinsight.lock.txt`
|
||||||
|
- **消费方**:CI、离线构建、生产复现部署;`pip install -r deploy/requirements-base.lock.txt` 可直接锁定安装而不依赖 `>=` 解析
|
||||||
|
- **提交策略**:两个 lock.txt 提交到仓库;版本下限(`requirements-{base,moldinsight}.txt`)按团队策略同步或保留 `>=` 灵活解析
|
||||||
|
|
||||||
## 3. 本地启动
|
## 3. 本地启动
|
||||||
|
|
||||||
后端三入口(均含 sys.path 修正,可从仓库根直接跑):
|
后端三入口(均含 sys.path 修正,可从仓库根直接跑):
|
||||||
|
|||||||
@@ -2,9 +2,14 @@
|
|||||||
|
|
||||||
> 文档定位:**唯一的「现在到哪了」**。README / AGENTS / 各主文档只链接到这里,不复制状态内容。
|
> 文档定位:**唯一的「现在到哪了」**。README / AGENTS / 各主文档只链接到这里,不复制状态内容。
|
||||||
> 维护规则:每完整完成一个需求,**倒序在本文顶部加一条**(日期 + 主题 + 关键事实);其余主文档(架构 / 规划 / 技术债 / 部署)维护各自的"当前有效说法",本文只记录"什么时候做到了哪一步"。维护规则出处见根目录 [AGENTS.md](../AGENTS.md)。
|
> 维护规则:每完整完成一个需求,**倒序在本文顶部加一条**(日期 + 主题 + 关键事实);其余主文档(架构 / 规划 / 技术债 / 部署)维护各自的"当前有效说法",本文只记录"什么时候做到了哪一步"。维护规则出处见根目录 [AGENTS.md](../AGENTS.md)。
|
||||||
|
> 早期条目(2026-09-17 之前)已精简为锚点,完整流水见 [archive/2026-09_governance_batches.md](archive/2026-09_governance_batches.md) 与 [archive/2026-09_status_history.md](archive/2026-09_status_history.md)。
|
||||||
|
|
||||||
> 2026-09-22(**Pydantic v2 schema 配置升级 + `datetime.utcnow()` 弃用清零**:① 全仓 14 处 `class Config`([src/inventory/schemas](../src/inventory/schemas/))+ [src/shared/services/auth_routes.py](../src/shared/services/auth_routes.py) 三处全部迁移到 `model_config = ConfigDict(from_attributes=True)`;② [src/shared/services/auth_service.py](../src/shared/services/auth_service.py) 中 `datetime.utcnow()` 改用 `datetime.now(timezone.utc)`,消除遗留 `DeprecationWarning`;③ 一次跑通 `pytest tests/ -q` 全量无 deprecation 警告,全仓 `from_attributes=True` 语义保持不变,未触发 OpenAPI 漂移。**测试基线**:**126 passed, 4 skipped**(与上一批次一致,无回归)。)
|
> 2026-09-22(**Pydantic v2 schema 配置升级 + `datetime.utcnow()` 弃用清零**:① 全仓 14 处 `class Config`([src/inventory/schemas](../src/inventory/schemas/))+ [src/shared/services/auth_routes.py](../src/shared/services/auth_routes.py) 三处全部迁移到 `model_config = ConfigDict(from_attributes=True)`;② [src/shared/services/auth_service.py](../src/shared/services/auth_service.py) 中 `datetime.utcnow()` 改用 `datetime.now(timezone.utc)`,消除遗留 `DeprecationWarning`;③ 一次跑通 `pytest tests/ -q` 全量无 deprecation 警告,全仓 `from_attributes=True` 语义保持不变,未触发 OpenAPI 漂移。**测试基线**:**126 passed, 4 skipped**(与上一批次一致,无回归)。)
|
||||||
>
|
>
|
||||||
|
> 2026-09-22(**D13 锁文件流程固化:镜像引入已清偿 + 落锁流程就绪**:① 新增 [deploy/generate_lockfiles.sh](../deploy/generate_lockfiles.sh) / [.bat](../deploy/generate_lockfiles.bat):在 moldinsight conda 环境(仅项目依赖,**不能**在混装开发栈跑)执行 `pip freeze --exclude pythonocc-core`,产出 `deploy/requirements-{base,moldinsight}.lock.txt`;② [Dockerfile.moldinsight](../deploy/Dockerfile.moldinsight) 注释改为指向生成脚本;③ [docs/OPERATIONS.md](../docs/OPERATIONS.md) §2.1 增加完整流程说明(生成时机 / 命令 / 产物 / 消费方 / 提交策略);④ [tests/test_lockfile_generation.py](../tests/test_lockfile_generation.py) 加锁文件存在性 + 体积契约,默认 skip(仓库单测不阻塞),CI 镜像构建 job 显式 `pytest --run-lockfile-check` 启用 fail-fast。**遗留**:锁文件本身尚未落盘——本机 Miniforge 装的是跨项目开发栈混装环境,污染严重不能直接用 `pip freeze`;须等 CI / 生产机器首次构建 moldinsight 镜像后按流程落锁。**测试基线**:**126 passed, 9 skipped**(默认 4 个原有 skip + D13 新增 5 个 skip;启用 `--run-lockfile-check` 时严格断言 2 项锁文件契约)。)
|
||||||
|
>
|
||||||
|
> 2026-09-02 起(含 09-17 之前基线条目)的完整内容见 [archive/2026-09_status_history.md](archive/2026-09_status_history.md)。
|
||||||
|
>
|
||||||
> 2026-09-21(**inventory 仪表盘聚合服务下沉完成:dashboard 薄路由化**:① 新增 [dashboard_service.py](../src/inventory/services/dashboard_service.py),将仪表盘首页所需的基础主数据统计、物料库存总量/总值、待处理采购/销售单数、低库存预警列表等聚合查询从路由层下沉到 service;② [dashboard_routes.py](../src/inventory/api/dashboard_routes.py) 改为单行委托薄路由,inventory 主要业务域路由已基本完成 service orchestration 收口;③ 新增 [test_api_dashboard_service.py](../tests/test_api_dashboard_service.py),覆盖 seeded summary 与低库存预警两条 API 回归。**接口面零变化**(无 openapi 漂移)。**测试基线**:**126 passed, 4 skipped**;新增 dashboard 回归 **2 passed**。)
|
> 2026-09-21(**inventory 仪表盘聚合服务下沉完成:dashboard 薄路由化**:① 新增 [dashboard_service.py](../src/inventory/services/dashboard_service.py),将仪表盘首页所需的基础主数据统计、物料库存总量/总值、待处理采购/销售单数、低库存预警列表等聚合查询从路由层下沉到 service;② [dashboard_routes.py](../src/inventory/api/dashboard_routes.py) 改为单行委托薄路由,inventory 主要业务域路由已基本完成 service orchestration 收口;③ 新增 [test_api_dashboard_service.py](../tests/test_api_dashboard_service.py),覆盖 seeded summary 与低库存预警两条 API 回归。**接口面零变化**(无 openapi 漂移)。**测试基线**:**126 passed, 4 skipped**;新增 dashboard 回归 **2 passed**。)
|
||||||
>
|
>
|
||||||
> 2026-09-21(**inventory 产品域跨模块桥接收口完成:`/api/products/from-task/{task_id}` 下沉至 `product_service`**:① [product_service.py](../src/inventory/services/product_service.py) 新增 `create_product_from_task`,将 ProcessingTask / STPFile 查询、已绑定成品幂等返回、`MI{stp_file_id}` SKU 冲突递增、分析结果摘要拼装、成品创建与 `stp_files.product_id` 回写从路由层下沉到 service;② [product_routes.py](../src/inventory/api/product_routes.py) 现已全量薄路由化,产品域 CRUD / BOM / from-task 三类接口统一改为 service orchestration;③ 扩展 [test_api_product_service.py](../tests/test_api_product_service.py) 与 [tests/conftest.py](../tests/conftest.py),补 `STPFile` / `ProcessingTask` 种子及 from-task 创建、重复调用幂等、任务不存在 404 回归。**接口面零变化**(无 openapi 漂移)。**测试基线**:**124 passed, 4 skipped**;product 域回归现为 **14 passed**。)
|
> 2026-09-21(**inventory 产品域跨模块桥接收口完成:`/api/products/from-task/{task_id}` 下沉至 `product_service`**:① [product_service.py](../src/inventory/services/product_service.py) 新增 `create_product_from_task`,将 ProcessingTask / STPFile 查询、已绑定成品幂等返回、`MI{stp_file_id}` SKU 冲突递增、分析结果摘要拼装、成品创建与 `stp_files.product_id` 回写从路由层下沉到 service;② [product_routes.py](../src/inventory/api/product_routes.py) 现已全量薄路由化,产品域 CRUD / BOM / from-task 三类接口统一改为 service orchestration;③ 扩展 [test_api_product_service.py](../tests/test_api_product_service.py) 与 [tests/conftest.py](../tests/conftest.py),补 `STPFile` / `ProcessingTask` 种子及 from-task 创建、重复调用幂等、任务不存在 404 回归。**接口面零变化**(无 openapi 漂移)。**测试基线**:**124 passed, 4 skipped**;product 域回归现为 **14 passed**。)
|
||||||
|
|||||||
+37
-70
@@ -18,59 +18,33 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 已完成的重要治理(摘要)
|
## 2. 已完成的重要治理(主题摘要)
|
||||||
|
|
||||||
以下高价值治理已完成:
|
按主题归类的高价值治理已完成项。每项的具体修复清单 / 迁移号 / 回归测试 / 测试基线见归档:
|
||||||
|
|
||||||
|
- [archive/2026-09_governance_batches.md](archive/2026-09_governance_batches.md):批次 0–4 + 后续专项 + 2026-09-21 inventory 服务下沉 + 2026-09-22 schema/datetime 弃用清零 的完整流水账
|
||||||
|
- [archive/MOLDINSIGHT_TECH_DEBT_PLAN.md](archive/MOLDINSIGHT_TECH_DEBT_PLAN.md):更早的设计审查原始计划
|
||||||
|
|
||||||
### 2.1 安全与权限
|
### 2.1 安全与权限
|
||||||
- debug/history 路由补鉴权
|
- debug / history 路由补鉴权;任务访问控制收紧;无主数据不再默认放行
|
||||||
- 任务访问控制收紧
|
- `/api/status/{task_id}` 补 JWT 鉴权与归属校验(原 D5 → §3 D5)
|
||||||
- 无主数据不再默认放行
|
- bcrypt 超 72 字节显式拒绝 + 截断比较;`SECRET_KEY` / `RUSTFS_*` 缺失明确报错
|
||||||
- `/api/status/{task_id}` 补 JWT 鉴权与归属校验(原 D5,2026-09-16 清偿,见 D5 条目)
|
|
||||||
- bcrypt 创建口令超 72 字节显式拒绝、验证侧截断比较;`SECRET_KEY` / `RUSTFS_*` 缺失时明确报错,代码侧弱默认移除(D14 部分,2026-09-16)
|
|
||||||
|
|
||||||
### 2.2 静默失败与可用性
|
### 2.2 静默失败与可用性
|
||||||
- `detect-undercuts` 改为基于真实 shape 分析
|
- `detect-undercuts` 改为基于真实 shape 分析
|
||||||
- OCC 超时后重建 executor,避免全队列永久堵死
|
- OCC 超时后重建 executor(短期)→ D10 方案 B 进程化彻底替换
|
||||||
- 后台任务统一分派,补强引用与并发控制
|
- 后台任务统一分派,补强引用与并发控制
|
||||||
|
|
||||||
### 2.3 状态存储与缓存
|
### 2.3 状态存储与缓存
|
||||||
- Redis 任务状态改为 Hash 字段级更新,兼容旧格式
|
- Redis 任务状态改为 Hash 字段级更新,兼容旧格式
|
||||||
- 完成态任务视图增加缓存
|
- 内存回退彻底删除,PG 为任务状态单一事实源(原 D7)
|
||||||
- 导出缓存与持久化链路收口,支持重启后再导出
|
- 完成态任务视图缓存;导出缓存与持久化链路收口
|
||||||
|
|
||||||
### 2.4 架构与代码清理
|
### 2.4 架构与代码清理
|
||||||
- 删除旧单体入口与死代码
|
- 删除旧单体入口与死代码(`db_manager.create_tables` / `log_user_activity` / `CADExporter.export_mold_results` / `getAluminumPrice` 等)
|
||||||
- 设置惰性配置校验,提升可测试性
|
- 惰性配置校验,提升可测试性
|
||||||
- Generator 公共接口提取完成,补充契约测试
|
- Generator 公共接口提取 + 契约测试
|
||||||
|
- 共享 ORM 按模块拆分,跨模块桥接收敛为裸 FK 硬规则(ARCHITECTURE §5.1)
|
||||||
### 2.5 部署正确性(2026-09-16,批次 0/1)
|
|
||||||
- `/api/status/{task_id}` 补鉴权与归属校验(原 D5)
|
|
||||||
- 主处理链路改走 RustFS:分派入参 `stp_file_id` 化,源文件按 object_key 下载;compose 共享卷过渡兜底(原 D6)
|
|
||||||
- `AUTO_MIGRATE` 开关 + 迁移脚本随镜像分发 + `alembic/`→`migrations/` 改名修复包遮蔽(原 D12)
|
|
||||||
- OCC 镜像改 conda 运行时原生执行、基础镜像 tag 锁定(D13 主体);compose 关键项去弱默认(D14 部分)
|
|
||||||
|
|
||||||
### 2.6 任务一致性模型(2026-09-16,批次 2)
|
|
||||||
- Redis 内存回退彻底删除,PG 为任务状态单一事实源(原 D7);批量元数据入库(`processing_tasks.batch_id`,迁移 `a3f8c2d91e47`)
|
|
||||||
- 型腔生成失败任务标 failed,不再静默 completed(原 D8)
|
|
||||||
- 持久化事务边界收口:数据本体分阶段原子提交、失败先回滚再置 failed(原 D9)
|
|
||||||
- D11(HTML 双写双读)本批未动:正确性已由共享卷兜底,RustFS 单一来源留待后续批次
|
|
||||||
|
|
||||||
### 2.7 API 与代码结构(2026-09-17,批次 3)
|
|
||||||
- `advanced_router` 按职责拆为 design / cost / machining / export 四个子路由,端点路径不变,请求体全量 Pydantic 化(原 D1)
|
|
||||||
- 路由装载失败显式化:`ROUTE_MODULES` 清单 + route_registry,失败经 `/api/health` 呈现 degraded(含真实 pythonocc 探测),DEBUG 下 fail fast
|
|
||||||
- 纯 Python 重计算端点(设计/加工/CAM 打包)统一 `asyncio.to_thread` 投放线程池,不再阻塞事件循环;OCC 操作仍走单线程 executor(D10 不变,批次 4)
|
|
||||||
- `StorageIntegrationService`(867 行)按职责拆为 TaskStorage / AnalysisStorage / FileHistory 三服务;无调用方的 `log_user_activity` 死代码删除
|
|
||||||
- 配置治理收尾:`MAX_FILE_SIZE` 接线生效、celery_app 复用 `Settings.redis_url`(原 D14)
|
|
||||||
- 连带修复:管理员重置密码改 JSON body(原裸 str 参数被解析为 query param,前端发 body 必 422,功能端到端断裂);Dockerfile.celery 的 FROM tag 与 compose/build.sh 实际构建的 `gemold-backend:latest` 对齐(此前干净环境 celery 镜像必构建失败)
|
|
||||||
- 接口变更三件套随批完成:openapi.json 重导出(76 paths)+ 前端 `gen:api`
|
|
||||||
|
|
||||||
### 2.8 架构演进(2026-09-17,批次 4)
|
|
||||||
- 共享 ORM 按模块拆分(原 D3 主体):891 行 `shared/models/database.py`(31 模型类三类同居)拆为 `shared/models/base.py`(唯一 Base + 归属约定)/ `shared/models/identity.py`(7 表)/ `moldinsight/models/`(9 表)/ `inventory/models/`(catalog/warehouse/trading/finance 15 表);**三条跨模块 ORM relationship(`User.stp_files`、`STPFile.user`、`STPFile.product`)经全仓核实均无使用方,直接删除**——跨模块桥接收敛为裸 FK 硬规则(ARCHITECTURE §5.1),单模块部署不再依赖另一侧模型注册;约 45 处 import 全量改写,无兼容 facade;全量注册点收敛为 migrations/env.py 与 tests/conftest.py;零调用方的死方法 `db_manager.create_tables` 一并删除(拆分后会静默建残缺 schema)
|
|
||||||
- OCC 泄漏治理 + 吞吐方案设计先行(原 D10):`_reset_occ_executor` 补 `cancel_futures=True`——不止卫生问题:旧实现下"慢恢复"的旧线程会继续消化旧队列,与新 executor **并发操作非线程安全的 OCC**(数据竞争);吞吐路线定稿于 [topics/performance/OCC_THROUGHPUT.md](topics/performance/OCC_THROUGHPUT.md)(短期 A:celery prefork 伸缩 + max-tasks-per-child 兜底;中期 B:run_occ 接口进程化 + kill-on-timeout 根治)
|
|
||||||
- 归属边界回归测试:[tests/test_model_ownership.py](../tests/test_model_ownership.py)(31 表全量注册、单模块独立 mapper 配置、旧模块无 facade)
|
|
||||||
|
|
||||||
详细历史过程保留在原始技术债文档中,后续将转入归档。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -86,24 +60,19 @@
|
|||||||
|
|
||||||
~~原现状 / 影响~~:导出/估算/设计接口混在单文件,边界不清晰、OpenAPI 可读性差、参数校验不统一。
|
~~原现状 / 影响~~:导出/估算/设计接口混在单文件,边界不清晰、OpenAPI 可读性差、参数校验不统一。
|
||||||
|
|
||||||
### D2. 铝价模拟数据未显式标注来源
|
### D2. 铝价模拟数据未显式标注来源 —— 已清偿(2026-09-18,批次 4 后续专项)
|
||||||
|
|
||||||
现状:
|
修复内容(保留编号以维持引用稳定):
|
||||||
- 铝价服务返回的是模拟/参考数据,但接口层未明确表达
|
- [src/moldinsight/services/aluminum_price_service.py](../src/moldinsight/services/aluminum_price_service.py):`get_aluminum_current_price` 响应补 `source: "simulated"`,`get_aluminum_price_history` 逐项补同字段
|
||||||
|
- [frontend/src/modules/home/HomeView.vue](../frontend/src/modules/home/HomeView.vue):按 `source` 字段渲染"模拟数据 · 参考走势,非实时行情"标注(不再硬编码"上海期货交易所"等虚假来源)
|
||||||
|
- 死代码 `getAluminumPrice` 删除(前端此前保留了一份本地硬编码函数,已无调用方)
|
||||||
|
|
||||||
影响:
|
~~原现状 / 影响~~:铝价接口返回走势数据但无来源声明,前端原硬编码"上海期货交易所"字样,与实际模拟数据不一致,属虚假来源声明。
|
||||||
- 容易误导前端与业务使用者,把模拟数据理解为实时行情
|
|
||||||
|
|
||||||
建议:
|
|
||||||
- 响应增加 `source: "simulated"`
|
|
||||||
- 前端界面同步标注“模拟/参考数据”
|
|
||||||
|
|
||||||
优先级:**P2**
|
|
||||||
|
|
||||||
### D3. shared/platform 边界仍需继续收敛 —— 主体已清偿(2026-09-17 批次 4 + 2026-09-18 后续)
|
### D3. shared/platform 边界仍需继续收敛 —— 主体已清偿(2026-09-17 批次 4 + 2026-09-18 后续)
|
||||||
|
|
||||||
已完成部分:
|
已完成部分(修复文件清单见 [archive/2026-09_governance_batches.md](archive/2026-09_governance_batches.md) 批次 4 / 后续专项):
|
||||||
- 共享 ORM(原最强耦合点)按模块拆分:base / identity(shared)+ moldinsight/models + inventory/models;跨模块只允许裸 FK,单模块部署 mapper 可独立配置(详见 §2.8 与 [ARCHITECTURE.md](ARCHITECTURE.md) §6.1)
|
- 共享 ORM(原最强耦合点)按模块拆分:base / identity(shared)+ moldinsight/models + inventory/models;跨模块只允许裸 FK,单模块部署 mapper 可独立配置(详见 [ARCHITECTURE.md](ARCHITECTURE.md) §6.1)
|
||||||
- 旧 `shared/models/database.py` 物理删除,无兼容 facade;归属边界由 [tests/test_model_ownership.py](../tests/test_model_ownership.py) 锁定
|
- 旧 `shared/models/database.py` 物理删除,无兼容 facade;归属边界由 [tests/test_model_ownership.py](../tests/test_model_ownership.py) 锁定
|
||||||
- **app_factory 组合职责收敛**(2026-09-18):平台工厂只做纯平台引导,`connect_rustfs` 参数移除;moldinsight 专属接线(RustFS 启动钩子 [init_storage.py](../src/moldinsight/storage/init_storage.py) `rustfs_startup_hook`、路由单点聚合 [moldinsight/api/__init__.py](../src/moldinsight/api/__init__.py) `register_moldinsight_routers`)收敛回模块层,入口退化为纯组装(ARCHITECTURE §6.2)
|
- **app_factory 组合职责收敛**(2026-09-18):平台工厂只做纯平台引导,`connect_rustfs` 参数移除;moldinsight 专属接线(RustFS 启动钩子 [init_storage.py](../src/moldinsight/storage/init_storage.py) `rustfs_startup_hook`、路由单点聚合 [moldinsight/api/__init__.py](../src/moldinsight/api/__init__.py) `register_moldinsight_routers`)收敛回模块层,入口退化为纯组装(ARCHITECTURE §6.2)
|
||||||
|
|
||||||
@@ -112,21 +81,14 @@
|
|||||||
|
|
||||||
优先级:**P3**(仅剩 identity/platform 语义注释口径)
|
优先级:**P3**(仅剩 identity/platform 语义注释口径)
|
||||||
|
|
||||||
### D4. 文档现状 / 规划 / 历史混放
|
### D4. 文档现状 / 规划 / 历史混放 —— 已清偿(2026-09-22)
|
||||||
|
|
||||||
现状:
|
修复内容(保留编号以维持引用稳定):
|
||||||
- 文档存在部署说明重叠、计划/总结/权威文档混放
|
- 已建立 `STATUS / ARCHITECTURE / ROADMAP / TECH_DEBT / DEPLOYMENT` 主骨架,每类信息单一归属;历史材料归档至 `docs/archive/`
|
||||||
- README 承担过多职责
|
- 本文档 §2 由"按批次回顾"精简为"按主题摘要",修复文件清单 / 迁移号 / 回归测试 / 测试基线等详细流水账整体迁入 [archive/2026-09_governance_batches.md](archive/2026-09_governance_batches.md)(避免与 §3 重复膨胀)
|
||||||
|
- §3 中对历史批次的引用(如 D3 → §2.8)改为 archive 指针;D2 等已清偿项补齐时间戳
|
||||||
|
|
||||||
影响:
|
~~原现状 / 影响~~:TECH_DEBT §2 与 §3 内容重复膨胀,文档目录结构清晰度受新成员评估影响。
|
||||||
- 新成员难以判断“哪篇才是当前有效说法”
|
|
||||||
- 状态、部署、规划容易发生漂移
|
|
||||||
|
|
||||||
建议:
|
|
||||||
- 建立 `STATUS / ARCHITECTURE / ROADMAP / DEPLOYMENT` 主骨架
|
|
||||||
- 历史材料迁入 `docs/archive/`
|
|
||||||
|
|
||||||
优先级:**P1**
|
|
||||||
|
|
||||||
### D5. `/api/status/{task_id}` 未鉴权(安全缺口)—— 已清偿(2026-09-16,批次 0)
|
### D5. `/api/status/{task_id}` 未鉴权(安全缺口)—— 已清偿(2026-09-16,批次 0)
|
||||||
|
|
||||||
@@ -222,13 +184,18 @@
|
|||||||
- 语义保持:仅切换 Pydantic v2 配置语法 + UTC 时区语义,字段 / OpenAPI / JWT 行为零变化
|
- 语义保持:仅切换 Pydantic v2 配置语法 + UTC 时区语义,字段 / OpenAPI / JWT 行为零变化
|
||||||
- 验证:`pytest tests/ -q` **126 passed, 4 skipped**,deprecation warning 全部清零
|
- 验证:`pytest tests/ -q` **126 passed, 4 skipped**,deprecation warning 全部清零
|
||||||
|
|
||||||
### D13. PythonOCC 镜像引入方式脆弱 + 依赖无版本锁(主体已清偿,锁文件遗留)
|
### D13. PythonOCC 镜像引入方式脆弱 + 依赖无版本锁(镜像引入已清偿;锁文件流程已固化,待首次构建落盘)
|
||||||
|
|
||||||
现状:
|
现状:
|
||||||
- ~~从 conda env 拷贝 site-packages 进 python:3.12-slim~~(2026-09-16 已修正:[Dockerfile.moldinsight](../deploy/Dockerfile.moldinsight) 改为 conda 运行时原生执行,不再跨镜像拷贝;基础镜像 tag 锁定 `continuumio/miniconda3:24.7.1-0`、`python:3.12-slim-bookworm`;tag 可用性随下次镜像构建验证)
|
- ~~从 conda env 拷贝 site-packages 进 python:3.12-slim~~(2026-09-16 已修正:[Dockerfile.moldinsight](../deploy/Dockerfile.moldinsight) 改为 conda 运行时原生执行,不再跨镜像拷贝;基础镜像 tag 锁定 `continuumio/miniconda3:24.7.1-0`、`python:3.12-slim-bookworm`;tag 可用性随下次镜像构建验证)
|
||||||
- [requirements.txt](../requirements.txt) 全部为 `>=` 下限,无锁文件(**遗留**:首次镜像构建成功后 `pip freeze` 生成锁文件,命令已注释在 Dockerfile 内)
|
- 锁文件流程已固化(2026-09-22):
|
||||||
|
- 新增 [deploy/generate_lockfiles.sh](../deploy/generate_lockfiles.sh) / [generate_lockfiles.bat](../deploy/generate_lockfiles.bat):在 moldinsight conda 环境(仅项目依赖,**不能**在混装开发栈跑)执行 `pip freeze --exclude pythonocc-core`,产出 `deploy/requirements-{base,moldinsight}.lock.txt`
|
||||||
|
- [Dockerfile.moldinsight](../deploy/Dockerfile.moldinsight) 注释改为指向生成脚本
|
||||||
|
- [docs/OPERATIONS.md](../docs/OPERATIONS.md) §2.1 增加完整流程说明(生成时机 / 命令 / 产物 / 消费方 / 提交策略)
|
||||||
|
- [tests/test_lockfile_generation.py](../tests/test_lockfile_generation.py) 加锁文件存在性 + 体积契约;默认 skip(仓库单测不阻塞),CI 镜像构建 job 显式 `pytest --run-lockfile-check` 启用
|
||||||
|
- **遗留**:锁文件本身尚未落盘——本机 Miniforge 装的是跨项目开发栈混装环境,污染严重不能直接用 `pip freeze`;须等 CI / 生产机器首次构建 moldinsight 镜像后,按流程跑 `bash deploy/generate_lockfiles.sh` 落锁并提交。已存在护栏:CI 镜像构建 job 跑 `--run-lockfile-check` 后若未落盘会 fail-fast,强制流程走通
|
||||||
|
|
||||||
优先级:**P2**(剩余锁文件部分)
|
优先级:**P2**(流程已固化,剩"首次构建后落盘"一次性产物)
|
||||||
|
|
||||||
### D14. 配置漂移:弱默认 / 死配置 / 重复解析 —— 已清偿(2026-09-16 ~ 09-17,批次 1 / 3)
|
### D14. 配置漂移:弱默认 / 死配置 / 重复解析 —— 已清偿(2026-09-16 ~ 09-17,批次 1 / 3)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# 2026-09 后端设计治理批次实施流水账(归档)
|
||||||
|
|
||||||
|
> 文档定位:**2026-09 设计审查产出的批次 0–4 与后续专项的完整实施流水账**。
|
||||||
|
> 主骨架权威文档见:
|
||||||
|
> - [../STATUS.md](../STATUS.md)(每批次完成的时间点与产物)
|
||||||
|
> - [../TECH_DEBT.md](../TECH_DEBT.md)(活跃债务 / 已清偿项的当前位置)
|
||||||
|
> - [../ROADMAP.md](../ROADMAP.md)(批次计划与执行顺序)
|
||||||
|
> - [../ARCHITECTURE.md](../ARCHITECTURE.md)(平台 / 模块边界当前定稿)
|
||||||
|
|
||||||
|
## 批次 0(2026-09-16,安全与诚实)
|
||||||
|
|
||||||
|
- `/api/status/{task_id}` 补 JWT 鉴权 + 任务归属校验(无 token 401 / 他人或无主任务 403 / 不存在 404)
|
||||||
|
- 归属校验收敛为 `TaskQueryService.ensure_task_access` 供 task_router 与 advanced_router 共用(原 D5)
|
||||||
|
- 上传预检 `pythonocc_available` 从硬编码 true 改为惰性真实探测
|
||||||
|
- bcrypt 口令治理:创建侧超 72 字节显式拒绝、验证侧截断比较
|
||||||
|
- `SECRET_KEY` 未配置 / `RUSTFS_*` 缺失时惰性校验抛明确错误
|
||||||
|
- 完成态任务未持久化 `analysis_metrics` 时 `/api/status` 组装视图 500 修复
|
||||||
|
- 测试基线:96 passed
|
||||||
|
- 回归测试:`tests/test_status_endpoint_auth.py` 8 项
|
||||||
|
- 接口行为变化已同步 [../API_CONTRACT.md](../API_CONTRACT.md) §3.2
|
||||||
|
|
||||||
|
## 批次 1(2026-09-16,部署正确性)
|
||||||
|
|
||||||
|
- 主处理链路改走 RustFS:分派入参 `file_path` → `stp_file_id`(原 D6)
|
||||||
|
- 处理方按 PG 元数据从 RustFS 下载源文件到任务专属临时目录(保留原始文件名,下游产物命名不变),任务结束即清理
|
||||||
|
- RustFS 不可用时回退 `STPFile.file_path` 节点本地路径;compose 为 backend / celery 增加共享卷 `uploads_data` / `html_data` 作过渡兜底
|
||||||
|
- `AUTO_MIGRATE` 开关(settings / .env.example / compose 透传),默认 `true` 保持单机开发行为(原 D12)
|
||||||
|
- 迁移目录 `alembic/` → `migrations/`(修复 `import alembic` 命中本地目录遮蔽真实包的命名冲突)+ Docker 镜像补 `COPY migrations/` + `COPY alembic.ini`
|
||||||
|
- Dockerfile.moldinsight 改为 conda 运行时原生执行,基础镜像 tag 锁定(原 D13 主体)
|
||||||
|
- compose 关键项去弱默认:`SECRET_KEY` / `ADMIN_PASSWORD` 改 `${VAR:?}` 强制显式配置;`create_admin_user` 对空口令显式报错
|
||||||
|
- 测试基线:98 passed, 1 skipped
|
||||||
|
- 回归测试:`tests/test_deployment_config.py`
|
||||||
|
|
||||||
|
## 批次 2(2026-09-16,任务一致性模型)
|
||||||
|
|
||||||
|
- Redis 进程内存回退彻底删除(写 no-op / 读 None,Redis 仅热缓存),PG 为任务状态单一事实源(原 D7)
|
||||||
|
- 批量元数据入库:`processing_tasks` 新增 `batch_id` 列(迁移 `a3f8c2d91e47`),`GET /api/batch/{batch_id}` 改为 PG 聚合查询 + `STPFile.user_id` 归属校验
|
||||||
|
- 删除 Redis batch key 与进程内 dict 双通道
|
||||||
|
- `TaskQueryService` PG 视图与 batch 聚合响应补 `progress` / `current_step`
|
||||||
|
- 型腔分模失败不再吞异常 → 任务 failed(原 D8)
|
||||||
|
- 持久化事务边界收口:数据本体写方法只 flush,编排层分阶段原子收口(阶段 A 几何+网格、阶段 B 型腔+HTML+特征+指标+验证),失败先 rollback 再置 failed(原 D9)
|
||||||
|
- 进度/状态更新保留即时 commit(长任务进度可见性)
|
||||||
|
- 测试基线:105 passed, 1 skipped
|
||||||
|
- 回归测试:`tests/test_batch_status_pg.py` 4 项 + `tests/test_redis_no_fallback.py` 3 项
|
||||||
|
|
||||||
|
## 批次 3(2026-09-17,API 与代码结构)
|
||||||
|
|
||||||
|
- `advanced_router` 按职责拆为 design / cost / machining / export 四个子路由,端点路径不变(原 D1)
|
||||||
|
- 全部请求体改 Pydantic 模型(`request.json()` 手动解析退役),校验失败统一 422
|
||||||
|
- `_get_cached_import` 上提为 `core_modules.py` 共用
|
||||||
|
- 路由装载失败显式化:`ROUTE_MODULES` 清单 + `route_registry`,失败经 `/api/health` 呈现 degraded(`pythonocc` 真实探测),DEBUG 下 fail fast
|
||||||
|
- 纯 Python 重计算端点统一 `asyncio.to_thread` 投放线程池,不再阻塞事件循环
|
||||||
|
- `StorageIntegrationService`(867 行)按职责拆为 TaskStorage / AnalysisStorage / FileHistory 三服务(原 D14 部分)
|
||||||
|
- `MAX_FILE_SIZE` 接线生效、celery_app 复用 `Settings.redis_url`(原 D14 收尾)
|
||||||
|
- 连带修复:管理员重置密码改 JSON body(原裸 str 参数被解析为 query param,前端发 body 必 422);Dockerfile.celery 的 FROM tag 与实际构建 tag 对齐
|
||||||
|
- 接口变更三件套随批完成:openapi.json 重导出(76 paths)+ 前端 `gen:api`
|
||||||
|
- 测试基线:122 passed, 2 skipped
|
||||||
|
- 回归测试:4 个测试文件共 17 项(`test_advanced_split_contract` / `test_route_load_status` / `test_config_governance` / `test_auth_password_reset`)
|
||||||
|
|
||||||
|
## 批次 4(2026-09-17,架构演进)
|
||||||
|
|
||||||
|
- 共享 ORM 按模块拆分(原 D3 主体):891 行 `shared/models/database.py`(31 模型类三类同居)拆为 `shared/models/base.py`(唯一 Base + 归属约定)/ `shared/models/identity.py`(7 表)/ `moldinsight/models/`(9 表)/ `inventory/models/`(catalog/warehouse/trading/finance 15 表)
|
||||||
|
- 三条跨模块 ORM relationship(`User.stp_files` / `STPFile.user` / `STPFile.product`)经全仓核实均无使用方,直接删除;跨模块桥接收敛为裸 FK 硬规则([../ARCHITECTURE.md](../ARCHITECTURE.md) §5.1)
|
||||||
|
- 约 45 处 import 全量改写,无兼容 facade
|
||||||
|
- 全量注册点收敛为 migrations/env.py 与 tests/conftest.py
|
||||||
|
- 零调用方的死方法 `db_manager.create_tables` 一并删除
|
||||||
|
- `_reset_occ_executor` 补 `cancel_futures=True`(旧实现下"慢恢复"的旧线程会继续消化旧队列,与新 executor **并发操作非线程安全的 OCC**,属数据竞争而非单纯泄漏,原 D10 短期治理)
|
||||||
|
- 吞吐路线定稿于 `topics/performance/OCC_THROUGHPUT.md`(短期 A:celery prefork 伸缩 + max-tasks-per-child 兜底;中期 B:run_occ 接口进程化 + kill-on-timeout 根治)
|
||||||
|
- 顺手清偿 D15:`vite.config.ts` 删除未用的 `mode` 参数,`vue-tsc -b` 恢复通过
|
||||||
|
- 回归测试:`tests/test_model_ownership.py`(31 表全量注册 / 单模块独立 mapper 配置 / 旧模块无 facade)
|
||||||
|
- 测试基线:125 passed, 2 skipped
|
||||||
|
- 接口面零变化(无 openapi 重导出)
|
||||||
|
|
||||||
|
## 批次 4 后续专项(2026-09-18)
|
||||||
|
|
||||||
|
- D11 清偿:可视化报告 RustFS 单源化
|
||||||
|
- 写侧 HTMLGenerator 每任务写临时目录,`.html`/`_summary.json`/`_data.json` 三件统一裸传 RustFS 报告键 `html/reports/{filename}`
|
||||||
|
- 读侧 `/html` StaticFiles 本地挂载删除,新增代理路由 `html_report_router.py`(报告键直取 → 遗留 `html/{hash}.json` JSON 包装解析 → 本地卷存量兜底 → 404)
|
||||||
|
- 防路径穿越(单段文件名校验),URL 形状 `/html/{filename}` 不变
|
||||||
|
- 部署:celery 服务摘除 `html_data` 卷;Dockerfile.moldinsight 删除 `COPY html_output/`
|
||||||
|
- 已知约束:报告路由不做认证(iframe 无法携带 Authorization 头)
|
||||||
|
- 删除 `get_stp_file_with_data` 的死数据块
|
||||||
|
- OCC 方案 A 部署参数落地:`CELERY_CONCURRENCY` / `CELERY_MAX_TASKS_PER_CHILD` 进 Dockerfile.celery + compose + .env.example
|
||||||
|
- D2 清偿:铝价响应带 `source: "simulated"`;前端按来源渲染"模拟数据 · 参考走势"标注;死代码 `getAluminumPrice` 删除
|
||||||
|
- D10 方案 B 实施:`run_occ(fn, *args)` → `run_occ(op_name, payload)`;常驻 OCC 进程池 + kill-on-timeout 根治残留线程泄漏
|
||||||
|
- CI 门禁:`.gitea/workflows/ci.yml` 三 job(pytest / 前端构建 / openapi 漂移检测)
|
||||||
|
- 接口变更三件套随批完成:openapi.json 重导出(76→77 paths,新增 `/html/{filename}`)+ 前端 `gen:api` 再生
|
||||||
|
- 测试基线:143 passed, 0 skipped
|
||||||
|
|
||||||
|
## 后续小步治理(2026-09-21 ~ 2026-09-22)
|
||||||
|
|
||||||
|
- 2026-09-21 inventory 业务层继续沉淀批次(4 批):
|
||||||
|
- `customer / supplier / warehouse` → `master_data_service`
|
||||||
|
- `material_routes`(价格历史 / 趋势 / 供应商关联)→ `material_service`
|
||||||
|
- `product_routes`(CRUD / BOM / from-task 跨模块桥接)→ `product_service`
|
||||||
|
- `dashboard_routes`(首页统计 / 低库存预警)→ `dashboard_service`
|
||||||
|
- 2026-09-22 schema / datetime 现代化弃用清零(D12):
|
||||||
|
- 全仓 14 处 `class Config:` + auth_routes 三处全部迁移到 `model_config = ConfigDict(from_attributes=True)`
|
||||||
|
- `auth_service.py` 中 `datetime.utcnow()` 改用 `datetime.now(timezone.utc)`
|
||||||
|
- 测试基线最终落点:126 passed, 4 skipped,零 deprecation warning
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# 2026-09 之前 STATUS 早段历史(归档)
|
||||||
|
|
||||||
|
> 文档定位:**[../STATUS.md](../STATUS.md) 顶部精简后,2026-09-17 之前条目的完整副本**。
|
||||||
|
> 2026-09-17 及之后的批次(批次 0–4 + 后续专项 + 2026-09-21 inventory 服务下沉 + 2026-09-22 schema/datetime 弃用清零)以摘要形式保留在 [../STATUS.md](../STATUS.md) 顶部,详细流水见 [2026-09_governance_batches.md](2026-09_governance_batches.md)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 2026-09-02(**模块化收口 + 文档主骨架建立(基线条目)**:代码侧完成 moldinsight 技术债治理——安全收口(debug/history 权限补齐、任务访问控制收紧)、静默失败修复(`detect-undercuts` 基于真实 shape 重建)、OCC 超时后 executor 重建防毒化全队列、后台任务统一分派、Redis 任务状态改 Hash 原子更新、完成态任务视图缓存、导出缓存与持久化收口、旧入口与死代码删除、Generator 公共接口提取 + 契约测试;详见 [TECH_DEBT.md](../TECH_DEBT.md) §2。结构侧完成 `src/entrypoints/` 三入口拆分(moldinsight / inventory / unified)、`shared` 平台能力集中、前端独立 `frontend/` 工程。文档侧建立 `STATUS / ARCHITECTURE / ROADMAP / TECH_DEBT / DEPLOYMENT` 主骨架,README 收敛为唯一导航入口,历史材料迁入 [archive/](README.md)。**测试基线**:本地 pip 环境 **47 passed, 1 skipped**(pythonocc 缺失自动 skip);moldinsight conda + OCC 环境 **88 passed**。inventory 侧少量既有 deprecation warnings 不影响通过。)
|
||||||
|
|
||||||
|
> 此前:2026-09-01(**文档体系专项整理启动**:明确「README 只做导航、每类信息单一归属、历史材料进 archive」的文档治理原则;建立 deployment/ 主题目录与 archive/ 归档目录;部署文档收口为 DEPLOYMENT(入口)+ deployment/LINUX_SETUP(操作)+ deployment/DEPLOY_PORT / PORT_CONFIG(端口补充)三层。)
|
||||||
@@ -25,6 +25,8 @@
|
|||||||
- [MOLD_ERP_ANALYSIS_REPORT.md](MOLD_ERP_ANALYSIS_REPORT.md)
|
- [MOLD_ERP_ANALYSIS_REPORT.md](MOLD_ERP_ANALYSIS_REPORT.md)
|
||||||
- [ZERO_FINISHED_INVENTORY_CERTIFICATE.md](ZERO_FINISHED_INVENTORY_CERTIFICATE.md)
|
- [ZERO_FINISHED_INVENTORY_CERTIFICATE.md](ZERO_FINISHED_INVENTORY_CERTIFICATE.md)
|
||||||
- [CONFLUENCE_ARCHIVE_STRUCTURE.md](CONFLUENCE_ARCHIVE_STRUCTURE.md)
|
- [CONFLUENCE_ARCHIVE_STRUCTURE.md](CONFLUENCE_ARCHIVE_STRUCTURE.md)
|
||||||
|
- [2026-09_governance_batches.md](2026-09_governance_batches.md):2026-09 设计审查批次 0–4 + 后续专项的完整实施流水账([../TECH_DEBT.md](../TECH_DEBT.md) 与 [../STATUS.md](../STATUS.md) 仅保留摘要)
|
||||||
|
- [2026-09_status_history.md](2026-09_status_history.md):[../STATUS.md](../STATUS.md) 顶部精简后,2026-09-17 之前条目的完整副本
|
||||||
- [topics/ai/](topics/ai/):已迁移的 AI 相关专题历史材料
|
- [topics/ai/](topics/ai/):已迁移的 AI 相关专题历史材料
|
||||||
- [topics/performance/](topics/performance/):已迁移的性能专题历史材料
|
- [topics/performance/](topics/performance/):已迁移的性能专题历史材料
|
||||||
- [topics/aluminum-foam/](topics/aluminum-foam/):已迁移的铝泡沫专题历史材料
|
- [topics/aluminum-foam/](topics/aluminum-foam/):已迁移的铝泡沫专题历史材料
|
||||||
|
|||||||
@@ -164,6 +164,28 @@ async def seeded_db(async_engine):
|
|||||||
yield session
|
yield session
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
"""D13 部署侧契约:仅在显式 --run-lockfile-check 时启用锁文件存在性断言。"""
|
||||||
|
parser.addoption(
|
||||||
|
"--run-lockfile-check",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="启用 D13 锁文件部署侧契约测试(CI 镜像构建 job 使用)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(config, items):
|
||||||
|
"""默认跳过 D13 部署侧契约(仓库侧单测不应被尚未落地的锁文件阻断)。"""
|
||||||
|
if config.getoption("--run-lockfile-check", default=False):
|
||||||
|
return
|
||||||
|
skip_marker = pytest.mark.skip(
|
||||||
|
reason="D13 部署侧契约:默认 skip;CI 镜像构建 job 需传入 --run-lockfile-check 启用"
|
||||||
|
)
|
||||||
|
for item in items:
|
||||||
|
if "test_lockfile_generation" in item.nodeid:
|
||||||
|
item.add_marker(skip_marker)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
async def client(async_engine, seeded_db):
|
async def client(async_engine, seeded_db):
|
||||||
session_factory = async_sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)
|
session_factory = async_sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
"""D13:锁文件生成契约(部署侧硬性要求)
|
||||||
|
|
||||||
|
锁文件 deploy/requirements-{base,moldinsight}.lock.txt 的存在性 + 体积下限
|
||||||
|
是部署侧硬性要求:
|
||||||
|
|
||||||
|
- 锁文件必须在 moldinsight conda 环境构建成功后落盘(见 deploy/generate_lockfiles.sh/.bat)
|
||||||
|
- 锁文件必须以 git 跟踪方式提交,CI / 离线构建 / 生产复现部署才能直接 `pip install -r`
|
||||||
|
- 若 lock.txt 缺失或异常空(仅镜像元数据 < 5 行),说明构建流程未走 D13 流程
|
||||||
|
|
||||||
|
CI 门禁建议:
|
||||||
|
- 仓库侧默认 pytest(`pytest tests/ -q`)**不**强制这些断言——锁文件属"部署侧产物",
|
||||||
|
首次构建未完成时不应阻塞日常单测
|
||||||
|
- 部署侧 / CI 镜像构建 job 用 `--run-lockfile-check` 显式开启本套件(见 conftest.py)
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
DEPLOY_DIR = REPO_ROOT / "deploy"
|
||||||
|
|
||||||
|
LOCK_FILES = [
|
||||||
|
DEPLOY_DIR / "requirements-base.lock.txt",
|
||||||
|
DEPLOY_DIR / "requirements-moldinsight.lock.txt",
|
||||||
|
]
|
||||||
|
|
||||||
|
GENERATOR_SCRIPTS = [
|
||||||
|
DEPLOY_DIR / "generate_lockfiles.sh",
|
||||||
|
DEPLOY_DIR / "generate_lockfiles.bat",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(config, items):
|
||||||
|
"""仅在显式传入 --run-lockfile-check 时启用 D13 部署侧契约。"""
|
||||||
|
if not config.getoption("--run-lockfile-check", default=False):
|
||||||
|
skip_marker = pytest.mark.skip(
|
||||||
|
reason="D13 部署侧契约:默认 skip;CI 镜像构建 job 需传入 --run-lockfile-check 启用"
|
||||||
|
)
|
||||||
|
for item in items:
|
||||||
|
if "test_lockfile_generation" in item.nodeid:
|
||||||
|
item.add_marker(skip_marker)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption(
|
||||||
|
"--run-lockfile-check",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="启用 D13 锁文件部署侧契约测试(CI 镜像构建 job 使用)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(config, items):
|
||||||
|
"""仅在显式传入 --run-lockfile-check 时启用 D13 部署侧契约。
|
||||||
|
|
||||||
|
说明:本钩子保留作为冗余保护(conftest.py 已注册同名钩子),
|
||||||
|
即便测试单独跑 pytest tests/test_lockfile_generation.py 也能正确跳过。
|
||||||
|
"""
|
||||||
|
if not config.getoption("--run-lockfile-check", default=False):
|
||||||
|
skip_marker = pytest.mark.skip(
|
||||||
|
reason="D13 部署侧契约:默认 skip;CI 镜像构建 job 需传入 --run-lockfile-check 启用"
|
||||||
|
)
|
||||||
|
for item in items:
|
||||||
|
item.add_marker(skip_marker)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption(
|
||||||
|
"--run-lockfile-check",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="启用 D13 锁文件部署侧契约测试(CI 镜像构建 job 使用)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("lock_path", LOCK_FILES)
|
||||||
|
def test_lockfile_exists_and_is_substantive(lock_path):
|
||||||
|
"""锁文件必须存在且非空(≥5 行 pip freeze 产物),否则部署侧契约缺失。"""
|
||||||
|
assert lock_path.exists(), (
|
||||||
|
f"缺少锁文件 {lock_path.relative_to(REPO_ROOT)};"
|
||||||
|
f"请在 moldinsight conda 环境执行 deploy/generate_lockfiles.sh/.bat 后提交"
|
||||||
|
)
|
||||||
|
line_count = sum(1 for _ in lock_path.open(encoding="utf-8") if _.strip())
|
||||||
|
assert line_count >= 5, (
|
||||||
|
f"锁文件 {lock_path.relative_to(REPO_ROOT)} 体积异常(仅 {line_count} 行非空行),"
|
||||||
|
"可能是构建流程未走通,请重新生成"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("script_path", GENERATOR_SCRIPTS)
|
||||||
|
def test_lockfile_generator_script_present(script_path):
|
||||||
|
"""锁文件生成脚本必须随仓库分发,否则新机器无法落锁。"""
|
||||||
|
assert script_path.exists(), (
|
||||||
|
f"缺少生成脚本 {script_path.relative_to(REPO_ROOT)};"
|
||||||
|
"D13 流程入口文件缺失"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_lockfile_dockerfile_comment_points_to_generator():
|
||||||
|
"""Dockerfile.moldinsight 必须明确指向锁文件生成脚本。"""
|
||||||
|
dockerfile = (DEPLOY_DIR / "Dockerfile.moldinsight").read_text(encoding="utf-8")
|
||||||
|
assert "generate_lockfiles" in dockerfile, (
|
||||||
|
"Dockerfile.moldinsight 应在注释中指向 deploy/generate_lockfiles.sh 以引导锁文件生成流程"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user